下面的代码总是在显示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();
}
})
通常,我使用的是另一条路线,而不是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 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()
,这将在每次重建屏幕时重新启动异步调用。
您必须在构建方法调用之前发送该数据,这意味着您已经从父窗口小部件传递了该数据。
以下代码可能会对您有所帮助。
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"),
);
}
}