我正在尝试创建一个首选项菜单,其中我有三个设置(例如“通知”)与共享首选项一起存储 . 它们应用于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 回答
State对象的生命周期为
createState
- >initState
- >didChangeDependencies
- >build
(有关详细信息,请参阅链接的文档) . 所以在你的情况下's not an ordering problem. What'实际发生的是loadSettings
被调用,但是一旦它命中await
,Future
就会返回并且调用者的执行继续(参见Dart docs中的async / await) . 因此,正在构建您的小部件树并且正在使用您的初始空值,然后执行异步部分并初始化您的变量并调用setState
来触发重建,这很好 .您需要使用的是FutureBuilder,以便在Future完成后可以相应地构建UI:
在上面的示例中,您将
_calculation
替换为loadSettings
并返回none
和waiting
状态中的相关UI(后者将是SwitchListTile
的状态) .