Dmitry Bubnenkov
Dmitry Bubnenkov

Reputation: 9859

This Overlay widget cannot be marked as needing to build because the framework is already in the process of building widgets

I have got follow app:

class MyAppState extends State<MyApp>
{
  TenderApiProvider _tenderApiProvider = TenderApiProvider();

  Future init() async {
     await _tenderApiProvider.getToken();  
  }
  MyAppState()
  {
    init();
  }

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(builder: (_) => _tenderApiProvider),
      ],
      child: MaterialApp(
        title: "My App",
        routes: {
          '/': (context) => HomePage(),
          '/splash-screen': (context) => SplashScreen(),
          '/result_table': (context) => ResultDataTable(),
        }

      ),
    );
  }

}

I need to draw firstly SplashScreen current code show at start HomePage.

In splash-screen I need to switch to HomePage after all data loaded. Here it's code:

  Widget build(BuildContext context) {
    TenderApiProvider apiProv = Provider.of<TenderApiProvider>(context);
    return StreamBuilder(
        stream: apiProv.resultController,
        builder: (BuildContext context, AsyncSnapshot snapshot) {   
        //...
        if(apiProv.apiKeyLoadingState ==  ApiKeyLoadingState.Done && apiProv.regionsLoadingState == RegionsLoadingState.Done)
        {
         Navigator.of(context).pushNamed("/"); // Should it be placed in Build??
        }
        });
    }

Could you help me and show to to draw at app start SplashScreen and then switch from it to HomePage?

Upvotes: 12

Views: 13027

Answers (2)

Daniel Eberl
Daniel Eberl

Reputation: 1424

You will need to wrap your SplashScreen() inside a StatefulWidget so you can fetch your data in initState(). It is important to wrap fetch() logic inside a SchedulerBinding.instance.addPostFrameCallback() to access the BuildContext inside initState(). Also, that way, you avoid conflicts with RenderObjects that get destoryed while they are actually build.

Following a complete minimal example. EDIT: You cant use await in initState({}).

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Wrapper(),
    );
  }
}


class Wrapper extends StatefulWidget {
  @override
  _WrapperState createState() => _WrapperState();
}

class _WrapperState extends State<Wrapper> {

  @override
  void initState() {
    super.initState();
    SchedulerBinding.instance.addPostFrameCallback((_) {
      _loadDataAndNavigate()
    });
  }

  _loadDataAndNavigate() async {
    // fetch data | await this.service.fetch(x,y)
    Navigator.of(context).pushNamed('/');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SplashScreen(),
    );
  }
}

Upvotes: 12

Junsu Cho
Junsu Cho

Reputation: 854

i use splashScreen

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SplashHome(),
      routes: <String, WidgetBuilder>{
        '/HomeScreen': (BuildContext context) => new ImageHome()
      },
    );
  }
}

class SplashHome extends StatefulWidget{
  @override
  State<StatefulWidget> createState() {
    return _SplashHome();
  }
}

const timeout = const Duration(seconds: 2);

class _SplashHome extends State<SplashHome>{

  startTimeout() {
    return new Timer(timeout, handleTimeout);
  }
  void handleTimeout() {
    Navigator.of(context).pushReplacementNamed('/HomeScreen');
  }

  @override
  void initState() {
    super.initState();
    startTimeout();
  }

  @override
  Widget build(BuildContext context) {
    return new Container(
      color: Colors.lightBlue,
    );
  }
}

Upvotes: 0

Related Questions