36

I am programming in Flutter using Dart 2.1.0, and come across this situation:

mixin Salt {
  final int pinches;  // Immutable, and I want to delay initialization.

  // Cannot declare constructors for mixin
}

class Meat with Salt {
  Meat(int pinches) ... // How to initialize it?
}

Salt has no constructor, so I cannot use initializer list. pinches is final, so I cannot set it in Meat's constructor.

I don't want to make Salt a class because Meat may need to extend from something else.

And I want to keep pinches immutable.

Any way to do it? Thanks in advance.

Denis Sablukov
  • 3,360
  • 2
  • 26
  • 31
Nick Lee
  • 5,639
  • 3
  • 27
  • 35

5 Answers5

44

You can change the declaration of your mixin to:

mixin Salt {
  int get pitches;
}

And then define the field inside the implementation class

class Meat with Salt {
  final int pitches;
  Meat(this.pitches);
} 
Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • 8
    What point of using `mixin` in this situation, If we should duplicate code this way? – Denis Sablukov Jan 13 '19 at 11:56
  • 19
    Mixins are not for declaring variables. But for reusable methods instead – Rémi Rousselet Jan 13 '19 at 12:37
  • 1
    @RémiRousselet how can I initialize on Flutter? `DemiLogic` is mixin `class _DemoPageState extends State with DemoLogic { ` – BIS Tech Apr 05 '21 at 16:46
  • It's not strictly necessary, but `final int pitches` should be annotated with `@override`. Also the property in the `mixin` could be declared as `abstract final int pitches` instead of getter. Totally just flavour but worth mentioning. – Forke Aug 30 '23 at 09:20
7

I offer my take on a solution to this. By marking the variable late you can make it final. No warning will appear if you fail to initialize it so use with caution.

mixin Salt {
  late final int pinches;
}

class Vegetable with Salt {
  Vegetable(int pinches) {
    this.pinches = pinches;
  }
}
Nathaniel Johnson
  • 4,731
  • 1
  • 42
  • 69
6

By design it is not possible to declare a final member into a mixin because it is not possible to declare a constructor for initializing the final member, citing the docs:

However, in this proposal, a mixin may only be extracted from a class that has no declared constructors. This restriction avoids complications that arise due to the need to pass constructor parameters up the inheritance chain.

A compromise may be to declare a private member and implement only a getter.
_pinches is visible only inside the library, it is read-only for library users.

mixin Salt {
  int _pinches;

  get pinches => _pinches;

}

class Meat with Salt {

  Meat(int pinches)  {
   _pinches = pinches;
  }
}

Note: the above pattern, because of the visibility rules, works only if the mixin and the mixed classes reside in the same library.

attdona
  • 17,196
  • 7
  • 49
  • 60
  • That only works if `Salt` and `Meat` are declared in the same library, otherwise `_pinches` would not be accessible. – Günter Zöchbauer Jan 13 '19 at 14:27
  • 4
    Can I half-accept two answers, because @attdona and @RémiRousselet together give the whole picture? @RémiRousselet 's suggestion would work, but it defeats the purpose of having data in mixins. And `_pinches` is not strictly immutable in @attdona 's suggestion. You together confirm that there's no ideal solution. I have up-voted both answers, but am sorry for not (fully) accepting either. – Nick Lee Jan 14 '19 at 14:08
2

Similar to attdona's suggestion, but a little bit closer to what you really wanted, you could do it like

mixin Salt {
  int _pinches;
  int get pinches => _pinches;
  void initSalt(int pinches) {
    assert(_pinches == null);
    _pinches = pinches;
  }
}

class Meat with Salt {
  Meat(int pinches) {
    initSalt(pinches);
  }
}

It's still not strictly final, but (so long as the mixin's in a different library so you can't change the private member directly) it's immutable at runtime. Not as good as if it could be properly final, but maybe close enough.

Raven Black
  • 349
  • 3
  • 4
2

The following method allows you to set the data at a later time, and gets rid of the warning:

This class (or a class that this class inherits from) is marked as '@immutable', but one or more of its instance fields aren't final

mixin Salt {
  final SaltData _saltData = SaltData();

  int get pinches => _saltData.pinches;

  set pinches(int extraData) {
    _saltData.pinches = extraData;
  }
}

class SaltData {
  int pinches = 0;
}

So what I did is create a class SaltData. This will store all the variables you need.

The private _saltData variable is final, this will stop the warning.

Then use a getter and setter to retrieve and update the data.

int get pinches => _saltData.pinches;

set pinches(int extraData) {
  _saltData.pinches = extraData;
}

If you want you can could expose the entire saltData object as well:

SaltData get saltData => _saltData;

vixez
  • 846
  • 9
  • 25