为什么我的initState()在构建小部件之前不调用使用sharedprefs的函数?

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

在我的flutter应用程序中,我试图创建一个/第一次屏幕,该屏幕显示首次下载该应用程序时的教程。我决定使用“共享首选项”来存储数据。但是,每当我运行该应用程序时,我都会得到[[失败的断言:布尔表达式不能为null:那是由于我的代码中的变量_ seen未初始化,当我进一步查看时发现我的代码是在用于构建窗口小部件之后初始化变量。反正有什么解决办法?

我的代码

void main() => runApp(Start()); class Start extends StatefulWidget { @override App createState() => new App(); } class App extends State<Start> { bool _seen; @override void initState() { print(1); _checkFirstTime(); super.initState(); } _checkFirstTime() async { print(1.1); SharedPreferences prefs = await SharedPreferences.getInstance(); _seen = (prefs.getBool('seen') ?? false); print(1.2); } _updateFirstTime() async { SharedPreferences prefs = await SharedPreferences.getInstance(); _seen = true; prefs.setBool('seen', true); } Widget build(BuildContext context) { print(2); bool seen = _seen; if (_seen == false) {_updateFirstTime();} print(2.1); return MaterialApp( debugShowCheckedModeBanner: false, home: seen ? HomeScreen() : SignUpScreen(), ); } }

function asynchronous flutter dart sharedpreferences
3个回答
0
投票
您可以在下面复制粘贴运行完整代码步骤1:您可以将逻辑checkFirstTime()放入main()步骤2:要在updateFirstTime()中使用build,可以使用initialRoute避免seen值更新原因SingupScreen()突然更改为HomeScreen()

代码段

Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); SharedPreferences prefs = await SharedPreferences.getInstance(); seen = (prefs.getBool('seen') ?? false); runApp(MyApp()); } ... initialRoute: seen == false || seen == null ? "/sign" : "/", routes: { '/': (context) => HomeScreen( title: "demo", ), "/sign": (context) => SignUpScreen(),

工作演示

enter image description here

完整代码

import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter/services.dart'; bool seen; Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); SharedPreferences prefs = await SharedPreferences.getInstance(); seen = (prefs.getBool('seen') ?? false); runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), initialRoute: seen == false || seen == null ? "/sign" : "/", routes: { '/': (context) => HomeScreen( title: "demo", ), "/sign": (context) => SignUpScreen(), }, ); } } class SignUpScreen extends StatelessWidget { _updateFirstTime() async { SharedPreferences prefs = await SharedPreferences.getInstance(); seen = true; prefs.setBool('seen', true); } @override Widget build(BuildContext context) { if (seen == false) { _updateFirstTime(); } return Text('SignUpScreen'); } } class HomeScreen extends StatefulWidget { HomeScreen({Key key, this.title}) : super(key: key); final String title; @override _HomeScreenState createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), ); } }


0
投票
在这种情况下,您可以使用Future构建器。这是一个例子

import 'package:flutter/material.dart'; void main() => runApp(Start()); class Start extends StatefulWidget { @override App createState() => new App(); } class App extends State<Start> { bool _seen; @override void initState() { print(1); _checkFirstTime(); super.initState(); } Future<bool> _checkFirstTime() async { print(1.1); SharedPreferences prefs = await SharedPreferences.getInstance(); _seen = (prefs.getBool('seen') ?? false); print(1.2); return _seen; } _updateFirstTime() async { SharedPreferences prefs = await SharedPreferences.getInstance(); _seen = true; prefs.setBool('seen', true); } Widget build(BuildContext context) { print(2.1); return MaterialApp( debugShowCheckedModeBanner: false, home: FutureBuilder<bool>( future: _checkFirstTime(), builder: (BuildContext context, AsyncSnapshot<bool> snapshot) { if (snapshot.hasData) { if (_seen == false) { _updateFirstTime(); } return snapshot.data ? HomeScreen() : SignUpScreen(); } else { //you can put anything while retrieving data return Container(); } }), ); } }


0
投票
这里的实际问题是由于SharedPreferences API是异步的,因此,按预期,在加载prefs之前会触发build()方法。

进行中:

1. initState 2. _checkFirstTime (starts) 3. build 4. _checkFirstTime (completes)

有很多方法可以修复它,可以将其初始化为false,可以使用FutureBuilder,可以使用_isInitComplete标志等。

一个简单的解决方法是将其添加到构建的顶部:

if(_seen == null) return Container();

现在,将显示4ms的空视图,或者Prefs完成加载所需的时间。这在功能上与使用FutureBuilder相同。不要忘记在setState()的末尾调用_checkFirstTime来触发刷新。

© www.soinside.com 2019 - 2024. All rights reserved.