Posts in the “flutter” category

A Dart Isolates example (Actors in Dart)

Dart Isolates give you a way to perform real multi-threading in Dart applications, the ability to truly run code on another thread. (For more information on that statement, see Dart futures are NOT run in a separate thread.)

When I first read that Isolates were like actors I was very interested in them, but then I was surprised to see that (IMHO) they are implemented in a more low-level/primitive way than something like the Akka Actors library. (The entrepreneur out there might see this as an opportunity to create an Akka-like port for Dart and Flutter.)

An attempt at a Flutter file logger using Dart isolates

Filed in the “FWIW” category ... I wrote the following Dart/Flutter code as an attempt to create a Flutter file logger using Dart isolates, but as it turns out, at the time of this writing the Flutter platform won’t let you do that. So I thought I’d share/save my code here in case I can use it in the future.

First, here’s my Flutter file logging code that uses Dart isolates:

Dart futures are NOT run in a separate thread (they are run in the event loop)

I’ve been working with Flutter and Dart for several weeks now, and I was surprised to read several times that Dart is single-threaded, knowing that it has a concept of a Future (or futures) and async methods. Last night I read this excellent article about Dart’s event loop, which sums up Dart futures very nicely in that statement:

“the code of these Futures will be run as soon as the Event Loop has some time. This will give the user the feeling that things are being processed in parallel (while we now know it is not the case).”

Earlier in the article the author also states:

“An async method is NOT executed in parallel but following the regular sequence of events, handled by the Event Loop, too.”

So, in summary, Dart has a single-threaded event loop, and futures and async methods aren’t handled by a separate thread; they’re handled by the single-threaded event loop whenever it has nothing else to do.

I just wanted to note this here for myself today, but for many more details, please see that article, which also discusses Dart isolates, which are like a more primitive form of Akka actors.

Flutter: Override widgets/components to get access to events

As a brief “note to self,” when you need to get access to Flutter widget events that you normally can’t access, override the widget to access lifecycle-related events like initState() and dispose() so you can access them. For example, this image (that comes from this URL) shows how to gain access to those lifecycle methods for a Flutter Drawer. You can do this with any component/widget — and you can also make the code more generic by passing in child widgets — and you can also mix in WidgetsBindingObserver and then use didChangeAppLifecycleState to gain access to more events.

Flutter Quick Reference

Flutter Quick Reference - book cover

When I started working with Flutter a couple of months ago to develop iOS and Android apps from one code base, I started keeping notes about how to do things with Flutter and Dart (the programming language behind Flutter). These notes became my own personal cheatsheet, and then those notes just kept getting larger, and larger.

From there, I started to create a book I titled “Flutter Quick Reference” based on those notes. Right now this “book” is really just a very large Flutter/Dart cheatsheet, but because some of the content in it can’t be found elsewhere on the internet, I thought I’d share it here. Also, because I don’t know if I’ll ever take the time to finish making this into a real book, I made this first release free.

Flutter: How to supply an initial value to a TextFormField

If you need to supply an initial value to a Flutter TextFormField (and possibly a TextField), the initialValue parameter may not do what you expect it to do. (More on this in the Discussion.) Instead, I just used this technique to populate the initial value for a TextFormField when using it with a TextEditingController:

A Dart Future/then/catchError example

When I was trying to debug a problem that I thought was related to Flutter’s SharedPreferences library, I ended up rewriting a preferences method to use code like this rather than the normal approach:

Future<SharedPreferences> fPrefs = SharedPreferences.getInstance();
fPrefs.then((value) {rez = value.getBool(KEY_ENABLE_NOTIFICATIONS) ?? false; })
   .catchError((e) {
       debugPrint("===== ERROR: ${e.error}");
       return 60;
   });
return rez;

While that ended up being a waste of time, the benefit of my side excursion is that I get to show this example of how to use then and catchError with a Dart future. So if you wanted to see a Dart Future/then/catchError example, I hope this is helpful.

How to use a Future with a Duration delay in Dart (and Flutter)

As a quick note, here are two examples of how to use a Future with a Duration delay in Dart (and Flutter):

// example 1
Future<bool> _getFutureBool() {
    return Future.delayed(Duration(milliseconds: 500))
        .then((onValue) => true);
}

// example 2
print('1');
Future.delayed(const Duration(milliseconds: 500), () {
    print('Hello, world');
});
print('2');

If you’re comfortable with Dart futures, you know that the second example will (eventually) print this output:

1
2
Hello, world

Using a SQLite date/time field with Flutter and Dart

As a brief note, SQLite doesn’t have date/time (datetime) fields, so when you want to use a datetime field with Flutter (and Dart), you have to do something else.

My choice was to store my datetime data as a SQLite INTEGER field. I did this so I can query and sort that field easily myself. (The benefit of storing a datetime field as a TEXT field is that you can read it easier, but I think an INTEGER field is a little easier to work with, though YMMV.)

Dart: How to remove leading spaces in a multiline string (using splitMapJoin)

As far as I know, there’s no built-in way to remove spaces from a multiline string in Dart, but you can write your own function to do this. For example, I just dug into the splitMapJoin method of the Dart String class, and wrote a stripMargin function like this:

String stripMargin(String s) {
    return s.splitMapJoin(
        RegExp(r'^', multiLine: true),
        onMatch: (_) => '\n',
        onNonMatch: (n) => n.trim(),
    );
}

How to create a Dart project manually

[toc]

If you want to create a Dart project, probably the best way to do that currently is to use Stagehand:

Once you install that you can create new Dart command-line applications like this:

// create a command-line application
stagehand console-full

However, if you just want to create a little Dart project manually, you can also just follow the steps below:

A correct Flutter FutureBuilder example (for SharedPreferences, REST services, and database access)

At the time of this writing (September 19, 2019), there’s a lot of bad information in books and on the internet about how to use a Flutter FutureBuilder. That bad information caused me to waste a lot of time over the last two days. I don’t have time today to explain everything, but in short, here’s a technically correct FutureBuilder example:

import 'package:flutter/material.dart';
import 'shared_prefs_helper.dart';

class PreferencesEnableNotifications extends StatefulWidget {
    @override
    PreferencesEnableNotificationsState createState() => PreferencesEnableNotificationsState();
}

class PreferencesEnableNotificationsState extends State<PreferencesEnableNotifications> {

    @override
    Widget build(BuildContext context) {

        return Scaffold(
            appBar: AppBar(
                title: Text("Enable Notifications"),
            ),
            body: FutureBuilder<bool>(
                future: SharedPreferencesHelper.getEnableNotifications(),  //returns bool
                builder: (BuildContext context, AsyncSnapshot snapshot) {
                    if (snapshot.connectionState == ConnectionState.done) {
                        // YOUR CUSTOM CODE GOES HERE
                        return CheckboxListTile(
                            title: const Text('Enable Notifications'),
                            value: snapshot.data,  //the bool in the SharedPreferences
                            onChanged: (val) {
                                setState(() {
                                    SharedPreferencesHelper.setEnableNotifications(val);
                                });
                            }
                        );
                    } else {
                        return new CircularProgressIndicator();
                    }
                }
            )
        );
    }
}

The solution

The key part of this proper solution is everything inside of the “builder:” code. The most important thing to do is to show something like the CircularProgressIndicator while the FutureBuilder is waiting for the data to arrive. In this example the data comes from a Flutter SharedPreference, specifically this method which returns a Future:

future: SharedPreferencesHelper.getEnableNotifications(),

Why this is important

If you don’t write the FutureBuilder code as shown, you’ll be in for a world of hurt when your data is returned slowly (in my case, the data from the SharedPreferencesHelper.getEnableNotifications() method). If your data comes back in 10ms or so, you might be fine without the code shown, but if/when your data comes back more slowly, you’ll get some really ugly exceptions written in yellow text on a red background if you don’t use the code shown. (Just use the code shown. Always.)

I’ll try to write more when I have time, but in short, if you want to create a Flutter FutureBuilder that runs without errors when your data is returned slowly, I can confirm that this approach works successfully.

Update: Still not 100% correct

As I tried to do more complicated things with my app, I found out that the solution above is still not 100% correct, even though it’s much better than other documentation that’s out there.

The big thing I have learned is that you should not directly call a function in the “future: ...” part of the code. Because your build method may be called many times — think 60 times a second — if you call a function in that area, you’ll end up calling that function many times. Instead, what you’re supposed to do is create your future as a class-level variable in a State method like initState.

I’ll show how to do this when I have more time, but this is actually an extremely important point. I found this out the hard way when I started adding some animation into my build method, and suddenly the function shown in the original code above was being called many times, instead of just once.

When you have Flutter app performance problems, try Profile Mode

Today I learned that for several reasons, Flutter Debug mode may be significantly slower than Production mode. As just one reason, Debug mode is compiled using JIT while Production mode uses AOT. You can read more about the reasons on the Flutter UI Performance page.

A very important note on that page is that if you’re using a real hardware device (as opposed to an emulator), you can run your code in Profile mode like this: