Remember Flutter’s declarative language characteristics
HTML is a declarative programming language. It’s a markup language that represents in text script and in symbols a webpage at a specific point in time. The word used here is important. HTML ‘represents’ and does not ‘present’ the webpage. It’s a browser that does all the actual work — it reads the HTML, performs the rendering, and then presents a webpage with all its text, its lines, its buttons, its forms, etc. When it all comes down to it, HTML is just the instructions.
When You look at raw HTML source code, you are looking at a ‘static representation’ or a snapshot of what the webpage is to look like (as well as potentially what it is capable of doing) at that given moment. That text at that very instant is immutable. Unchanging. It’s only with a new HTML request, will a webpage ‘change’ its content. Note, in many cases, the change is not by modifying the existing HTML here and there, but by rebuilding the whole page all over again from scratch. Though not readily apparent, it’s an efficient approach. The HTML itself takes up little memory. It's just instructions after all.
You see, Flutter is a pseudo-declarative UI framework. It has its imperative, reactive, and functional programming characteristics, but it’s its declarative language characteristic that uses this ‘rebuild from scratch’ approach. With every call to a build() function, for example, from a State object or a Stateless widget, the returning widget is more often than not re-created all over again. The Dart code in that build() function is merely instructions. It’s the Flutter engine that does all the rendering. The whole process is actually light on memory resources and quite fast. Now, to make it even lighter on memory and even faster: Think HTML. Think declarative. Think immutable.
I Like Screenshots. Click For Gists.
As always, I prefer using screenshots in my articles over gists to show concepts rather than just show code. I find them easier to work with frankly. However, you can click or tap on these screenshots to see the code in a gist or in Github. Further, it’s better to read this article about mobile development on your computer than on your phone. Besides, we program mostly on our computers — not on our phones. Not yet anyway.
Keep It Immutable
When you first started learning Flutter, I have no doubt you encountered an error essentially saying: “This class is marked as ‘@immutable’, but one or more of its instance fields aren’t final.” Below is a snapshot of the proverbial ‘starter app’ (or the counter app) that comes with every new Flutter project. The keyword, final, was removed from the instance field, title, in the StatefulWidget, MyHomePage, and so that warning message appears.
Looking at that example, remember that a StatefulWidget is created, destroyed, and recreated again and again during the lifetime of a typical Flutter app. Like inside build() functions, this is another place where the declarative approach is implemented. Do your Flutter app a favor, and don’t ignore such warnings. Such ‘mutable’ instance fields are taking up memory and using more CPU cycles when executed. In fact, let's take a look at that proverbial counter app again and see if we can’t shave some milliseconds off its performance. Below is a snapshot of the counter app. Something we all should be familiar with.
Now, look at the version of the counter app below. When you run it, it’ll look just the same (Well, it’ll be green instead of blue). However, this one runs faster and likely has a smaller memory footprint in comparison. The little red arrows point to why. You’ll see a lot more
If your app produces objects that never change, you should make these objects compile-time constants. The Flutter engine will love you for it! You do so using the keyword,
const for any instance fields that you want to be compile-time constants as well — so to be unchanging for the lifetime of the app. Such variables then contain a value before you even run the app! That’s a lot of help to the Futter engine — less work to do.
Even a whole class can be constant! Unchanging. You do this by giving the class a ‘constant constructor.’ You do that by putting the
const keyword before the constructor name. This means, however, all instance fields if any in this class are declared final. If you can manage that for your classes, your app’s that much faster.
You can see in the screenshot below how we’re telling the compiler what’s immutable in the code and so will optimize accordingly. You can see, for example, the instance field, title, is declared final and so its class, MyHomePage, could and should have a constant constructor.
Many instance fields are initialized directly from a constructor parameter. This process is described as “initializing formal” and involves using the syntax, this., before the constructor parameter. As such, a named parameter is thus an instance field, and it’s being initialized right there in the parameter list. Such instance fields should be declared final if no other value will ever be assigned to those fields during that class’s lifecycle. Again, as you see below, the instance field, title, is one such example.
Instance variables/fields declared final must be initialized before a class’ constructor body starts. As stated above, one way such instance fields are initialized is as parameters in the class’ constructor. However, instance fields can also be declared final when they’re initialized in the constructor’s initializer list. You can see this in the revised counter app version. See below.
Put Some Lint In Your Code
Actually don’t put Lint in your code. ‘Lint’ in this context describes the programmatic and stylistic errors found in code. A more appropriate sentiment would be, ‘Put a Linter to your code.’ A Linter is the static code analysis tool used to flag such issues.
In time, as you work with Flutter, you’ll easily spot places in the code where a const or a final would be appropriate. Until then, why not let your Linter pick them out for you? For example, in most IDE’s, if you forget a semicolon to terminate a line, you’ll likely see a little red line appear on the screen indicating the issue. That’s the Dart analyzer performing its job on your code. It’s to indicate such errors as well as indicate warnings about code that runs afoul of the Dart language's own specifications.
However, you can further configure the Linter (the analyzer’s specific plugin that does the linting) to watch out for instances where the keywords, final and const, would be appropriate in your code.
ITomek Polański’s gist entry called, Strict Flutter Lint rules, catches such instances and more. It’s a really good approach to force you to make your code efficient and effective as well as comply with recognized standards. To utilize these rules, you simply copy them all into the analysis options file,
analysis_options.yamlwhich you create in the root of your project where your
pubspec.yaml file lives. Little grey lines will then appear among your code indicating warnings — heed those warnings, and you’ll be better for it.
Keep In Effective Style
By the way, to at least ensure that your code complies with the Dart Style Guide or follows the suggested guidelines found in Effective Dart, there are some additional Linter rules available to you. They may not be as strict as those above, but they do have many of the same rules and even intersect each other. These two separate guidelines are represented by two separate sets of Linter rules. First, you must declare either of them a dev dependency in your
Next, type in the command,
pub get, to get that dependency into your project. Finally, include their set of rules with the following command line entered at the beginning of your analysis options file,
include: package:pedantic/analysis_options.yamlOR include: package:effective_dart/analysis_options.yaml
Keep The Mutable To A Minimum
So, keeping the mutable element of your app to a minimum will indeed help with your app’s overall performance. So, with regards to this counter app, where’s the counter? The one thing we know outright is mutable in this app.
Well, keeping the mutable portion of your app in its place also has its benefits. Note, in the revised version, the State object, _MyHomePageState, has one lone instance field called, bloc, and it’s also declared as final. Now, what’s this bloc thingy?
It’s an instance field that references an altogether separate class! I called the class, Bloc, to hint it contains the ‘business logic’ involved — what little business logic there is for such a simple app. Granted, creating yet another class in this version puts a penalty on performance, but you’ll still find it’s faster.
As a plus, I’ve introduced to you an approach to organize your code. It’s an approach you’ve, in fact, no doubt adopted in some form in your apps anyway. I mean, you could just have all your app’s business logic and event handling in your State object, but it’ll make for a pretty big and messy State object. No? Your State objects still have access to this stuff, but it’s manipulated in separate classes. It’s an option.
There’s A Pattern In The Design
Looking at that separate class, you may recognize this approach as a common trait found in many of the design patterns currently offered to Flutter developers these days. It’s an approach that happens to also assist us in our efforts to make as much of the app a ‘static representation’ of what this app should look like (and what it can potentially do) at a given point in time. Granted, the little red arrows highlight code in the build() function that is far from immutable — they’re computed at runtime. However, those mutable components are now mostly found in one place. In a class called, Bloc. See a bit of a pattern there? It encourages low coupling and easier maintainability.
Anyway, let’s leave it there. If anything, following the Lint rules described above, will only make you a better Flutter developer. That’s one takeaway, right?