Flutter - why you may love it or hate it
Flutter is a new Google's framework for creating cross-platform applications. So far, I haven't tried to create any cross-platform projects so I wanted to give it a try. This won't be a next basic tutorial for creating apps with Flutter. I'll just present my first impression of things that have made me love and hate Flutter after a few hours of having fun with it.
Dart
Before I started implementing my first application, I had thought it would be wise to prepare myself for new language syntax. Flutter is just a framework. Language which it uses is called Dart. Dart is an object-oriented, class defined, garbage-collected language created by Google. It is pretty similar to Kotlin, C# or Swift. Everything you need to know about it you can find on Flutter's Get started page.
Flutter
One thing that is worth knowing is that Flutter’s application are not compiled like React Native or NativeScript code. It's not only UI elements, which are compiled, but the whole thing is. Flutter doesn't use a special bridge to communicate between our code and native code, it uses a C/C++ library for compilation, so it's much closer to a machine language and it gives us a much better performance.
First application
I've decided to continue my journey with Flutter's "Get started" documentation. The first application I've created was developed step by step from this tutorial. You can see the complete code of an application below:
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Startup Name Generator',
home: RandomWords(),
);
}
}
class RandomWords extends StatefulWidget {
RandomWordsState createState() => new RandomWordsState();
}
class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Startup Name Generator'),
),
body: _buildSuggestions(),
);
}
Widget _buildSuggestions() {
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (context, i) {
if (i.isOdd) return Divider();
final index = i ~/ 2;
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
});
}
Widget _buildRow(WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
);
}
A first few things you can notice is that:
- It's only one file - we don't create UI's in a separate file like I'm used to in Android development,
- It's short - about 50 lines for application which creates a never-ending list? It's pretty awesome!
- Everything is Widget - the whole Flutter application is created using Widgets. Every UI element (and even the main screen block) is a Widget. Creating Flutter app is just creating a tree of Widgets.
Let's just quickly explain what is going on step by step:
- we start an application in the
main()
function callingrunApp()
, - in
MyApp
class, we returnMaterialApp
with a title and the rest of the layout -RandomWords
class (which of course is a Widget), - in
RandomWordsState
we createState
which is a logic forRandomWords
class of what we would like to show inside, - overriden
build
method creates the Widget - Scaffold, which in itsbody
calls the functionbuildSuggestions()
, that will return us aListView
Widget, buildSuggestions()
for odd indexes creates a Divider (simple horizontal line) and for the even once, inflates aListTile
Widget usingbuildRow()
function,- finally the
buildRow()
creates a simple Text widget using a string generated by theenglish_words
library which is being held in asuggestions
array.
Combining layout and logic code in a single place seemed strange to me at first, but I got used to it after some time.
Hot Reload
Flutter supports a great development feature. Hot Reload allows you to rerun your application after some simple changes in
no time. That is great! I won't have to wait a minute every time I make a simple change, shall I? Well.. not necessarily, because
it doesn't work every time. Let's say that we want to change Text's color in our application.
We adjust _biggerFont
implementation to :
final _biggerFont = const TextStyle(fontSize: 18.0, color: Colors.green);
then save (saving the file applies Hot Reload as well) and... nothing happens. Texts inside the list doesn't change their color. We have to rerun the application completely to see the changes. Now, if we try to change the text color to blue after pressing save, Hot Reload does work but as you see, in most cases it is better to just rerun application completely to be sure something does or doesn't work. Nevertheless, rerunning Flutter application seems to be a lot faster than rerunning traditional Android code.
Material Design
As you could have already noticed, everything in the Flutter app depends on Material Design. Even the main application container
is called MaterialApp
. Personally, I think that is great, because I love all the UI designs there, but for iOS user it might
be strange. Every Flutter app looks exactly the same on every platform. You can obviously change some layouts, depending
on the device it's running but it doesn't automatically adjust the style of the components to match a specific platform.
Things I love in Flutter
- a lot of predefined Widgets - if you want to add Floating Button, Drawer Navigator or even some action buttons to the toolbar, it will take you only a few lines of the code to implement. All the basic features (like Toolbars - back button or Drawer's - open) are already implemented,
- easy implementation of animations,
- still developed - Google is adding new features all the time, new third-party libraries are being created and you can find a lot of help on StackOverflow already,
- better performance than all the JavaScript's methods,
- Hot Reload - it actually helps :D.
Things I hate in Flutter
- .yaml files - I've spent some time trying to fix a simple bug, which was caused by one additional white-space.
When you add image, you need to specify it in
pubspec.yaml
file and I've made a typo there. Fortunately, online validators came to the rescue, - ending each Widget's argument's with comma - after adding the last argument of a Widget, you should end it with
,
. Otherwise the code will be badly / ugly formatted. This sometimes leads to the code which in the end looks like this:
],
),
),
),
);
- ending lines with a semicolon - after getting used to Kotlin, it's really annoying that I have to add the semicolons again (but that's more of a Dart thing I guess).
Should I use it?
Everything depends on a scope of a project. If you want to develop multi platform application by yourself it's easier to use a cross-platform method, rather than writing native code for each one. Choosing between Flutter and React Native / NativeScript depends on you. Thanks to the constant development, it seems to me that it can outrun his competitors, but it hasn't done it yet. You also need to bare in mind that cross-platform methods always lack some features. It is highly probable that for the very specific task, you would have to add some native code. Fortunately, it's also not so hard to do with Flutter.