My app has a state which is computed as a Future. For example it includes a theme color, because I want to change the color when I navigate. I try to display a progress indicator while waiting for the data.
But I can't make it work. Either Navigator.push is not working and the app bar is missing, or I have no progress indicator and a route error...
Here is a code snippet.
import 'package:flutter/material.dart';
void main() => runApp(Test());
class Test extends StatefulWidget {
@override
State<StatefulWidget> createState() => _TestState();
}
class _TestState extends State<Test> {
Future<Color> color = Model.getColor();
@override
Widget build(BuildContext context) {
return FutureBuilder<Color>(
future: color,
builder: (context, snapshot) {
if (snapshot.hasError) throw snapshot.error;
if (snapshot.connectionState != ConnectionState.done) {
if (false) {
// Navigation not working. App bar missing.
return Material(child: Center(child: CircularProgressIndicator()));
} else {
// Progress not working. Screen flickering.
return MaterialApp(home: _buildWait());
}
}
var app = MaterialApp(
theme: ThemeData(primaryColor: snapshot.data),
home: _buildPage(),
// ERROR: The builder for route "/" returned null.
// routes: {'/': (_) => _buildPage()},
);
return app;
},
);
}
Widget _buildPage() {
return Builder(
builder: (context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: RaisedButton(
child: Text('Push'),
onPressed: () {
setState(() {
color = Model.getColor();
});
Navigator.push(context, MaterialPageRoute(builder: (context) {
return Scaffold(appBar: AppBar());
}));
},
),
),
);
},
);
}
}
Widget _buildWait() {
return Scaffold(
appBar: AppBar(title: Text('Wait...')),
body: Center(child: CircularProgressIndicator()),
);
}
class Model {
static final _colors = [Colors.red, Colors.green, Colors.amber];
static int _index = 0;
static Future<Color> getColor() {
return Future.delayed(Duration(seconds: 2), () => _colors[_index++ % _colors.length]);
}
}
Expected result: when I push the button to navigate to the new route, it should display a progress indicator, and then the new screen with a different theme color.
Now try the following. Try to make a root widget separately, because root widget is always there. you don't want a complete UI route to persist in the memory. Also make next route as a separate widget.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Test',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Test(),
);
}
}
class Test extends StatefulWidget {
@override
State<StatefulWidget> createState() => _TestState();
}
class _TestState extends State<Test> {
Future<Color> color = Model.getColor();
@override
Widget build(BuildContext context) {
return FutureBuilder<Color>(
future: color,
builder: (context, snapshot) {
if (snapshot.hasError) return Center(child: Text("An Error Occurred"));
if (snapshot.connectionState == ConnectionState.waiting) {
return _buildWait();
}
var app = Theme(
data: ThemeData(primaryColor: snapshot.data),
child: _buildPage(),
);
return app;
},
);
}
Widget _buildPage() {
return Scaffold(
appBar: AppBar(),
body: Center(
child: RaisedButton(
child: Text('Push'),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return NextRoute();
}));
},
),
),
);
}
}
Widget _buildWait() {
return Scaffold(
appBar: AppBar(title: Text('Wait...')),
body: Center(child: CircularProgressIndicator()),
);
}
class Model {
static final _colors = [Colors.red, Colors.green, Colors.amber];
static int _index = 0;
static Future<Color> getColor() {
return Future.delayed(
Duration(seconds: 2), () => _colors[_index++ % _colors.length]);
}
}
class NextRoute extends StatefulWidget {
NextRoute({Key key}) : super(key: key);
@override
_NextRouteState createState() => _NextRouteState();
}
class _NextRouteState extends State<NextRoute> {
@override
Widget build(BuildContext context) {
return FutureBuilder<Color>(
future: Model.getColor(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text("An Error Occurred"),
);
}
if (snapshot.connectionState == ConnectionState.waiting) {
return _buildWait();
}
return Theme(
data: ThemeData(primaryColor: snapshot.data),
child: Scaffold(
appBar: AppBar(),
),
);
});
}
}