简体   繁体   English

在 main.dart 中使用 FutureBuilder

[英]Using FutureBuilder in main.dart

Below code always show OnboardingScreen a little time (maybe miliseconds), after that display MyHomePage.下面的代码总是显示 OnboardingScreen 一段时间(可能是几毫秒),然后显示 MyHomePage。 I am sure that you all understand what i try to do.我相信你们都明白我想做什么。 I am using FutureBuilder to check getString method has data.我正在使用 FutureBuilder 来检查 getString 方法是否有数据。 Whats my fault ?什么是我的错? Or any other best way for this ?或者任何其他最好的方法?

saveString() async {
  final prefs = await SharedPreferences.getInstance();
  prefs.setString('firstOpen', '1');
}

getString() method always return string. getString() 方法总是返回字符串。

getString() async {
  final prefs = await SharedPreferences.getInstance();
  String txt = prefs.getString('firstOpen');
  return txt;
}

main.dart main.dart

home: new FutureBuilder(
            future: getString(),
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return MyHomePage();
              } else {
                return OnboardingScreen();
              }
            })

Usually I'm using another route, rather than FutureBuilder.通常我使用另一条路线,而不是 FutureBuilder。 Because futurebuilder every hot reload will reset the futureBuilder.因为futurebuilder 每次热重载都会重置futureBuilder。

There always will be some delay before the data loads, so you need to show something before the data will load.在数据加载之前总会有一些延迟,因此您需要在数据加载之前显示一些内容。

Snapshot.hasData is showing only the return data of the resolved future. Snapshot.hasData 仅显示已解决未来的返回数据。

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: SplashScreen(),
    );
  }
}

class SplashScreen extends StatefulWidget {
  @override
  _SplashScreenState createState() => _SplashScreenState();
}

const isOnboardingFinished = 'isOnboardingFinished';

class _SplashScreenState extends State<SplashScreen> {
  Timer timer;
  bool isLoading = true;

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

  Future<void> _checkIfFirstOpen() async {
    final prefs = await SharedPreferences.getInstance();
    var hasOpened = prefs.getBool(isOnboardingFinished) ?? false;

    if (hasOpened) {
      _changePage();
    } else {
      setState(() {
        isLoading = false;
      });
    }
  }

  _changePage() {
    Navigator.of(context).pushReplacement(
      // this is route builder without any animation
      PageRouteBuilder(
        pageBuilder: (context, animation1, animation2) => HomePage(),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return isLoading ? Container() : OnBoarding();
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(child: Text('homePage'));
  }
}

class OnBoarding extends StatelessWidget {
  Future<void> handleClose(BuildContext context) async {
    final prefs = await SharedPreferences.getInstance();
    prefs.setBool(isOnboardingFinished, true);
    Navigator.of(context).pushReplacement(
      MaterialPageRoute(
        builder: (_) => HomePage(),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Center(
        child: RaisedButton(
          onPressed: () => handleClose(context),
          child: Text('finish on bording and never show again'),
        ),
      ),
    );
  }
}

From the FutureBuilder class documentation :来自FutureBuilder 类文档

The future must have been obtained earlier, eg during State.initState, State.didUpdateConfig, or State.didChangeDependencies.未来必须更早获得,例如在 State.initState、State.didUpdateConfig 或 State.didChangeDependencies 期间。 It must not be created during the State.build or StatelessWidget.build method call when constructing the FutureBuilder.在构造 FutureBuilder 时,它不能在 State.build 或 StatelessWidget.build 方法调用期间创建。 If the future is created at the same time as the FutureBuilder, then every time the FutureBuilder's parent is rebuilt, the asynchronous task will be restarted.如果future和FutureBuilder同时创建,那么每次重建FutureBuilder的parent时,异步任务都会重新启动。

So you need to create a new Stateful widget to store this Future's as a State.因此,您需要创建一个新的 Stateful 小部件来将此 Future 存储为一个状态。 With this state you can check which page to show.在这种状态下,您可以检查要显示的页面。 As suggested, you can start the future in the initState method:按照建议,您可以在 initState 方法中启动未来:

class FirstPage extends StatefulWidget {

  _FirstPageState createState() => _FirstPageState();
}

class _FirstPageState extends State<FirstPage> {
final Future<String> storedFuture;

  @override
  void initState() {
      super.initState();
      storedFuture = getString();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
            future: storedFuture,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return MyHomePage();
              } else {
                return OnboardingScreen();
              }
            });
  }
}

So in your home property you can call it FirstPage:因此,在您的家庭财产中,您可以将其称为 FirstPage:

home: FirstPage(),

Your mistake was calling getString() from within the build method, which would restart the async call everytime the screen gets rebuilt.您的错误是从 build 方法中调用getString() ,这会在每次重建屏幕时重新启动异步调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM