I like to use final members in my classes whenever it makes sense, but I ran into a problem trying to do something relatively simple:

import 'dart:ui';

class Foo {
  final PictureRecorder recorder;
  final Canvas canvas;
  final int margin;

  Foo({
    this.margin = 10,
  })  : recorder = PictureRecorder(),
        canvas = Canvas(recorder) { // this line errors out
  }
}

The error I'm getting from the compiler is:

The instance member 'recorder' can't be accessed in an initializer. Try replacing the reference to the instance member with a different expression

Is there another way of doing what I want without making the canvas field non-final?

I know I can make the class look similar "from the outside" using a non-final private field and a custom getter, but it feels like an unnecessary workaround.


Solution 1: diegoveloper

Try using late keyword for the canvas.

late final Canvas canvas;

And the constructor should be updated like this :

Foo({
    this.margin = 10,
  }) : recorder = PictureRecorder() {
    canvas = Canvas(recorder);
  }

Check this post to get more info: https://dev.to/pktintali/late-variables-in-dart-dart-learning-series-1-2opf


Solution 2: Yeasin Sheikh

You can do this

class Foo {
  final PictureRecorder recorder;
  late final Canvas canvas;
  final int margin;

  Foo({
    this.margin = 10,
  }) : recorder = PictureRecorder() {
    canvas = Canvas(recorder);
  }
}


Solution 3: nietaki

Here's the workaround I have in mind, but I'd rather do without:

import 'dart:ui';

class Foo {
  final PictureRecorder recorder;
  Canvas? _canvas;
  final int margin;

  Canvas get canvas => _canvas!;

  PatternVisualizer({
    this.margin = 10,
  }) : recorder = PictureRecorder() {
    _canvas = Canvas(recorder);
  }
}


Solution 4: julemand101

You can make use of a factory constructor which calls your real (hidden) constructor like this:

class Foo {
  final PictureRecorder recorder;
  final Canvas canvas;
  final int margin;

  factory Foo({
    int margin = 10,
  }) {
    final recorder = PictureRecorder();
    final canvas = Canvas(recorder);

    return Foo._(
      recorder: recorder,
      canvas: canvas,
      margin: margin,
    );
  }

  Foo._({
    required this.recorder,
    required this.canvas,
    required this.margin,
  });
}

Since we have yet to create any object when running inside the factory body, we are allowed to do anything as long as we return an object compatible with the type which the factory constructor is part of.

So we can create all necessary values and then forward them to our real constructor and prevent the use of any late variables.