I'm trying to get the following to work. Changes to the app model state are not picked up via the InheritedWidget 'AppStateProvider'. I've manage to get this working with sinks/streams but was hoping to established a simpler structure.
This is just a test application to switch between various app modes.
What's missing?
import 'package:flutter/material.dart';
void main() {
runApp(AppStateProvider(
child: RootPage(),
appState: new AppState(),
));
}
enum AppMode { introduction, login, home }
class AppState {
AppMode appMode;
AppState({
this.appMode = AppMode.introduction,
});
}
class AppStateProvider extends InheritedWidget {
final AppState appState;
AppStateProvider({Key key, Widget child, this.appState})
: super(key: key, child: child);
@override
bool updateShouldNotify(InheritedWidget oldWidget) => true;
static AppStateProvider of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(AppStateProvider)
as AppStateProvider);
}
}
class RootPage extends StatelessWidget {
AppMode _mode;
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Inherited Widget Test',
theme: new ThemeData(
primarySwatch: Colors.blueGrey,
),
home: _body(context),
);
}
Widget _body(BuildContext context) {
final provider = AppStateProvider.of(context); //Registers as a listener
final state = provider.appState;
_mode = state.appMode;
return new Stack(
children: <Widget>[
new Offstage(
offstage: _mode != AppMode.introduction,
child: new MaterialApp(
home: ColorsListPage(
color: Colors.red,
targetAppMode: AppMode.login,
title: "Intro",
),
),
),
new Offstage(
offstage: _mode != AppMode.login,
child: new MaterialApp(
home: ColorsListPage(
color: Colors.blue,
targetAppMode: AppMode.home,
title: "Login",
),
),
),
new Offstage(
offstage: _mode != AppMode.home,
child: new MaterialApp(
home: ColorsListPage(
color: Colors.green,
targetAppMode: AppMode.introduction,
title: "Home",
),
),
),
],
);
}
}
class ColorDetailPage extends StatefulWidget {
final String title;
final MaterialColor color;
final int materialIndex;
final AppMode targetAppMode;
ColorDetailPage(
{this.color, this.title, this.targetAppMode, this.materialIndex: 500});
@override
_ColorDetailPageState createState() => new _ColorDetailPageState();
}
class _ColorDetailPageState extends State<ColorDetailPage> {
@override
Widget build(BuildContext context) {
final provider = AppStateProvider.of(context);
return Scaffold(
appBar: AppBar(
backgroundColor: widget.color,
title: Text(
'$widget.title[$widget.materialIndex]',
),
),
body: Container(
color: widget.color[widget.materialIndex],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
provider.appState.appMode = widget.targetAppMode;
});
},
heroTag: null,
),
);
}
}
class ColorsListPage extends StatefulWidget {
final MaterialColor color;
final String title;
final ValueChanged<int> onPush;
final AppMode targetAppMode;
final List<int> materialIndices = [
100,
200,
300,
400,
500,
600,
700,
800,
900,
];
ColorsListPage({this.color, this.targetAppMode, this.title, this.onPush});
@override
_ColorsListPageState createState() => new _ColorsListPageState();
}
class _ColorsListPageState extends State<ColorsListPage> {
@override
Widget build(BuildContext context) {
final provider = AppStateProvider.of(context);
return new Scaffold(
appBar: AppBar(
title: Text(widget.title),
backgroundColor: widget.color,
),
body: Container(
color: Colors.white,
child: _buildList(context),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
provider.appState.appMode = widget.targetAppMode;
});
},
heroTag: null,
));
}
Widget _buildList(BuildContext context) {
return ListView.builder(
itemCount: widget.materialIndices.length,
itemBuilder: (BuildContext content, int index) {
int materialIndex = widget.materialIndices[index];
return Container(
color: widget.color[materialIndex],
child: ListTile(
title: Text(
"$materialIndex",
style: TextStyle(fontSize: 24.0),
),
trailing: Icon(Icons.chevron_right),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ColorDetailPage(
color: widget.color,
title: widget.title,
targetAppMode: widget.targetAppMode,
materialIndex: materialIndex,
)),
);
}
//onTap: () => onPush(materialIndex),
));
},
);
}
}
You need to wrap your InheritedWidget
inside a StatefulWidget
class _AppStateProvider extends InheritedWidget {
final AppStateProviderState data;
_AppStateProvider({Key key, @required Widget child, @required this.data})
: super(key: key, child: child);
@override
bool updateShouldNotify(InheritedWidget oldWidget) => true;
}
class AppStateProvider extends StatefulWidget {
final Widget child;
final AppState appState;
AppStateProvider({
@required this.child,
@required this.appState,
});
static AppStateProviderState of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(_AppStateProvider)
as _AppStateProvider)
.data;
}
@override
AppStateProviderState createState() => AppStateProviderState(
appState,
);
}
class AppStateProviderState extends State<AppStateProvider> {
AppState appState;
AppStateProviderState(this.appState);
void updateAppMode(AppMode appMode) {
setState(() {
appState.appMode = appMode;
});
}
@override
Widget build(BuildContext context) {
return _AppStateProvider(
data: this,
child: widget.child,
);
}
}
pay attention to this method:
void updateAppMode(AppMode appMode) {
setState(() {
appState.appMode = appMode;
});
}
you can use it like this:
floatingActionButton: FloatingActionButton(
onPressed: () {
provider.updateAppMode(widget.targetAppMode);
},
heroTag: null,
),