The Flutter App Architecture

Pratik Jain
Appiko
Published in
6 min readMay 6, 2020

List of things which helps Appiko maintain and build smooth flutter apps.

Flutter is Google’s UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase.

Flutter is mostly a UI toolkit, offering basic state management. State management on large applications become difficult, and hence there are state management solutions for flutter. The one we are going to look at in this article is the provider, which is recommended by the flutter community for medium to fairly large applications.

Managing states is just not enough, we need to figure on how the UI will talk to other services, plugins, native code and APIs. Also we need to figure, the process of the UI listening to the changes in state, and updating itself depending on the state.

Following is the approach we take at Appiko, in order to build our mobile applications which help our users configure our hardware devices.

‎We will also take a look the template app, which is the improved version of the counter app by flutter, allows incrementing and decrementing the counter variable, it makes use of custom widgets, maintains the counter state, handles counter logic outside the UI, handles navigation in business logic when the counter reaches a particular value.

We shall start small and at look at the building blocks for building a flutter application, the widgets, then move on to fairly complex things like managing the states by using with the help of services and plugins like get_it and provider, and finally end with a note on navigation.

Widgets

Flutter Widgets

Everything in flutter is a widget.

Flutter comes with a bunch of UI components know as widgets. These components are mostly designed keeping in mind the Google’s material design and Apple’s Cupertino design.

These widgets can be used as is and act as the building blocks of the application.

Text widget is an example.

Text("Hello, world");

Custom Widgets

Custom widgets in this case, refers to a modified flutter widget, a widget build on top an already existing flutter widget to match the actual UI design, or a set of widgets combined together so that they can be reused consistently on different screens.

In our template app, we use a custom container to show the counterValue.

class CustomCounterContainer extends StatelessWidget {

@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Container(
height: size.width * 0.6,
width: size.width * 0.6,
decoration: BoxDecoration(...),
child: Center(...),
),
);
}
}

This widget is modifying the Container to match the UI

https://github.com/Appiko/flutter-architecture-demo/blob/master/lib/widgets/customer_counter_container.dart

Views

A view is a jargon for the a screen/page on the UI.

A view is made up of one or more widgets and mostly handles the layout and flow the widgets by using some of flutter layout management widgets like column, row, list, etc.

We have a DecrementView in our template app, which shows the customCounterWidget with some text and a FloatingActionButton to decrement the counterValue .

class DecrementView extends StatelessWidget {

@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
CustomCounterContainer(...),
SizedBox(height: 32),
Text(...),
floatingActionButton: FloatingActionButton.extended(...),
]);
}
}

Here we have a Scaffold whose body is a Column and that has a list of children which will be placed one below the other in that order.

https://github.com/Appiko/flutter-architecture-demo/blob/master/lib/views/decrement_view.dart

Services

While managing states completely on the a single page and replicating the same states on other screens, is just not a good idea, we have services to the rescue.

Services are a set of dart classes, which are intended to help with managing states, like a timer service would hold a timer and update the state whenever the timer value is changed.

The services also talk to other services, other flutter plugins, native code, rest APIs, and the UI.

Provider

Provider is a state management solution by the people in the flutter community, they listen to changes in the state and rebuilds only the widget whose state was changed.

In this case, each service extends the ChangeNotifier class and calls the notifyListeners() whenever there is a change in the state of that service.

We mostly use ChangeNotifierProvider in our applications but there are other kinds of providers, and the documentation explains them all.

Locators (get_it)

The get_it package helps us get quick singleton or factory instances of dart classes. We mostly use the locators to get an instance of the class and call a method on it, which may change the state or perform a task.

The CounterService in our template app, has a increment and a decrement method to change the value of the counter

class CounterService with ChangeNotifier {
Counter _counter = Counter(0);

void incrementCounter() {
_counter.value++;
// NotifyListeners will tell Provider that,
// there is a change instate.
notifyListeners();
}

void decrementCounter() {...}
}

https://github.com/Appiko/flutter-architecture-demo/blob/master/lib/services/counter_service.dart

Using Provider we update the UI in our HomeView and using GetIt we call the increment method on the CounterService

class HomeView extends StatelessWidget {
@override
Widget build(BuildContext context) {

final int _counterValue = Provider.of<CounterService>(context).counterValue;

return Scaffold(
body:Column(
children: <Widget>[
CustomCounterContainer(
counterValue: _counterValue,
),
...
],
),
floatingActionButton: FloatingActionButton.extended(
onPressed: locator<CounterService>().incrementCounter, label: Text("Increment"),
icon: Icon(Icons.add),
...
),
);
}
}

https://github.com/Appiko/flutter-architecture-demo/blob/master/lib/views/home_view.dart

Extras

While the the things listed above will help us finalizing on an architecture. Here are a few more things we use to make it better,

Models

Models are dart classes, which define a schema of the data. Models can be a part of the applications state or can be passed around while navigating between the screens. They may also contains methods which are specific to that model.

A basic model would be the Counter model from our template app.

class Counter {
/// Counter value
int value;
Counter(this.value);
}

https://github.com/Appiko/flutter-architecture-demo/blob/master/lib/models/counter.dart

Helper functions

Helper functions are utility functions, which are not really tied to any model or a service. At Appiko since we mostly work with data on bit level, we use utility functions to extract the a meaningful value of the byte, but its not limited to that.

In the template app we use a helper function to generate routes

Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case rootViewRoute:
return MaterialPageRoute(
settings: settings,
builder: (context) => MyHomePage(),
);
break;
...
}
....
}

Navigation

Navigation in flutter is mostly using the methods on the Navigator class which have the contextas their required argument. Navigating from a widget mostly works as the contexts from the widgets can be passed to navigator class.

Handling navigation on the business logic gives us special powers. And passing the context to the business logic does not make sense.

Let’s consider a logical scenario, at Appiko we want to show a different page if the device is disconnected (we listen to changes connection state). While handling that is possible on the view, it is much more simpler if the navigation can be handled from the Bluetooth connection service.

Dane Mackier, has an article on how to get that working.

In the template app, we just navigate when the counterValue is changed to something.

Feel free to use our template app, to get started with this architecture.
https://github.com/Appiko/flutter-architecture-demo.

In the process of writing this, I also watched a couple of talks from Flutter Europe. And I see that there are multiple, better ways to do the same thing. Checkout Flutter Europe on YouTube

References

--

--