我正在尝试创建一个“首选项”菜单,其中有三个与“共享首选项”一起存储的设置(例如“通知”)。它们将应用于SwitchListTiles。
每次选择我的设置选项卡时,都会发生错误(I / flutter(22754):引发了另一个异常:'package:flutter / src / material / switch_list_tile.dart':失败的断言:第84行pos 15:'value!= null ':不正确。)仅显示一毫秒。之后,将显示正确的设置。当我不向“ ProfileState”中初始化的变量添加默认值时,就会发生这种情况。如果它们具有默认值,则错误消失,但是在选项卡选择中,开关从“默认值”到“共享首选项”中的正确值“闪烁”。
我的假设是我的loadSettings函数在build方法之后执行。
我该如何解决?任何帮助表示赞赏。
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{ 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: [ new Row( children: [ 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: [ new TextField( ) ], ), ), ), new Divider( height: 10.0, ), new Row( children: [ 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{ bool notifications; bool trackHistory; bool instantOrders; SharedPreferences prefs; @override void initState() { super.initState(); loadSettings(); } //load settings Future 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 or null builder: (BuildContext context, AsyncSnapshot 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: [ new Row( children: [ 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: [ 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, ); } }
Derek Lakin.. 6
State对象的生命周期为createState
-> initState
-> didChangeDependencies
-> build
(有关更多详细信息,请参见链接的文档)。因此,在您的情况下,这不是订购问题。实际发生的是loadSettings
被调用,但是一旦它到达await
a,Future
就会立即返回并继续执行调用方(请参见Dart文档中的async / await )。因此,将构建窗口小部件树并使用最初的空值,然后执行异步部分,并对变量进行初始化,这setState
被称为触发重建,效果很好。
您需要使用FutureBuilder,以便您可以在Future完成后相应地构建UI:
new FutureBuilder( future: _calculation, // a Futureor null builder: (BuildContext context, AsyncSnapshot 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
并返回none
与waiting
状态中的相关UI (后者将是带有SwitchListTile
s的UI )。
State对象的生命周期为createState
-> initState
-> didChangeDependencies
-> build
(有关更多详细信息,请参见链接的文档)。因此,在您的情况下,这不是订购问题。实际发生的是loadSettings
被调用,但是一旦它到达await
a,Future
就会立即返回并继续执行调用方(请参见Dart文档中的async / await )。因此,将构建窗口小部件树并使用最初的空值,然后执行异步部分,并对变量进行初始化,这setState
被称为触发重建,效果很好。
您需要使用FutureBuilder,以便您可以在Future完成后相应地构建UI:
new FutureBuilder( future: _calculation, // a Futureor null builder: (BuildContext context, AsyncSnapshot 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
并返回none
与waiting
状态中的相关UI (后者将是带有SwitchListTile
s的UI )。