我正在尝试创建一个首选项菜单,其中我有三个设置(例如“通知”)与共享首选项一起存储。它们应用于 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}');
}
},
)
在上面的示例中,您将替换 _calculation
与 loadSettings
并返回 none
中的相关 UI和 waiting
状态(后者将是你的 SwitchListTile
s)。
关于dart - Flutter 执行顺序 : build and initState,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51963497/