Over the last few months, I have been working on a side project that I intend to launch this coming fall. I’ve been working on a mobile app that will provide users with a new form of social networking. The choice I had to make was whether or not I should use a cross-platform framework. 3 choices stood out to me, React Native, Flutter, and Nativescript. All of these choices have their pros and cons, but what I eventually chose was Flutter.
How Flutter has Benefitted Me
As a one-man team for this project, I get to focus on what matters: the code. By taking away questions about how to style things, and setting up native
projects with sensible defaults, I don’t need to get bogged down in figuring out how best to set up a building Android/iOS project. When you first
initialize a Flutter project, the flutter CLI also sets up two nested projects under ios/
and android/
folders in the directory. Both of these folders
are working, native codebases. This is great, because figuring out project setup can take a lot of time for a new mobile developer.
Also, with both native codebases in the same directory, when I did need to write a few bits of native code for SDK integrations I was able to with ease. All of the code is in the same place.
In addition, Flutter provides first class facilities for interfacing with native code through platform channels. Platform channels work by implementing a message passing mechanism between the native side and the actual Dart Virtual Machine running the Flutter code. To leverage this feature, say you are writing a platform channel to listen for location updates (something I’m doing plenty of in my new app!). You’d write the native code that queries the user location and checks permissions in Kotlin, then you would import the Flutter platform channels package in Kotlin. You’d then write an EventChannel
implementation that props up a new channel instance, and pushes user location events into an EventSink
object. On the Flutter side you would then write code that listens on the channel name (denoted by a string key) that you set up in Kotlin, and reacts to events as they come in. This approach allows for asynchronous, non-blocking communications with the native platform and is an excellent way to implement complex features.
Flutter’s model for actually developing user interfaces is very familiar as well. Everything is a Widget. In React, everything is a component. Skills and mental models that you build for React transfer readily to Flutter. It is easy to decompose whatever design you have in your head into widgets that you can then set out to build. As an example, I got a design made for the profile screen of our application. Looking at the different elements in the design, I was able to visualize the components as a hierarchy of widgets, and I matched the design almost pixel-for-pixel. I did this two weeks after beginning my Flutter journey. How many frameworks have that little cognitive overhead? Not a lot.
The core of Flutter’s “everything is a widget” model centers around two core classes: StatelessWidget
and StatefulWidget
. I found these two classes
incredibly easy to understand. StatelessWidget
is a declaration that this widget won’t change when it is built. It is a leaf in the widget tree. A StatefulWidget
has an associated State<StatefulWidget>
object that is at the same level in the widget tree, and the State
object is mutable. The
State
object will handle user interactions and let the StatefulWidget
object know when to redraw based on changes to the state. Redraws are triggered
by calls to setState
. That should be familiar to those of you React developers out there that know your lifecycle methods. With just these two classes
alone you can build incredibly complex applications. There are other patterns, like using Providers
that I will cover in later posts, but basic Flutter
alone has benefitted me greatly.
Flutter has first class native support through platform channels, and it is easy to build native code that hooks into flutter with them. Flutter also
uses the same mental models that other popular frameworks (React) follow so cognitively it is easy to pick up. Flutter’s two core widget classes,
StatefulWidget
and StatelessWidget
are easy to understand and all widgets you build should subclass these two. Flutter is a great choice for building
mobile applications and I have seen many benefits choosing it for the side project I am currently writing.
Now, another reason Flutter is so great (and easy to understand) is the language it is built in: Dart. Dart is the most familiar language to anyone that has worked in a C-style language in the last 30 years.
Dart, My Favorite Parts
Syntactically, I have heard Dart described as Java-lite, and I would call that accurate. Functions are similar, with really only one thing missing: access modifiers. Classes follow similar structure to any OO-based language, and have constructors and methods. Dart does have added (amazing) features for asynchronous programming, and in my opionion they are very easy to use. They are similar to async structures in Javascript. People on the Dart team obviously wanted to build a language that would be familiar to everyone, and they did. Some of my favorite Dart features that I have discovered so far have made programming my app in Flutter a breeze.
Shorthand Constructors
In any OO-based language you are going to need to write constructors. In particular, a UI framework where everything is an object is going to require at least n constructors for the n widgets you build. Dart has an awesome syntax for constructors that allows you to perform the most basic task of setting instance variables in just one line!
class MyClass {
int myVar;
MyClass({this.myVar}); // Wow!! Are you freakin' kidding me?
}
See if you can tell which line is the constructor, it’s pretty easy. And using the curly braces exposes myVar as a keyword argument! so when you actually
instantiate this class, you are able to call the constructor like this MyClass(myVar: 3)
. I’ve built some pretty complex widgets so far, but my
constructor needs have remained the same: assign instance variables. This shorthand is an incredibly convenient way to do that without taking up too much
space with boilerplate code. Less cognitive overhead.
Asynchronous programming, Async/Await.
Dart was designed with async as a first-class citizen. Many languages have had async features be added as afterthoughts as the world opts for a less
blocking, more non-blocking style of development. Darts features are very easy to understand and they are based on two concepts. That of the Future
and
async/await
inside of async functions.
Future<int> doTheThing() async {
var expensiveCalculation = await doAnotherThing();
return expensiveCalculation;
}
This function shows all of those concepts at once. Because the function is marked as async
, we know that this function will return a Future
, thus the
return type of Future<int>
. The Future<int>
signifies that we will return a Future
object that eventually will contain an int. Think of it as a
promise that an int is coming, but we are still working on it, like an IOU. Inside of the function we use the await
keyword. This means that
doAnotherThing()
is also an asynchronus function. Typically when writing async code, you might want to call other async code to do some work and react
to the return values of that code accordingly. Using await
tells Dart to wait for the asynchronous code to finish, instead of just returning a Future
and happily chugging along. If we didn’t have the await
the code would look like this
doAnotherThing().then((value) {
// Do something with that value
})
Is this so bad? No, but eventually when you’ve strung a lot of these .then
calls together, you get to the point developers refer to as callback hell.
await
allows us to write asynchronous code that reads like synchronous code, and this is important for readability’s sake.
Dart is a great language, and it is easy to pick up for anyone that has worked in a modern C-style language. Shorthand constructors are wonderful, as are the syntactic structures for asynchrous programming. I can’t wait to discover more hidden gems working with the language!
Thanks for taking the time to read my post, I hope you’ve enjoyed it! I want to get back into writing these as much as possible so if you could leave me feedback here or wherever I posted this article please do so! I love writing, it helps me learn and it fulfills me to try to spread the knowledge I’ve gained so if I could get better at it in the process that would be excellent.