I'm having trouble accessing a services object when initializing a stateful widget. The problem comes from the context object not being available in initState.
I'm using InheritedWidget to inject a services object in my main.dart file like so
void main() async { final sqflite.Database database = await _openDatabase('db.sqlite3'); runApp( Services( database: database, child: MyApp(), ), ); } The Services object is quite straightforward. It will have more than just the database as a member. The idea is that the widgets don't need to know if a local database, local cache, or remote server is being accessed.
class Services extends InheritedWidget { final Database database; const Services({ Key key, @required Widget child, @required this.database, }) : assert(child != null), assert(database != null), super(key: key, child: child); Future<List<models.Animal>> readAnimals() async { return db.readAnimals(database: this.database); } @override bool updateShouldNotify(InheritedWidget oldWidget) { return false; } static Services of(BuildContext context) { return context.inheritFromWidgetOfExactType(Services) as Services; } } The trouble comes in my _HomePageState state when I want to load all the animals from the database. I need to access the Services object. I cannot access the Services object in initState so I am using didChangeDependencies. A problem comes when the home page is removed from the stack. It seems didChangeDependences is called and the access to the context object is illegal. So I created an _initialized flag that I can use in didChangeDependencies to ensure I only load the animals the first time. This seems very inelegant. Is there a better way?
class _HomePageState extends State<HomePage> { bool _initialized = false; bool _loading = false; List<Animal> _animals; @override Widget build(final BuildContext context) { return Scaffold( appBar: AppBar( title: Text(Strings.of(this.context).appName), ), body: _HomeBody( loading: this._loading, animals: this._animals, ), ); } @override void didChangeDependencies() { super.didChangeDependencies(); if (!this._initialized) { this._initialized = true; this._loadAnimals(); } } void _loadAnimals() async { this.setState(() { this._loading = true; this._animals = null; }); final List<Animal> animals = await Services.of(this.context).readAnimals(); this.setState(() { this._loading = false; this._animals = animals; }); } }
InheritedWidgetdirectly but it seems like a common way to inject dependencies in the widget tree. WouldScopedModelorProvidermake a difference? I thought they were based onInheritedWidgetanyway.