We can do this:
class WorldModel {
WorldModel({required this.skyModel});
final SkyModel skyModel;
static final instance = Provider<WorldModel>(
(ref) => WorldModel(
skyModel: SkyModel(),
),
);
final countBirds = Provider<int>((ref) => 25);
}
Which would mean that we can only access our countBirds
provider after we have accessed the WorldModel
instance. Those (in build()
method):
Widget build(BuildContext context, WidgetRef ref) {
final worldModel = ref.watch(WorldModel.instance);
final countBirds = ref.watch(worldModel.countBirds);
return Text(countBirds.toString());
}
Otherwise, we can even define it like this:
late final countBirds = Provider<int>((ref) => 5);
It all works great and performs its function 100%. Even when using the .autoDispose
modifier, everything works fine and is disposed of. But, the official documentation strongly recommends using providers ONLY as final ones:
Providers should always be final.
Does this mean they can be late
? Why and what are the pitfalls?
About function
Why do I need this (I’m talking about definitions in the WorldModel
class)? This is because countBirds
may depend on some fields of the WorldModel
class. I can’t do it any other way, just because I think it’s good practice for dependency injection. Here is a good example:
class WorldModel {
WorldModel({required this.skyModel});
final SkyModel skyModel;
static final instance = Provider<WorldModel>(
(ref) => WorldModel(skyModel: SkyModel()),
);
late final countBirds = Provider<int>((ref) => skyModel.countBirds);
}
class SkyModel {
late int countBirds;
}
2
Answers
It’s not recommend to use providers as
static
, while I don’t think there is technically an issue with that but still it’s a bad practices.As for defining the provider as
final
inside a class withoutstatic
keyword, this will lead to a new provider being created with every instance of the class, so when you lose access to the class instance you will not be able to access the provider and this will be a bigger issue if the provider isn’t auto-disposable.Providers can be defined late as following
And then you can initialize the providers before accessing in the widget tree by warping the parent widget with
ProviderScope
And then you can access the providers in any widget in the widget tree which is under the
ProviderScope
.Another thing you can do is to use
.family
modifierHowever you have to pass the same
SkyModel
instance to reuse the same provider instance, otherwise you will get a new providerProviders should always be final because non-final providers will lead to low performance.
All in all, you should make unchanging variables final because it’s good modelling, not because you worry about performance. Providers should be unchanging.
Why providers should be unchanging? Because there is no reason why you may need it changing. If you need different behaviours of the provider – you can encapsulate these behaviours into the provider. If you need providers with different types you should create several final providers with different types.
You can make the provider not final but this may lead to runtime errors if you do not keep track of the type or behaviour of the provider.