Posts in the “flutter” category

Dart example: Factory pattern, constructors, mixins

I’m generally getting out of the writing business, but that being said, I don’t mind sharing some code as I go along. In this case I created the follow Dart programming examples as I reload Dart into my brain:

  • Dart Factory pattern/method example
  • Dart constructors with public and private fields
  • Dart mixin example

Flutter/Dart: A few ways to simulate slow-responding functions and methods

As a quick note, here are a couple of examples of how to simulate a slow-responding Flutter/Dart method:

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

Future<bool> getEnableNotifications() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    return Future.delayed(const Duration(milliseconds: 1000), () {
        return prefs.getBool(KEY_ENABLE_NOTIFICATIONS) ?? false;      
    });
}

As shown, the first example uses Future.delayed followed by a then call, and the second example shows Future.delayed with a lambda function.

You’ll want to use this code/technique when you’re working with Flutter futures and its FutureBuilder class.

I was reminded about the need to test things likes futures and FutureBuilder when some code I found on the internet wasn’t working properly. As a result I wrote my tutorial, How to correctly create a Flutter FutureBuilder.

Flutter/Dart: How to use async code inside a non-async method (like initState)

As a brief note, sometimes in Flutter (and Dart) you have to write a method/function that makes an asynchronous call, but the method can’t be marked async itself. The Flutter initState method is a good example of this.

So far I’ve learned two ways to deal with this:

  • Use then inside the non-async method
  • Use an unusual anonymous-function style syntax inside the method

I’ll demonstrate each technique below.

Dart Map class forEach method examples

As a brief note to self, here’s an example of the forEach method on a Dart Map class. First, a sample Dart Map:

final states = {
    'AK' : 'Alaska',
    'AL' : 'Alabama', 
    'AR' : 'Arizona'
};

Dart lists: A multiline map method example

Many times when you use the Dart map method on a list, you’ll write the method as a one-line function. For example, the second line of code here shows a one-line map method on a Dart list:

final xs = ['aaa', 'bb', 'cccc'];
final ys = xs.map((String s) => s.length);  //one-line 'map'
ys.forEach(print);

An example of a Flutter RadioListTile in a ListView

Here’s a little example of a Flutter time preferences widget, something I’ll be using in the new version of my Just Be app. Probably most important thing about this source code is that it shows an example of how to use the Flutter RadioListTile:

import 'package:flutter/material.dart';

class TimeValue {
    final int _key;
    final String _value;
    TimeValue(this._key, this._value);
}

class TimePreferencesWidget extends StatefulWidget {
    @override
    TimePreferencesWidgetState createState() => TimePreferencesWidgetState();
}

class TimePreferencesWidgetState extends State<TimePreferencesWidget> {
    int _currentTimeValue = 1;

    final _buttonOptions = [
        TimeValue(30,  "30 minutes"),
        TimeValue(60,  "1 hour"),
        TimeValue(120, "2 hours"),
        TimeValue(240, "4 hours"),
        TimeValue(480, "8 hours"),
        TimeValue(720, "12 hours"),
    ];

    @override
    Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
                title: Text("Time Preferences"),
            ),
            body: ListView(
                padding: EdgeInsets.all(8.0),
                children: _buttonOptions.map((timeValue) => RadioListTile<int>(
                    groupValue: _currentTimeValue,
                    title: Text(timeValue._value),
                    value: timeValue._key,
                    onChanged: (val) {
                        setState(() {
                            debugPrint('VAL = $val');
                            _currentTimeValue = val;
                        });
                    },
                )).toList(),
            ),
        );
    }
}

I initially wanted to create _buttonOptions as a Map, but I ended up creating the TimeValue class to get what I wanted. (I’m still new to Dart, and I couldn’t find a good way to convert _buttonOptions.map from a Map to a List, which is required for the children parameter, so I went with the approach shown.)

Here’s an image of what this code creates:

An example of a Flutter RadioListTile in a ListView

In summary, if you wanted to see an example of a Flutter RadioListTile inside a ListView, I hope this example is helpful.

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.

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 Dart 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(),
    );
}

Dart/Flutter: How to use a Future with a Duration delay

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

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.

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.)

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.