Navigation problem with FutureBuilder and MaterialApp Navigation problem with FutureBuilder and MaterialApp dart dart

Navigation problem with FutureBuilder and MaterialApp


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(),            ),          );        });  }}


so I think I have found the error, actually you must have an MaterialApp inside runApp() as root.

so you can't have MaterialApp inside FutureBuilderwhat you can do is make MaterialApp the root widget and have a default Home Screen and inside its build method you can have your FutureBuilder but again don't include materialApp inside it just use Scaffold directly.

EDIT :To answer the question regarding app theme

You can have switching themes by usingtheme and darkTheme in materialApp And control themeMode from Provider or any other state management approach.

MaterialApp(          title: 'Flutter Tutorials',          debugShowCheckedModeBanner: false,          theme: AppTheme.lightTheme,          darkTheme: AppTheme.darkTheme,          themeMode: appState.isDarkModeOn ? ThemeMode.dark : ThemeMode.light,          home: ThemeDemo(),        );

There are several ways to do it here is one more that I found custom theme app

Try this out it will work, if doesn't let me know


I think you can do as follow:

  1. Move all Future data/FutureBuilder into different Stateless/Stateful Widget and override the theme color with Theme class

    class SecondPage extends StatelessWidget {  @override  Widget build(BuildContext context) {    var color = Theme.of(context).primaryColor;    return FutureBuilder(      future: Model.getColor(),      builder: (context, snapshot) {        if (!snapshot.hasData) {          return Theme(            data: ThemeData(primaryColor: color),             child: _buildWait(),          );        } else {          color = snapshot.data;          return Theme(            data: ThemeData(primaryColor: color),            child: Scaffold(              appBar: AppBar(),            ),          );        }      },    );  }}
  2. The first page use the local variable to store color

    ...Color _primaryColor;@overridevoid initState() {  _primaryColor = Theme.of(context).primaryColor;  super.initState();}@overrideWidget build(BuildContext context) {  return MaterialApp(    theme: ThemeData(primaryColor: _primaryColor),    home: _buildPage(),  );}...
  3. If you want the first page update the theme on the same time, you should use some method to share data between widget (e.g. Provider). I use the simple method to catch the custom return value

    // First Page// use "then" can get the return value from the other route...onPressed: () {  Navigator.push(context, MaterialPageRoute(builder: (context) {    return SecondPage();  })).then((color) {    setState(() {      _primaryColor = color;    });  });},...// Second Page// WillPopScope can catch navigator pop event...return WillPopScope(  onWillPop: () async {    Navigator.of(context).pop(color);    return Future.value(false);  },  child: FutureBuilder(   ...

If it is not necessary for you to use Routing when you try to change Theme,I can provide a simple solution that change the theme data by Theme class

ThemeData currentTheme;@overridevoid initState() {  super.initState();  currentTheme = Theme.of(context);}@overrideWidget build(BuildContext context) {  return MaterialApp(    home: FutureBuilder<Color>(      future: color,      builder: (context, snapshot) {        Widget child;        if (snapshot.hasError) throw snapshot.error;        if (snapshot.connectionState != ConnectionState.done) {          child =  Scaffold(            appBar: AppBar(),            body: const Center(child: CircularProgressIndicator()),          );        }else{          currentTheme = currentTheme.copyWith(primaryColor: snapshot.data);          child = _buildPage();        }        return Theme(          data: currentTheme,          child: child,        );      },    ),  );}

Here is the document of Themes for part of an application