Write BETTER code with Dart 2.17 (Included in Flutter 3)
Tags: flutter, dart, programming
Published: 2022-05-25 10:58:50

Flutter 3 was announced in the last Google I/O event, and with it the possibility of being able to deploy desktop applications on macOS and Linux in a stable way. They also showed the new performance improvements that this new version brings, and regarding the programming language of Flutter, Dart, version 2.17 was officially presented; which is the version included by default in new projects created with Flutter 3.

> 📽 Video version available on [YouTube](https://youtu.be/mGOxuLV2EwI) and [Odysee](https://odysee.com/@svprdga:d/write-better-code-with-dart-2-17-included-in-flutter-3)

{% embed https://youtu.be/mGOxuLV2EwI %}

In this blog I have previously covered the incredible performance improvements affecting [Flutter for web apps](https://davidserrano.io/flutter-3-for-web-have-they-really-managed-to-increase-performance) and the possibility of running [macOS applications natively in Apple Silicon processors](https://davidserrano.io/amazing-performance-improvements-in-flutter-3-for-mac-apps-macos-apple-silicon-m1-stable-support). In this article I am going to delve into the most notable improvements that _Dart 2.17_ brings, which is the enhancement of enums, the super initializers and the removal of order constraint for named parameters.

In order to easily see and understand what are these improvements, I am going to present you a very simple application created with Flutter 2. Then I am going to update it to Flutter 3 and I will show you the changes that we should make to this application to apply each one of these improvements, in this graphical way you will easily understand what are those changes and how you can benefit from them in your applications developed with Flutter 3 and Dart 2.17.

To start with, I'll show you the test application I've created in the previous version of Flutter, version `2.10.5`:

### Flutter 2

This is the test application:

![App screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ao5tbx3sxa75p6l39xdg.png)
 

It consists of 3 different parts painted in a different color, with the name of the color in the center of the container.

To determine the color and its name I use the following `enum`:

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

enum MyColor {
  red,
  green,
  blue,
}

extension MyColorExtension on MyColor {
  String getName() {
    switch (this) {
      case MyColor.red:
        return 'Red';
      case MyColor.green:
        return 'Green';
      case MyColor.blue:
        return 'Blue';
    }
  }

  MaterialColor getMaterialColor() {
    switch (this) {
      case MyColor.red:
        return Colors.red;
      case MyColor.green:
        return Colors.green;
      case MyColor.blue:
        return Colors.blue;
    }
  }
}
```

As you can see, this enum defines what color it is, and through an extension I add its name and what `MaterialColor` it refers to. I will use these values ​​later to be able to paint the container and to display the name of the color.

Before Dart 2.17, the only way to be able to add values ​​to an enum is by using extensions.

Each of the three fragments shown in the previous screenshot is represented by the following widget:

```dart
class ColorWidget extends StatelessWidget {
  final MyColor color;

  const ColorWidget(this.color, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Container(
        color: color.getMaterialColor(),
        child: Center(
          child: Text(
            color.getName(),
            style: const TextStyle(
              color: Colors.white,
              fontWeight: FontWeight.bold,
              fontSize: 24.0,
            ),
          ),
        ),
      ),
    );
  }
}
```

This widget receives a value from the previously created enum and uses it to paint the background of the container and display the name.

Finally, this widget is used in the following way:

```dart
import 'package:flutter/material.dart';
import 'package:flutter_3_dart_2_17/color.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: MainScreen());
  }
}

class MainScreen extends StatelessWidget {
  const MainScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Dart 2.17 example'),
      ),
      body: Column(
        children: const [
          ColorWidget(
            MyColor.red,
            key: Key('Red'),
          ),
          ColorWidget(
            MyColor.green,
            key: Key('Green'),
          ),
          ColorWidget(
            MyColor.blue,
            key: Key('Blue'),
          ),
        ],
      ),
    );
  }
}
```

So that you can appreciate the changes that we will make later, note that the `key` parameter that the widgets receive has to be passed to the super class in the way described in this example:

```dart
const App({Key? key}) : super(key: key);
```

We will change this later so that we will do the same thing with less code. Also in this case when we declare a `ColorWidget` we have to pass the `key` parameter at the end, since it is a named parameter. If I try to alter the order of the parameters the compiler will give me an error:

```dart
// In previous versions of Dart, named parameters
// have to be at the end
ColorWidget(
  key: Key('Blue'),
  MyColor.blue, // <- This throws an error
),
```

The next step is to upgrade to Flutter 3 and start using Dart 2.17 to apply the new improvements.

### Flutter 3 and Dart 2.17

After executing a `flutter upgrade` to update to Flutter 3, the next thing we need to do is increase the minimum SDK level in `pubspec.yaml` in order to use Dart 2.17:

```yaml
environment:
  sdk: ">=2.17.0 <3.0.0"
```

Next, we are going to apply the following changes to the previous _enum_ `MyColor`:

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

enum MyColor {
  red('red', Colors.red),
  green('green', Colors.green),
  blue('blue', Colors.blue);

  final String name;
  final MaterialColor materialColor;

  const MyColor(this.name, this.materialColor);
}
```

As you can see, we can define two variables, `name` and `materialColor`; which are passed by constructor on each value. In this way we can do the same job as before but in a cleaner and more elegant way, with less code, and without the need to use extension functions.

Now let's do the following to `ColorWidget`:

```dart
class ColorWidget extends StatelessWidget {
  final MyColor color;

  const ColorWidget(this.color, {super.key});

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Container(
        color: color.materialColor,
        child: Center(
          child: Text(
            color.name,
            style: const TextStyle(
              color: Colors.white,
              fontWeight: FontWeight.bold,
              fontSize: 24.0,
            ),
          ),
        ),
      ),
    );
  }
}
```

Here we are using the variables created earlier, for example before we got the name of the color with `color.getName()` now we just have to do `color.name`.

Also note the change in the `key` parameter passed in the constructor. With dart 2.17 we can simply pass this parameter to the super class via `super.key`. In this case the change is small, but let's imagine a constructor like this:

```dart
class ClassThatExtends extends SuperClass {
  ClassThatExtends({
    required int firstInt,
    required int secondInt,
    required int thirdInt,
    required int fourthInt,
    required int fifthInt,
    required int sixthInt,
    required int seventhInt,
  }) : super(
          firstInt: firstInt,
          secondInt: secondInt,
          thirdInt: thirdInt,
          fourthInt: fourthInt,
          fifthInt: fifthInt,
          sixthInt: sixthInt,
          seventhInt: seventhInt,
        );
}
```

After using this new enhancement, it would look like this:

```dart
class ClassThatExtends extends SuperClass {
  ClassThatExtends({
    required super.firstInt,
    required super.secondInt,
    required super.thirdInt,
    required super.fourthInt,
    required super.fifthInt,
    required super.sixthInt,
    required super.seventhInt,
  });
}
```

To finish, we will modify the rest of the widgets as follows:

```dart
import 'package:flutter/material.dart';
import 'package:flutter_3_dart_2_17/color.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: MainScreen());
  }
}

class MainScreen extends StatelessWidget {
  const MainScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Dart 2.17 example'),
      ),
      body: Column(
        children: const [
          ColorWidget(
            key: Key('Red'),
            MyColor.red,
          ),
          ColorWidget(
            key: Key('Green'),
            MyColor.green,
          ),
          ColorWidget(
            key: Key('Blue'),
            MyColor.blue,
          ),
        ],
      ),
    );
  }
}
```

In addition to applying the super constructor enhancement, notice that I've altered the order of the `key` parameters and the associated `MaterialColor` in the `ColorWidget`. In Dart 2.17 we can place the named parameters in any order we see fit. In this simple example the order has little influence, but let's take a look at the example the Flutter team used:

```dart
final factorials = List<int>.generate(
  10,
  (int i) {
    if (i == 0) {
      return 1;
    } else {
      var result = 1;
      for (var r = 2; r <= 1; ++r) {
        result *= r;
      }
      return result;
    }
  },
  // This param could be difficult to see due to the code block above
  growable: true,
);
```

Here the `growable` parameter is hard to see, after applying this tweak we can place it earlier to make it easier for the reader to understand that it's there:

```dart
final factorials = List<int>.generate(
  10,
  growable: true,
  (int i) {
    if (i == 0) {
      return 1;
    } else {
      var result = 1;
      for (var r = 2; r <= 1; ++r) {
        result *= r;
      }
      return result;
    }
  },
);
```

### Conclusion

In this article we have seen 3 of the improvements applied in Dart 2.17. If you want, you can take a look at the [official blog post](https://medium.com/dartlang/dart-2-17-b216bfc80c5d) to see all these improvements and the rest of the new features that this new version of Dart offers.

You can find the sample of this article [here](https://github.com/svprdga/Flutter-3-Dart-2.17-Example).

Thank you very much for reading this far.