【问题标题】:Why does my initState() not call a function that uses sharedprefs before the widget is built?为什么我的 initState() 在构建小部件之前不调用使用 sharedprefs 的函数?
【发布时间】:2020-06-07 15:34:49
【问题描述】:

在我的颤振应用程序中,我试图创建一个一次性/第一次屏幕,在首次下载应用程序时显示教程。我决定使用 Shared Preferences 来存储数据。但是每当我运行应用程序时,我都会得到 Failed assertion: boolean expression must not be 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


    【解决方案1】:

    这里的实际问题是由于 SharedPreferences API 是异步的,所以 build() 方法在 prefs 加载之前触发,正如预期的那样。

    它正在发生:

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

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

    一个简单的解决方法是在构建的顶部添加它:

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

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

    【讨论】:

    • 刚刚使用了你所说的,它奏效了。非常感谢哟!
    【解决方案2】:

    您可以在下面复制粘贴运行完整代码
    第 1 步:您可以将逻辑 checkFirstTime() 放入 main()
    第2步:要在build中使用updateFirstTime(),可以使用initialRoute来避免 seen值更新导致SingupScreen()突然变成HomeScreen()

    代码sn-p

    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(),
    

    工作演示

    完整代码

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

    【讨论】:

    • 这只是让 main 成为一个异步函数吗?
    • 并非如此。有一些额外的工作,你可以看到完整的代码。
    【解决方案3】:

    在这种情况下,您可以使用 Future builder。这是一个例子

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

    【讨论】:

      猜你喜欢
      • 2021-02-08
      • 2019-07-12
      • 2021-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-24
      • 1970-01-01
      相关资源
      最近更新 更多