I am making an example to understand how it works and the correct use of InheritedWidget
, which until now seems to be misused.
While running the example, reading the documentation, and testing, I notice some issues that I don't understand.
problem 1
Using the example of the documentation on InheritedWidget
, they tell us how it works , how to create it and how to use it , all this with an example of Color
. But they never tell us how I can change that value of Color
.
Example from the documentation :
class FrogColor extends InheritedWidget {
const FrogColor({
Key? key,
required this.color,
required Widget child,
}) : super(key: key, child: child);
final Color color;
static FrogColor of(BuildContext context) {
final FrogColor? result = context.dependOnInheritedWidgetOfExactType<FrogColor>();
assert(result != null, 'No FrogColor found in context');
return result!;
}
@override
bool updateShouldNotify(FrogColor old) => color != old.color;
}
After some research and looking at several examples, I managed to do the color change by creating a class ColorBloc
and using Stream
, I use this in my example below when I tackle problem 2
class ColorBloc
class ColorBloc {
Color myColor;
ColorBloc({this.myColor = Colors.amber});
final _color = StreamController<Color>();
Stream<Color> get colorStream => _color.stream;
void changeColor(Color newColor) {
_color.add(newColor);
myColor = newColor;
}
}
problem 2
When using my own InheritedWidget
I realize that apart from using a listener (such as StreamBuilder
) that checks the changes made to the colorBloc
.
If you want to use it on a second screen/route, you need to use MaterialApp
it otherwise when navigating to it it sends the following error.
════════ Exception caught by widgets library ═══════════════════════════════════
The following NoSuchMethodError was thrown building SecondPage(dirty):
The getter 'colorBloc' was called on null.
Receiver: null
Tried calling: colorBloc
The only way I can get it to work correctly is with MaterialApp
, which I don't know if it's the right thing to do since it's not the main of the app and I hope it's a good use of theInheritedWidget
usage exampleInheritedWidget
Home and FirstPage
class MyInheritedWidgetPage extends StatelessWidget {
final myColor = ColorBloc();
@override
Widget build(BuildContext context) {
myColor.myColor = Colors.blue;
return MyInheritedWidget(
colorBloc: myColor,
child: StreamBuilder<Color>(
stream: myColor.colorStream,
builder: (context, snapshot) {
print('myColor => $myColor');
return MaterialApp(
home: FirstPage(),
);
}),
);
}
}
class FirstPage extends StatelessWidget {
const FirstPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
final color = MyInheritedWidget.of(context).colorBloc;
return Scaffold(
appBar: AppBar(
title: Text('MyInheritedWidget'),
backgroundColor: color.myColor,
),
body: Center(
child: Column(
children: [
Text('Color nuevo',
style: TextStyle(
color: MyInheritedWidget.of(context).colorBloc.myColor)),
RaisedButton(
child: Text('Set color red'),
onPressed: () {
MyInheritedWidget.of(context).colorBloc.changeColor(Colors.red);
}),
RaisedButton(
child: Text('navigate to second page'),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => SecondPage()));
}),
],
),
),
);
}
}
Second Page
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final color = MyInheritedWidget.of(context).colorBloc;
return Scaffold(
appBar: AppBar(
title: Text('Second'),
backgroundColor: color.myColor,
),
body: Center(
child: Column(
children: [
Text('Hola Mundo', style: TextStyle(color: color.myColor),),
RaisedButton(
child: Text('Change color to amber'),
onPressed: () => color.changeColor(Colors.amber))
],
),
),
);
}
}
All this is my public repository , which I use for small flutter examples
The
InheritedWidget
serves as a dependency injector, you can inject values likeBlocs
and get it in the widget tree.If you want it to
MyInheritedWidget
be exposed throughout your app, you need to start it above theMaterialApp
(Same as in provider/flutter_bloc).It would be something like:
If you see that hot-reloading restarts the bloc, then you require a Stateful and declare the
ColorBloc
within the State.He
MaterialApp
has aNavigator
by default, every time you do apush
to a new page/view, it is created at the same level.Tree representation:
When you move the
MyInheritedWidgetPage
top ofMaterialApp
it would be:And so they can access all the widgets.