在main.dart中使用FutureBuilder

问题描述 投票:1回答:3

下面的代码总是在显示MyHomePage之后总是显示OnboardingScreen一点时间(可能是几毫秒)。我相信你们都知道我要做什么。我正在使用FutureBuilder检查getString方法是否有数据。我怎么了或任何其他最佳方法?

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

getString()方法始终返回字符串。

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

main.dart

home: new FutureBuilder(
            future: getString(),
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return MyHomePage();
              } else {
                return OnboardingScreen();
              }
            })
flutter sharedpreferences
3个回答
1
投票

通常,我使用的是另一条路线,而不是FutureBuilder。因为futurebuilder每次热重装都会重置futureBuilder。

在加载数据之前总是会有一些延迟,因此您需要在加载数据之前显示一些内容。

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

0
投票

FutureBuilder class documentation

[未来一定是较早获得的,例如在State.initState,State.didUpdateConfig或State.didChangeDependencies中。构造FutureBuilder时,不得在State.build或StatelessWidget.build方法调用期间创建它。如果future与FutureBuilder同时创建,则每次重新构建FutureBuilder的父代时,异步任务都将重新启动。

因此,您需要创建一个新的有状态小部件以将该Future的状态存储为State。在此状态下,您可以检查要显示的页面。如建议的那样,您可以在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();
              }
            });
  }
}

因此,在您的家庭财产中,您可以将其称为第一页:

home: FirstPage(),

您的错误是从build方法中调用getString(),这将在每次重建屏幕时重新启动异步调用。


0
投票

您必须在构建方法调用之前发送该数据,这意味着您已经从父窗口小部件传递了该数据。

以下代码可能会对您有所帮助。

class DeleteWidget extends StatelessWidget {
  const DeleteWidget({Key key}) : super(key: key);

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('test app'),
        ),
        body: Container(
          child: Center(
            child: RaisedButton(
              onPressed: () async {
                String data = await getString();
                Navigator.push(
                    context,
                    MaterialPageRoute(
                        builder: (context) => Delete2(
                              check: data,
                            )));
              },
              child: Text("press"),
            ),
          ),
        ));
  }
}

class Delete2 extends StatelessWidget {
  final check;
  const Delete2({Key key, this.check}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      child: check != '1' ? Text("MyHomePage") : Text("OnboardingScreen"),
    );
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.