I'm new to Flutter and confused about how InheritedWidget
works with routes. I'm using an SQLite database with the sqflite library. Basically, what I'm trying to achieve is, when my app is launched, I want all widgets that don't require the database to show right away. For instance, the bottomNavigationBar of my Scaffold
doesn't need the database but the body does. So I want the bottomNavigationBar to show right away, and a CircularProgressIndicator
to be shown in the body. Once the database is open, I want the body to show content loaded from the database.
So, in my attempt to achieve this, I use FutureBuilder
before my Scaffold
to open the database. While the Future
is not completed, I pass null
for the drawer and a CircularProgressBar
for the body, and the bottomNavigationBar as normal. When the Future
completes, I wrap the drawer and body (called HomePage
) both with their own InheritedWidget
(called DataAccessor
). This seems to work, as I can access the DataAccessor
in my HomePage
widget. But, when I use the Navigator
in my drawer to navigate to my SettingsScreen
, my DataAccessor
is not accessible and returns null.
Here's some example code, not using a database but just a 5 second delayed Future
:
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: FutureBuilder(
future: Future.delayed(Duration(seconds: 5)),
builder: (context, snapshot) {
Widget drawer;
Widget body;
if (snapshot.connectionState == ConnectionState.done) {
drawer = DataAccessor(
child: Drawer(
child: ListView(
children: <Widget>[
ListTile(
title: Text("Settings"),
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => SettingsScreen()))
)
]
)
)
);
body = DataAccessor(child: HomePage());
}
else {
drawer = null;
body = Center(child: CircularProgressIndicator());
}
return Scaffold(
drawer: drawer,
body: body,
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Container(), title: Text("One")),
BottomNavigationBarItem(icon: Container(), title: Text("Two"))
]
)
);
}
)
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
DataAccessor dataAccessor = DataAccessor.of(context); //dataAccessor IS NOT null here
print("HomePage: ${dataAccessor == null}");
return Text("HomePage");
}
}
class SettingsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
DataAccessor dataAccessor = DataAccessor.of(context); //dataAccessor IS null here
print("SettingsScreen: ${dataAccessor == null}");
return Text("SettingsScreen");
}
}
class DataAccessor extends InheritedWidget {
DataAccessor({Key key, Widget child}) : super(key: key, child: child);
@override
bool updateShouldNotify(InheritedWidget oldWidget) => false;
static DataAccessor of(BuildContext context) => context.inheritFromWidgetOfExactType(DataAccessor);
}
It's possible I'm doing things wrong. Not sure how good of practice storing widgets in variables is. Or using the same InheritedWidget
twice? I've also tried wrapping the entire Scaffold
with my DataAccessor
(and having the database as null while it is loading), but the issue still remains where I can't get my DataAccessor
in my SettingsScreen
.
I've read that a possible solution is to put my InheritedWidget
before the MaterialApp
but I don't want to resort to this. I don't want a whole new screen to show while my database is opening, I want my widgets that don't need the database to be shown. This should be possible somehow.
Thanks!