Michele Volpato

Michele Volpato

Flutter full app 3. Update to Flutter 2, sound null safety, and add a license

Tutorials Flutter
Flutter Dart just_audio
2.0.1 2.12.0 0.7.1

The Flutter team has recently announced Flutter 2. Among others, sound null safety is my favorite feature.

Today we are going to upgrade Flutter to version 2, and our project to sound null safety.

Upgrade Flutter

We start with upgrading Flutter:

flutter upgrade
flutter --version

Flutter 2.0.1 • channel stable • https://github.com/flutter/flutter.git
Framework • revision c5a4b4029c (5 days ago) • 2021-03-04 09:47:48 -0800
Engine • revision 40441def69
Tools • Dart 2.12.0

This was painless, but now when we run the app in debug mode, we are greeted with a message: Running with unsound null safety. We need to visit the migration page from the Flutter team and follow the steps to migrate our project to sound null safety.

Migrate to sound null safety

Switch to the Dart 2.12 release

We are already on 2.12, as we can see from the output of flutter --version above. Just to be sure let’s run dart --version.

Dart SDK version: 2.12.0 (stable) (Thu Feb 25 19:50:53 2021 +0100) on "macos_x64"

Check dependency status

Next, we run dart pub outdated --mode=null-safety, to check the dependency status. We depend only on just_audio.

Showing dependencies that are currently not opted in to null-safety.
[] indicates versions without null safety support.
[] indicates versions opting in to null safety.

Package Name  Current  Upgradable  Resolvable  Latest  

direct dependencies:
just_audio    ✗0.6.13  ✗0.6.15+1   ✓0.7.1      ✓0.7.1  

1 upgradable dependency is locked (in pubspec.lock) to an older version.
To update it, use `dart pub upgrade`.

1 dependency is constrained to a version that is older than a resolvable version.
To update it, edit pubspec.yaml, or run `dart pub upgrade --null-safety`.

As expected, we need to update just_audio. We should not blindly change the version in our pubspec.yaml. Let’s check first what else changed:

Version Change
  • Fix IllegalSeekPositionException on Android (DenisShakinov).
  • Fix error playing files when a user agent is set.
  • Null safety.
  • Retype headers from Map to Map<String, String>
  • Fix doc references.
  • Fix bug with spaces in asset paths.
  • Fix bug setting a ClippingAudioSource after another source.
  • Update ICY metadata feature status in README.
  • Initial support for ICY metadata on iOS.
  • Upgrade to ExoPlayer 2.13.1 (MichealReed).

Nothing seems needing our attention. Our app is still so simple that it will unlikely break on a package upgrade.

We update the version of just_audio:

# Play music files
just_audio: ^0.7.1

and re-run dart pub outdated --mode=null-safety.

Showing dependencies that are currently not opted in to null-safety.
[] indicates versions without null safety support.
[] indicates versions opting in to null safety.

All your dependencies declare support for null-safety.

Migrate with the migration tool

Now we are ready for the migration, using the tool provided by the Flutter team.

dart migrate
See https://dart.dev/go/null-safety-migration for a migration guide.

Analyzing project...
[------------------------------------------------------------\]No analysis issues found.

Generating migration suggestions...

Compiling instrumentation information...

View the migration suggestions by visiting:

With a link to a webpage with the changes the tool made automatically:

The Flutter null safety migration tool

The Flutter null safety migration tool

There are not many changes, but we want to perform the migration manually, to really understand what is happening.

Open pubspec.yaml and change the environment sdk:

  sdk: '>=2.12.0 <3.0.0'

We will get errors because the code is no sound nulls safe.

Open audio_metadata.dart and think about the field. We want the title to always be provided. About the artwork, we assume that sometimes it might not be available, but we want to have a default placeholder. Thus we change the constructor to:

// TODO change placeholder
    {required this.title, this.artwork = 'https://via.placeholder.com/150'});

In player.dart the property _audioPlayer is not initialized in a constructor, but in initState(). We can mark it as late.

late AudioPlayer _audioPlayer;

In playlist.dart we need to change the type of the key in the constuctor.

const Playlist(this._audioPlayer, {Key? key}) : super(key: key);

It can be null, in fact, we do not use it in your app. We also need to update the StreamBuild generic type to SequenceState?, as per just_audio 0.7.1.

return StreamBuilder<SequenceState?>( // this was changed
      stream: _audioPlayer.sequenceStateStream,
      builder: (context, snapshot) {
        final state = snapshot.data;
        if (state == null) return CircularProgressIndicator(); // this was added
        final sequence = state.sequence; // this was changed
        return ListView(...)

Finally open player_buttons.dart. As for playlist.dart we need to update the type of the key:

const PlayerButtons(this._audioPlayer, {Key? key}) : super(key: key);

We need to update the generic type of the previous and next buttons’ StreamBuilder.


We also need to change the parameter type of _playPauseButton to be nullable.

Widget _playPauseButton(PlayerState? playerState) {

The final change is to the onPressed function of the replay button:

return IconButton(
        icon: Icon(Icons.replay),
        iconSize: 64.0,
        onPressed: () => _audioPlayer.seek(Duration.zero,
            index: _audioPlayer.effectiveIndices?.first), // this was changed

Now we are ready to run the app again 🤞…

💪 Running with sound null safety 💪

Yes, we did it.

Choose a license

If you want to distribute code on the Internet, like I am doing now with this series of articles, it is a good idea to think about a license. GitHub has a website to help you choose an open-source license, if you do not choose one, then the ordinary copyright law of your country applies.

I am not sure about how this series of articles will develop in the future, but in case it becomes a real app, I want to protect the code so that no other person can copy it and upload it to the stores.

For this reason, I am not going to use an open-source license, for now. You, as a reader, can read the code, download it, learn from it and run it on you machine with the purpose of learning. But you are not allowed to modify and/or use it in any project, commercial or non-commercial.

If you would like to re-use the code for one of your projects, any kind of projects, you can contact me, and we can discuss the specific use.

The current version of the code is available on GitHub.

In the next article we will add Provider and we will move the hardcoded data out of the audio player. We will also add the possibility to select different playlists.

Get a weekly email about Flutter

Subscribe to get a weekly curated list of articles and videos about Flutter and Dart.

    We respect your privacy. Unsubscribe at any time.

    Leave a comment