Intro to Flutter for Android Developers

by Adel Boussaken, senior software engineer at TrueFace.ai

I’m Adel Boussaken, a senior software engineer at TrueFace.ai. Today I’m going to give you a brief introduction to Flutter development. Thank you for coming to the dart side.

I will skip all parts about installing, running Flutter app and get directly into gust of it, if you are not sure about what’s Flutter and have many questions, please read What’s Revolutionary about Flutter.

Dart

If you think Kotlin is a modern alternative over Java, wait till you see Dart, it has no nonsense, has no boilerplate:

Here an example of Dart code, a private method for getting IP Address:

_getIPAddress() async {
 String url = 'https://httpbin.org/ip';
 var httpClient = createHttpClient();
 var response = await httpClient.read(url);
 Map data = JSON.decode(response);
 String ip = data['origin'];
}

Design Pattern

I am pretty sure you have evolved your Android skill set from spaghetti code to MVC, MVP, MVVM, Dagger2, DataBinding and Android Architecture Components, Flutter is mostly just V, reactive view no less, it can be either stateless or stateful widget, even AppCompatActivity is a widget.

Let create a very simple activity, We need a text view and a button that update text, here the activity (mind missing XML):

public class Click extends Activity {
int count = 0;
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.main);
final Button button = (Button) findViewById(R.id.button);
final TextView text = (TextView) findViewById(R.id.counter);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
count++;
text.setText("Button clicked " + counter + " times");
}
});
}
}

In Flutter, you do not manipulate views, like in setText(), you manipulate state instead and Flutter will take care of updating screen, the equivalent of above code in Flutter would be:

import 'package:flutter/material.dart';

class Activity extends StatefulWidget {
@override
ActivityState createState() => new ActivityState();
}

class ActivityState extends State<Activity> {
int counter = 0;
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'Button clicked ${counter} times',
),
new FlatButton(
child: new Text('Increment'),
onPressed: () {
setState(() {
counter++;
});
},
),
],
),
),
);
}
}

As you noticed, we have increased counter inside setState, once onPressed is trigged (then setState), Flutter re-render the whole widget and text object with new counter value.

The biggest advantage of separating widget and state (for Android developers), if you rotate device or resize window (Multi-Window Support), in Java code the value of counter will reset to zero, you have to code a lot of boilerplate managing activity lifecycle, in Flutter, that’s free, out of box, no extra code.

Although Dart compile down to native code, it has no access to device hardware, if you want to get user location for example, you have to writing custom platform-specific code with platform channels, while indeed there’s many plugins to cover such cases, you are still expected to launch Android Studio (or Xcode) to write fair among of code.

Async/Await

In Android Development the UI thread is a precious thread, do anything slowish and you will get the infamous mystery line

I/Choreographer(1378): Skipped 55 frames!  The application may be doing too much work on its main thread.

Which you could only fight using threads, like in this example:

public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
// a potentially time consuming task
final Bitmap bitmap =
processBitMap("image.png");
mImageView.post(new Runnable() {
public void run() {
mImageView.setImageBitmap(bitmap);
}
});
}
}).start();
}

The above code can be easily done in Dart:

onClick() async {
Bitmap bitmap = await processBitMap("image.png");
// you don't want to update non-existent widget
if (!mounted) return;
setState(() {
imageView = bitmap;
});
}

What has happened here? the async modifier mark method scheduled for execution later, the method became none blocking so your app won’t miss any frame, the await modifier suspends execution of method, waiting for the processBitMap to finish then the execution resumes.

Widget

There is large variety of OEM software and skins running on top Android, understandable as every manufacturer want to build own added value to their device, I can’t count how many times I said “Fuck S*ms*ng”, OEM customisation has introduced hard issues to Android developers… no more.

Flutter, in order to archive 120 FPS has decided to start with an empty canvas, using Skia a 2D graphics library, paints every widget instead of reusing OEM widgets, implementing most of Material Design widgets and common layouts.

This approach, unsure your application will look the same on many devices, even though not all of Flutter widgets are optimised to work as well on tablets as on smaller devices.

Accidentally, or not, this approach also opens doors for many innovation on UI, for example, the following code allows user to zoom the whole activity:

class HeroScreen extends StatefulWidget {
@override
HeroState createState() => new HeroState();
}

class HeroState extends State<HeroScreen> with SingleTickerProviderStateMixin {

bool _scaling = false;

Matrix4 _transformScale = new Matrix4.identity();
double scalePos = 1.0;
set _scale(double value) =>
_transformScale = new Matrix4.identity()..scale(value, value, 1.0);

@override
Widget build(BuildContext context) {
return new Scaffold(
body: new GestureDetector(
onScaleStart: (details) {
setState(() {
_scaling = true;
});
},
onScaleUpdate: (details) {
var n = details.scale;
setState(() {
scalePos = n;
_scale = n;
});
},
onScaleEnd: (details) {
setState(() {
_scaling = false;
_scale = scalePos < 1 ? 1.0 : scalePos;
});
},
child: new Container(
color: Colors.black26,
child: new Transform(
transform: _transformScale,
child: new Center(
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(_scaling ? "Scaling" : "Scale me up"),
]))))));
}
}

That lead us to a very interesting aspect of widget, it is just too easy to write reusable widgets, I can turn above not-so-good code into reusable and clean widget:

class Scale extends StatefulWidget {
final Widget child;
final GestureScaleStartCallback onStart;
final GestureScaleEndCallback onEnd;
final GestureScaleUpdateCallback onUpdate;

Scale({Key key, this.child, this.onStart, this.onUpdate, this.onEnd})
: super();

@override
ScaleState createState() => new ScaleState();
}

class ScaleState extends State<Scale> with SingleTickerProviderStateMixin {
Matrix4 _transformScale = new Matrix4.identity();
double scalePos = 1.0;
set _scale(double value) =>
_transformScale = new Matrix4.identity()..scale(value, value, 1.0);

@override
Widget build(BuildContext context) {
return new GestureDetector(
onScaleStart: widget.onStart,
onScaleUpdate: (details) {
widget.onUpdate(details);
var n = details.scale;
setState(() {
scalePos = n;
_scale = n;
});
},
onScaleEnd: (details) {
widget.onEnd(details);
setState(() {
_scale = scalePos < 1 ? 1.0 : scalePos;
});
},
child: new Container(
color: Colors.black26,
child: new Transform(
transform: _transformScale, child: widget.child)));
}
}

Then reuse it as follow:

class HeroScreen extends StatefulWidget {
@override
HeroState createState() => new HeroState();
}

class HeroState extends State<HeroScreen> with SingleTickerProviderStateMixin {
bool _scaling = false;

@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Scale(
onStart: (details) => setState(() => _scaling = true),
onUpdate: (details) {},
onEnd: (details) => setState(() => _scaling = false),
child: new Container(
color: Colors.black26,
child: new Center(
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(_scaling ? "Scaling" : "Scale me up"),
]))),
));
}
}

Etc

  • Instant run is called hot reload and it take less than a second
  • Flutter paint using CPU, render using GPU but 3D API is not available
  • There is no XML for layout
  • Layouts take one pass to calculate tree size
  • Even with syntax errors, your app won’t crash
  • Flutter widgets can embed within Android activity
  • You can’t embed Android library within Flutter widget
  • 60 FPS unless you do something really busy
  • API is inspired by React, it doesn’t work like React
  • There is no POJOs

That’s how awesome Flutter is, I hope you have enjoyed my first attempt to write a blog post, Thank you for reading so far.

Show your support

Clapping shows how much you appreciated TrueFace.ai’s story.