The provider can't be found and even if you're sure it should be there, check the widget inspector and see if the type of the provider is right.
To use the provider, type of the provider needs to be specific. Even if it's inherited from the parent class, the provider won't automatically find it.
abstract class A extends ChangeNotifier {
get string;
}
class AB extends A {
@override
get string => 'AB';
}
class AC extends A {
@override
get string => 'AC';
}
Here's some inherited changenotifier classes.
class MyGenericProviderApp extends StatelessWidget {
const MyGenericProviderApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(useMaterial3: true, primaryColor: Colors.red),
home: GenericProvider(
changeNotifier: AB(),
),
);
}
}
class GenericProvider extends StatelessWidget {
const GenericProvider({super.key, required this.changeNotifier});
final A changeNotifier;
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider.value(
value: changeNotifier,
builder: (context, child) {
return const ProviderConsumer();
},
);
}
}
class ProviderConsumer extends StatelessWidget {
const ProviderConsumer({super.key});
@override
Widget build(BuildContext context) {
var provider = Provider.of<AB>(context);
return Scaffold(
body: Center(child: Text(provider.string)),
);
}
}
Above code will throw an error saying that the provider of AB can't be found.
ProviderNotFoundException (Error: Could not find the correct Provider<AB> above this ProviderConsumer Widget...)
The consumer cannot know if the Provider if of 'A' or 'AB' because it's given as 'A'. With the widget inspector, the problem can be seen.
The 'AB' changenotifier is provided as 'A'. This is why flutter can't find the right provider of type 'AB', because it isn't 'AB' but 'A'.
This problem can be solved easily with type casting.
var provider = Provider.of<A>(context) as AB;
First way is to cast the type of the provider to inherited class in the child widget. Downside of this solution is that the provider should be casted everytime it's called. It can mess up the code when using context.read or context.watch.
But I have an alternate solution that can avoid that problem.
class GenericProvider<T extends A> extends StatelessWidget {
const GenericProvider({super.key, required this.changeNotifier});
final T changeNotifier;
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider.value(
value: changeNotifier,
builder: (context, child) {
return const ProviderConsumer();
},
);
}
}
Type casting is no longer needed if generic method is used.
Now, in the widget inspector, the provider is of type 'AB'.
Anyways, this mistake I've made with provider type has taught me some lessons. With generic expressions, I think I can solve some problems and also cleanup codes with various types.