Migrating to Dart’s Null Safety

Prior to starting on my 4th Flutter mobile app, and because it seems that every plugin now supports it, I decided to give migrating to Dart’s null safety a go.

Sound null safety is available in Dart 2.12 and Flutter 2.

In reading through the migration documentation, it appears the migration tool may pose as much time and effort as migrating by hand. And being I’m in this to learn the code, migrating by hand is the path I’ve opted for. I’m currently on Step 2.4 of the Migration Guide.

Because each of my reminder apps is setup with a similar code base, I also decided to migrate all three apps at the same time. Thus far I’ve got one app down to 500 critical errors, while the other two are down to 396 and 393.

Mobile Apps Critical Warn Info
B4-I-Go 500 10 64
Hungry-on_Hand 396 8 56
Xpired To Be 393 10 58

Having started at over 600 critical errors on the B4-I-Go app, the migration thus far has been tedious, but informative and enlightening—to both Dart, and my app’s own code.

I’m sure a pattern of migrating will emerge, but currently the most time-consuming aspect comes in having to analyze the context of each error to determine if a variable can be null, or if it will never be null. It seems a small distinction between the two when reading about them, but in context, the decision seems quite important for each case.

? |=>
  • Indicates an object member’s leftmost operand can be null; example: foo?.bar selects property bar from expression foo unless foo is null (in which case the value of foo?.bar is null).
  • You can make a variable nullable by putting a question mark (?) at the end of its type.
    For example, a variable of type int? might be an integer, or it might be null.
! |=>
  • If you know that an expression never evaluates to null but Dart disagrees.

  • Map<String, List<String>> _dataMap = {};
    _dataMap.putIfAbsent('message', () => []);
  • // ^^^ ‘!‘ means we know the message key will never be null because we added it on the line above.

The other two prominent null safe keywords are required and late. While required was introduced before I began learning Flutter via the @required annotation, which is easily remedied by removing the `@`, I’ve come across ample variables that have made good of the new late modifier, which simply indicates the variable will be initialized later.

If you fail to initialize a late variable, a runtime error occurs when the variable is used.

Fortunately, should errors prevail when the migration is complete, I should also get a good look at Flutter’s new debugging updates.

Happy migrating!

– Keith | https://keithdc.com

Converting nullable dynamic Lists inside Maps

As a last-minute add, below is a null-safe approach used to convert a dynamic List inside a Map, such as when using json.decode, into a List of Strings.
If anyone knows if anything is incorrect or can be done better, please comment. Always looking to learn proper methodologies and better approaches.
Two scenarios: A key exists in a Map, or it doesn’t (is null).
Map<String, List<dynamic>> _statusObj = {‘message’: [‘Hello!’]};
Map<String, List<dynamic>> _statusObj = {};
A null-safe solution:
List<String> _alertMsgs = List<String>.from(_statusObj[“message”]?.toList() ?? <String>[]);
Reference notes with comments and some approaches that didn’t work:

Keith D Commiskey

1 thought on “Migrating to Dart’s Null Safety

  1. The overall migration to null safety doing three similar apps at the same time took 3+ days.

    Starting at 500 critical errors on the B4-I-Go app, not counting the first 157 critical errors…

    From 157 – 341
    @ 184 crits | Was 4:15 min per crit.

    From 341 – 500
    @ 159 crits | Was 3:10 min per crit.

    This included research and analysis, general debugging, and breaks.

    Towards the end some patterns did emerge, and the last 91 critical errors took less than 2 hours at about 1:15 min per crit.

Leave a Reply