Developing with Flutter
Developing with Flutter looks like an interesting alternative to Kotlin, Swift, and React. Last year I worked thru my React Nanodegree so I didn’t feel like changing up directions yet. Google also has a tendency of starting many things, but not sticking with them. Flutter seems to have some legs and has been a big part of Google’s conferences in 2019. So I’m going to explore Flutter development and see how it compares.
Installation and Configuration
I’m using my regular MBP and will be using emulators and my Pixel 2 physical device when needed. Unfortunately there is no Homebrew installation so we’ll have to rely on the Google instructions and then maintaining versions separately. The instructions work pretty well. Once you get the SDK installed, use flutter doctor to find any missing dependencies and get everything fixed up. I had a bunch of things to install and also a bunch of license agreements that I had to agree to.
I am running on Flutter v1.7.8+hotfix.3 at the time of posting this (07/23/2019).
Getting Flutter’s Basics Working with Android
Like most frameworks these days, the command line can be used to generate a starter app. Its best to start there to see if everything is working as expected:
flutter create my_project
Will chug out a project that you can start up with flutter run. I hit some bumps here with the dreaded “HttpException: Connection closed before full header was received” when attempting to load my app to a locally running Android emulator. Awesome…this error means that hot reload will not work as well as any of the other debugging features.
I did quite a bit of Google-ing around. Lots of people are having difficulty with this issue. I ran a bunch of different debugging techniques and tried various scripts suggested by threads. All sorts of crazy theories about the cause – wrong versions of Python, proxy blocking software, etc. My advice is to stay calm, try very small things first before you start massive work to clean up your machine or make big changes.
Finally I thought – hmmm….maybe I should try a physical device. Duh. Yeah that works.
I had installed a Pixel 2 emulator with Android 9.1. After about an hour of fiddling around I decided to install a Pixel 2 with Oreo (Android 8.1) instead. Bingo – this works. Lesson learned – try a physical device first and then go to the emulators. Interesting that my physical device is running Android 9 (5/5/2019) and it works, but the emulator at 9.1 does not. The emulators burn so much battery power I usually use a physical device anyway. Also, if the latest does not work step back an Android version before freaking out and changing crap on your laptop.
Advanced Flutter Observations
While scouring github.com and stackoverflow.com for answers to my problems with the Android emulator, I came across a script that allowed me to inspect the Dart environment that is working with the device/emulator. Save this file locally as a .dart file:
import 'dart:io';
var port = 3456;
main() async {
print('HTTP_PROXY: ${Platform.environment['HTTP_PROXY']}');
print('NO_PROXY: ${Platform.environment['NO_PROXY']}');
print('');
await test('ws://echo.websocket.org');
await test('wss://echo.websocket.org');
await Future.forEach(['localhost', '127.0.0.1'], (String address) async {
final server = await startLocalServer(address, ++port);
await test('ws://$address:$port');
await server.close();
});
print('Completed!');
}
Future<HttpServer> startLocalServer(String address, int port) async {
final server = await HttpServer.bind(address, port);
print('Server listening at ${server.address.address}:$port');
server.transform(WebSocketTransformer()).listen((WebSocket client) {
client.listen((data) => client.add(data));
});
return server;
}
Future<void> test(String url) async {
print('Connecting to $url');
final ws = await WebSocket.connect(url);
send(ws, 'TEST STRING');
await receive(ws, 'TEST STRING');
await ws.close();
print('');
}
void send(WebSocket ws, String s) {
print('SEND: $s');
ws.add(s);
}
Future<void> receive(WebSocket ws, String s) async {
final msg = await ws.first.timeout(const Duration(seconds: 1));
print('RCV: $msg');
if (msg != s) {
throw 'Expected "$s" but got "$msg"';
}
}
You can run this with this command, where proxy.dart is the name of the file:
~/tools/flutter/bin/cache/dart-sdk/bin/dart --observe --pause-isolates-on-start proxy.dart
Update the path to the SDK to match where you installed it. Running the script will include a URL to a page you can load up in Chrome (Firefox Dev Edition does not work as of version 68.0b4
Then you will be able to connect to the page, which looks like this:
If you get an app working below and squint at the output in terminal you will see a link like this too, which you can then use in Chrome to observe your app. Pretty cool!
Getting the iOS Basics Working with Flutter
With all that experience, iOS should be easy, right? No. You can fire up a simulator pretty easily on the terminal: open -a Simulator should do the trick. Then you can repeat flutter run and it will start the app, but what I found was that hot reload and other streaming stuff does not seem to work. The app loads and works, but the session ends.
I get a warning: log: “Must be admin to run ‘stream’ command”. Not sure what is causing this and like the issue with Android there are lots of people with this issue stemming from a wide variety of suspected sources.
After a bit of playing around, what I determined is that, while the command above will open a simulator and run your app, you need to run the command differently to get hot reloading working. If you want to get the hot reloading support you need to start the simulator specifying it by the id that was created when you first executed the “open -a Simulator” command. To see the list of emulators type:
flutter emulators
Now you can start the app specifying the id and the “–hot” options to get hot reload support working right:
flutter emulators --launch --hot apple_ios_simulator
You can use the same technique for Android too rather than using the AVD in Android Studio. So with that I have both iOS and Android working with all the features!
I’m developing for Android, but if you are working with iOS, there are a bunch more configuration steps for your signing certs and stuff – be sure to read this page on the Flutter.io site carefully.
Developing with Flutter
So with that all out of the way, how about doing some development? Yes! I find myself using VS Code more and more, but I think I’ll try both Android Studio and VS Code, there are plugins/extensions for both, just search on Flutter and you will find them easily.
Light mode works well on the back deck in the summer. 🙂
So I can see that the Android Studio (AS) tools are a full featured IDE. Nice to be able to start the iOS simulator right from within AS. Creating a sample project from the New Project wizard is an obvious first step. Surprisingly you have the exact same features in VSC! I do really like VSC and use it quite a bit for React, Node, and other JS frameworks.
Learning the Dart syntax is necessary to really take Flutter seriously I think. I got thru the starter tutorials pretty easily. Seems to be a random smattering of hanging commas and semi-colons. I had just gotten used to ES6 where no line terminators are required. 🙁 The language seems relatively straightforward. Then there are things like prepending a “_” on a function name makes a function private. Hmm…I know less typing than “private”.
I found the layout tutorial particularly helpful to understand both “widgets” and how layouts work. Some decent debugging tools like visual layout debugging are available.
There does also seem to be a whole lot of nesting going on. The screenshot here shows a very simply layout and what it takes to get a label positioned center and left in a layout. Could get pretty crazy with a bunch of interactive widgets and elements on a screen.
Debugging with Flutter
There are some very nice tools to help:
- Flutter Inspector
- Fluter Performance
- Flutter Outline
Of course there is also a fairly complete Debug toolset in Android Studio similar to debugging with other languages like Kotlin and Android. You can also drop out to the basics of logging things to the console with commands like this:
print('_updateToConversion Fire!' + '$_inputValue');
This will basically output the value of the variable _inputValue to the IDE console or command line console.
Flutter 2.1 introduced an integrated linter, which is fantastic! This will really help a developer improve code, but some of the rules can be annoying. You can adjust these rules in the analysis_options.yaml file that sits at the project root. Two rules that I prefer to turn off are:
prefer_const_constructors: false
prefer_const_constructors_in_immutables: false
To see all the rules go here: https://dart-lang.github.io/linter/lints/index.html
I’ve notice that sometimes a process can hand in Android Studio when you try to kill a running app and fully restart it. The fix is to kill any stray Dart processes with “killall dart -9” from the terminal in Android Studio.
Great Information Sources on Flutter
- https://medium.com/asos-techblog/flutter-vs-react-native-for-ios-android-app-development-c41b4e038db9
- Udacity and Google created a Flutter Course
- Flutter’s Rendering Engine
- Documentation: https://docs.flutter.io/
- Github: https://github.com/flutter/flutter
- StackOverflow: https://stackoverflow.com/questions/tagged/flutter
- Gitter: https://gitter.im/flutter/flutter
- Effective Dart Guide: https://www.dartlang.org/guides/language/effective-dart
- Dart Tips: https://www.dartlang.org/resources/dart-tips
- Flutter FAQ: https://flutter.io/faq/
- Flutter Rendering: https://www.youtube.com/watch?v=UUfXWzp0-DU
- Flutter Engine: https://github.com/flutter/engine/wiki
- Hot Reload: https://flutter.io/hot-reload/
- Flutter Inspector: https://dart-lang.github.io/observatory/
- Dart Style Guide: https://www.dartlang.org/guides/language/effective-dart/style
- Dart Observatory: https://dart-lang.github.io/observatory/
- Widgets for Flutter: https://flutter.io/widgets/
- Flutter Gallery App Code: https://github.com/flutter/flutter/tree/master/examples/flutter_gallery
- Gallery Android App: https://play.google.com/store/apps/details?id=io.flutter.demo.gallery
- Flutter Layout: https://flutter.io/tutorials/layout/
- Material: https://material.io
- Icons: https://thenounproject.com
- Images: https://unsplash.com
- Fonts: https://fonts.google.com
- Google APIs: https://pub.dartlang.org/packages/googleapis
- Async and Futures: https://www.dartlang.org/tutorials/language/futures
- Testing: https://flutter.io/testing/
Overall Thoughts on Flutter
Well its a bit crude with no visual layout tools and the code seems to get very nested, but overall I see lots of potential if Google keep at the same pace of releases and new features. The IDE integration is pretty good. I think what is really needed is more examples of complex apps and/or tutorials that are more advanced. The Udacity Flutter course is a good start, but they should get to work on more sophisticated classes and topics. I’m going to keep going and see where Flutter flies to.
A final thought is that understanding the underlying Dart language is important. If you think Flutter is cool, don’t forget to check out Dart.
There can definitely be some tricky things with Flutter and Dart. For example if you do this in Android Studio:
data[key].map((dynamic data) => Unit.fromJson(data)).toList;
You will NOT get an error. See what I did. toList is supposed to be a function call, but I forgot the parens. No compile error or even a warning. The line should look like this:
data[key].map((dynamic data) => Unit.fromJson(data)).toList();
Very cool new widget:
https://medium.com/flutter/material-range-slider-in-flutter-a285c6e3447d