dart - Flutter 执行顺序 : build and initState

标签 dart flutter

我正在尝试创建一个首选项菜单,其中我有三个设置(例如“通知”)与共享首选项一起存储。它们应用于 SwitchListTiles。

每次选择我的设置选项卡时都会出现错误 (I/flutter (22754): Another exception was throwed: 'package:flutter/src/material/switch_list_tile.dart': Failed assertion: line 84 pos 15: 'value != null': 不是真的。)只出现一毫秒。之后会显示正确的设置。当我没有向“ProfileState”中初始化的变量添加默认值时,就会发生这种情况。如果它们有默认值,错误就会消失,但在选项卡选择时开关会“闪烁”,从默认值到共享首选项中的正确值。

我的假设是我的 loadSettings 函数在构建方法之后执行。

我该如何解决?感谢您的帮助。

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

class Profile extends StatefulWidget {
  @override
  ProfileState createState() {
    return new ProfileState();
  }
}

class ProfileState extends State<Profile> {
  bool notifications;
  bool trackHistory;
  bool instantOrders;

  @override
  void initState() {
    super.initState();
    loadSettings();
  }

  //load settings
  loadSettings() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    setState(() {
      notifications = (prefs.getBool('notifications') ?? true);
      trackHistory = (prefs.getBool('trackHistory') ?? true);
      instantOrders = (prefs.getBool('instantOrders') ?? false);
    });
  }

  //set settings
  setSettings() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setBool('notifications', notifications);
    prefs.setBool('trackHistory', trackHistory);
    prefs.setBool('instantOrders', instantOrders);
  }

  @override
  Widget build(BuildContext context) {
    return new ListView(
      children: <Widget>[
        new Row(
          children: <Widget>[
            new Container(
              padding: new EdgeInsets.fromLTRB(20.0, 20.0, 0.0, 8.0),
              child: new Text("General", style: new TextStyle(color: Colors.black54)),
            )
          ],
        ),
        new SwitchListTile(
          title: const Text('Receive Notifications'),
          activeColor: Colors.brown,
          value: notifications,
          onChanged: (bool value) {
            setState(() {
              notifications = value;
              setSettings();
            });
          },
          secondary: const Icon(Icons.notifications, color: Colors.brown),
        ),
        new SwitchListTile(
          title: const Text('Track History of Orders'),
          activeColor: Colors.brown,
          value: trackHistory,
          onChanged: (bool value) {
            setState((){
              trackHistory = value;
              setSettings();
            });
          },
          secondary: const Icon(Icons.history, color: Colors.brown,),
        ),
        new SwitchListTile(
          title: const Text('Force instant Orders'),
          activeColor: Colors.brown,
          value: instantOrders,
          onChanged: (bool value) {
            setState((){
              instantOrders = value;
              setSettings();
            });
          },
          secondary: const Icon(Icons.fast_forward, color: Colors.brown),
        ),
        new Divider(
          height: 10.0,
          ),
        new Container(
          padding: EdgeInsets.all(32.0),
          child: new Center(
            child: new Column(
              children: <Widget>[
                new TextField(
                )
              ],
            ),
          ),
        ),
        new Divider(
          height: 10.0,
        ),
        new Row(
          children: <Widget>[
            new Container(
              padding: new EdgeInsets.fromLTRB(20.0, 20.0, 0.0, 20.0),
              child: new Text("License Information", style: new TextStyle(color: Colors.black54)),
            )
          ],
        ),
        new Container(
          padding: new EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 20.0) ,
          child: new RichText(
            text: new TextSpan(
              text: "With confirming our terms and conditions you accept full usage of your personal data. Yikes!",
              style: new TextStyle(color: Colors.black)
            )
          )
        )
      ]
    );
  }
}

编辑

我尝试使用 Darek 解决方案中推荐的 FutureBuilder 来解决它。最初的错误现在已解决,但现在我面临另一个不便。每次轻按开关时,该选项卡都会完全自行构建,这一点非常明显。此外,开关不再平稳运行。在应用程序启动时,您还可以很快看到等待消息,这不是那么漂亮。

这是代码中的新类:

class ProfileState extends State<Profile> {
  bool notifications;
  bool trackHistory;
  bool instantOrders;
  SharedPreferences prefs;

  @override
  void initState() {

    super.initState();
    loadSettings();
  }

  //load settings
  Future<String> loadSettings() async {
    prefs = await SharedPreferences.getInstance();
    notifications= (prefs.getBool('notifications') ?? true);
    trackHistory = (prefs.getBool('trackHistory') ?? true);
    instantOrders= (prefs.getBool('instantOrders') ?? true);
  }

  //set settings
  setSettings() async {
    prefs.setBool('notifications', notifications);
    prefs.setBool('trackHistory', trackHistory);
    prefs.setBool('instantOrders', instantOrders);
  }

  @override
  Widget build(BuildContext context) {
    var profileBuilder = new FutureBuilder(
      future: loadSettings(), // a Future<String> or null
      builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
        switch (snapshot.connectionState) {
          case ConnectionState.none:
            return new Text('No preferences');
          case ConnectionState.waiting:
            return new Text('Loading preferences');
          case ConnectionState.done:
            if (snapshot.hasError)
              return new Text('Error: ');
            else
              return new Column(
                  children: <Widget>[
                    new Row(
                      children: <Widget>[
                        new Container(
                          padding: new EdgeInsets.fromLTRB(20.0, 20.0, 0.0, 8.0),
                          child: new Text("General", style: new TextStyle(color: Colors.black54)),
                        )
                      ],
                    ),
                    new SwitchListTile(
                      title: const Text('Receive Notifications'),
                      activeColor: Colors.brown,
                      value: notifications,
                      onChanged: (bool value) {
                        setState(() {
                          notifications = value;
                          setSettings();
                        });
                      },
                      secondary: const Icon(Icons.notifications, color: Colors.brown),
                    ),
                    new SwitchListTile(
                      title: const Text('Track History of Orders'),
                      activeColor: Colors.brown,
                      value: trackHistory,
                      onChanged: (bool value) {
                        setState((){
                          trackHistory = value;
                          setSettings();
                        });
                      },
                      secondary: const Icon(Icons.history, color: Colors.brown,),
                    ),
                    new SwitchListTile(
                      title: const Text('Force instant Orders'),
                      activeColor: Colors.brown,
                      value: instantOrders,
                      onChanged: (bool value) {
                        setState((){
                          instantOrders = value;
                          setSettings();
                        });
                      },
                      secondary: const Icon(Icons.fast_forward, color: Colors.brown),
                    ),
                    new Divider(
                      height: 10.0,
                    ),
                    new Row(
                      children: <Widget>[
                        new Container(
                          padding: new EdgeInsets.fromLTRB(20.0, 20.0, 0.0, 20.0),
                          child: new Text("License Information", style: new TextStyle(color: Colors.black54)),
                        )
                      ],
                    ),
                    new Container(
                        padding: new EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 20.0) ,
                        child: new RichText(
                            text: new TextSpan(
                                text: "With confirming our terms and conditions you accept full usage of your personal data. Yikes!",
                                style: new TextStyle(color: Colors.black)
                            )
                        )
                    )
                  ]
              );
        }
      },
    );
    return new Scaffold(
      body: profileBuilder,
    );


  }
}

最佳答案

State 的生命周期对象去 createState -> initState -> didChangeDependencies -> build (有关详细信息,请参阅链接文档)。因此,就您而言,这不是订购问题。实际发生的是 loadSettings正在被调用,但是一旦它到达 await一个Future返回并继续执行调用者(请参阅 Dart docs 中的 async/await)。因此,正在构建您的小部件树并使用您的初始空值,然后执行异步部分并初始化您的变量和 setState被称为触发重建,它工作正常。

您需要使用的是 FutureBuilder这样您就可以在 Future 完成时相应地构建 UI:

new FutureBuilder(
  future: _calculation, // a Future<String> or null
  builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
    switch (snapshot.connectionState) {
      case ConnectionState.none: return new Text('Press button to start');
      case ConnectionState.waiting: return new Text('Awaiting result...');
      default:
        if (snapshot.hasError)
          return new Text('Error: ${snapshot.error}');
        else
          return new Text('Result: ${snapshot.data}');
    }
  },
)

在上面的示例中,您将替换 _calculationloadSettings并返回 none 中的相关 UI和 waiting状态(后者将是你的 SwitchListTile s)。

关于dart - Flutter 执行顺序 : build and initState,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51963497/

相关文章:

android - 在对 JSON 响应进行一些数学运算后,如何在 flutter 中创建 PDF 文件?

flutter - 列表中的 indexOf 方法总是返回 -1

regex - 如何在 Flutter 中删除 markdown 语法并仅输出纯文本

flutter - flutter 随机化-如何防止显示先前显示的元素

flutter - 是否可以监听更改,并在更改后从单独的类获取这些新值并在当前类中更新变量?

dart - Flutter 和 Dart - 如何在一行代码中格式化整数日期

firebase-realtime-database - Flutter Firebase 数据库持久性不起作用

javascript - Flutter - 方法 'then' 被调用为 null

firebase - 我可以在 Firestore 搜索中过滤多个字段吗?

dart - ServerSockets 和 Websockets 有什么区别?