下面的代码总是显示OnboardingScreen一小段时间(可能是毫秒(,然后显示MyHomePage。我相信你们都明白我想做什么。我正在使用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();
}
})
通常我使用另一条路线,而不是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'),
),
),
);
}
}
来自FutureBuilder类文档:
future必须更早获得,例如在State.initState、State.didUpdateConfig或State.didChangeDependencies期间。在构造FutureBuilder时,不能在State.build或StatelessWidget.build方法调用期间创建。如果future与FutureBuilder同时创建,那么每次重建FutureBuilders的父级时,异步任务都将重新启动。
因此,您需要创建一个新的Stateful小部件来将此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();
}
});
}
}
因此,在你的房产中,你可以称之为FirstPage:
home: FirstPage(),
您的错误是从构建方法中调用getString()
,这将在每次重建屏幕时重新启动异步调用。