Flutter 3

Software developmentFlutter

Flutter 3

Yesterday during Google I/O Google announced the release of Flutter 3.0.
You can have a look at the full video or read the blog post about Flutter 3 or the one about Dart 2.17.

There are plenty of new features but what stroke me most were two tiny announcements that were part of the Dart 2.17 release notes because they basically target two blog posts I made 😁.

Dart enums

I had a blog post describing how an enum can be extended by using extensions.
In the context of the Flutter 3 announcement they also announced an evolution of enums in Dart.

It is now possible to have a constructor and members directly in enums without using extensions. The only thing we loose is the reverse map providing the fromValue method.
Here is the example from my previous post but adapted to Dart 2.17:

enum FlagValues {
  flag1(0x01 << 0),
  flag2(0x01 << 1),
  flag3(0x01 << 2),
  flag4(0x01 << 3),

  final int value;
  const FlagValues(this.value);

  //checks if this FlagValues is set in the given bit field
  bool isSetIn(int bitfield) => (bitfield & value) != 0;

  //sets the bit(s) that match(es) [this] in the given bitfield and returns the result
  int setIn(int bitField) => bitField | value;

  //erases the bit(s) that match(es) [this] in the given bitfield and returns the result
  int eraseIn(int bitfield) => bitfield & ~value;

  int operator &(int bitfield) {
    return bitfield & value;

  int operator |(int bitfield) {
    return bitfield | value;

  int operator ~() {
    return ~value;

  String toString() => '$name, $value (${FlagValues.valueToString(value)})';

  static String valueToString(int value) {
    return '0x${value.toRadixString(2).padLeft(8, '0')}';

void main() {
  const flag = FlagValues.flag2;
  // getting the value tied to the enum
  final val = flag.value;
  assert(val == 2);

  //setting some bits
  int bitfield = FlagValues.flag1.value;
  bitfield = FlagValues.flag3 | bitfield;
  assert(bitfield == 5);
  print('Flag 1 | 3: $bitfield (${FlagValues.valueToString(bitfield)})');
  bitfield = FlagValues.flag3.eraseIn(bitfield);
  assert(bitfield == 1);
  print('Flag 3 erased: $bitfield (${FlagValues.valueToString(bitfield)})');

  //check if bits are set
      '${FlagValues.flag2} is not set in $bitfield (${FlagValues.valueToString(bitfield)})');


Do you remember my blog post about passing big amount of data between Isolates and my complaint about the lack of destructors in Dart? Sure you do 😉. In case you don’t you can read it here.

And guess what? In Flutter 3.0 they added destructors! Kind of 😁.
The concept is that you can attach a Finalizer to an object and when the object is garbage collected it will be called.
It is not the same as a destructor as you have to attach it by your own but at least you can attach functionality to the garbage collection mechanism.

Sadly this example doesn’t run as web and ffi are no friends.

import 'dart:ffi' as ffi;
import 'package:ffi/ffi.dart';

class ManagesForeignResources {
  late final ffi.Pointer<ffi.Int8> _ptr;

  ManagesForeignResources() {
    _ptr = calloc<ffi.Int8>(500);
    _finalizer.attach(this, _ptr, detach: this);

  static final Finalizer<ffi.Pointer<ffi.Int8>> _finalizer = Finalizer((ptr) {
    print('Clean-up happened');

void main() async {
    //scope for mgr
    final mgr = ManagesForeignResources();
  // let's hope that the garbage collector will hit in those 100s
  await Future.delayed(const Duration(seconds: 100));


So 2 of my major problems with Flutter / Dart are solved now. At least somehow.
The next thing I wish for is: Real threading as an option. Don’t replace Isolates but give us the option to dig into that locking and race condition hell if we think we need to be there 😁.


Alternative wish: Provide a mechanism on Dart language level to make immutable objects and allow passing them through the Isolate boundary without any copying involved. Then I could do without having real threads.

Post Directory


  1. Flutter 3
    1. Dart enums
    2. Destructors
  2. Summary