首页 文章

Flutter执行顺序:build和initState

提问于
浏览
0

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

每次选择我的设置选项卡都会出现错误(I / flutter(22754):抛出另一个异常:'package:flutter / src / material / switch_list_tile.dart':断言失败:第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)
            )
          )
        )
      ]
    );
  }
}

EDIT

我尝试使用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,
    );


  }
}

1 回答

  • 2

    State对象的生命周期为 createState - > initState - > didChangeDependencies - > build (有关详细信息,请参阅链接的文档) . 所以在你的情况下's not an ordering problem. What'实际发生的是 loadSettings 被调用,但是一旦它命中 awaitFuture 就会返回并且调用者的执行继续(参见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}');
        }
      },
    )
    

    在上面的示例中,您将 _calculation 替换为 loadSettings 并返回 nonewaiting 状态中的相关UI(后者将是 SwitchListTile 的状态) .

相关问题