我正在尝试创建一个首选项菜单,其中我有三个设置(例如“通知”)与共享首选项一起存储。它们应用于 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/
我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass
我遵循了教程http://gettingstartedwithchef.com/,第1章。我的运行list是"run_list":["recipe[apt]","recipe[phpap]"]我的phpapRecipe默认Recipeinclude_recipe"apache2"include_recipe"build-essential"include_recipe"openssl"include_recipe"mysql::client"include_recipe"mysql::server"include_recipe"php"include_recipe"php::modul
我在用Ruby执行简单任务时遇到了一件奇怪的事情。我只想用每个方法迭代字母表,但迭代在执行中先进行:alfawit=("a".."z")puts"That'sanalphabet:\n\n#{alfawit.each{|litera|putslitera}}"这段代码的结果是:(缩写)abc⋮xyzThat'sanalphabet:a..z知道为什么它会这样工作或者我做错了什么吗?提前致谢。 最佳答案 因为您的each调用被插入到在固定字符串之前执行的字符串文字中。此外,each返回一个Enumerable,实际上您甚至打印它。试试
如何检查Ruby文件是否是通过“require”或“load”导入的,而不是简单地从命令行执行的?例如:foo.rb的内容:puts"Hello"bar.rb的内容require'foo'输出:$./foo.rbHello$./bar.rbHello基本上,我想调用bar.rb以不执行puts调用。 最佳答案 将foo.rb改为:if__FILE__==$0puts"Hello"end检查__FILE__-当前ruby文件的名称-与$0-正在运行的脚本的名称。 关于ruby-检查是否
//1.验证返回状态码是否是200pm.test("Statuscodeis200",function(){pm.response.to.have.status(200);});//2.验证返回body内是否含有某个值pm.test("Bodymatchesstring",function(){pm.expect(pm.response.text()).to.include("string_you_want_to_search");});//3.验证某个返回值是否是100pm.test("Yourtestname",function(){varjsonData=pm.response.json
我从Ubuntu服务器上的RVM转移到rbenv。当我使用RVM时,使用bundle没有问题。转移到rbenv后,我在Jenkins的执行shell中收到“找不到命令”错误。我内爆并删除了RVM,并从~/.bashrc'中删除了所有与RVM相关的行。使用后我仍然收到此错误:rvmimploderm~/.rvm-rfrm~/.rvmrcgeminstallbundlerecho'exportPATH="$HOME/.rbenv/bin:$PATH"'>>~/.bashrcecho'eval"$(rbenvinit-)"'>>~/.bashrc.~/.bashrcrbenvversions
我有一个使用SeleniumWebdriver和Nokogiri的Ruby应用程序。我想选择一个类,然后对于那个类对应的每个div,我想根据div的内容执行一个Action。例如,我正在解析以下页面:https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=puppies这是一个搜索结果页面,我正在寻找描述中包含“Adoption”一词的第一个结果。因此机器人应该寻找带有className:"result"的div,对于每个检查它的.descriptiondiv是否包含单词“adoption
我写了一个非常简单的rake任务来尝试找到这个问题的根源。namespace:foodotaskbar::environmentdoputs'RUNNING'endend当在控制台中执行rakefoo:bar时,输出为:RUNNINGRUNNING当我执行任何rake任务时会发生这种情况。有没有人遇到过这样的事情?编辑上面的rake任务就是写在那个.rake文件中的所有内容。这是当前正在使用的Rakefile。requireFile.expand_path('../config/application',__FILE__)OurApp::Application.load_tasks这里
RSpec似乎按顺序匹配方法接收的消息。我不确定如何使以下代码工作:allow(a).toreceive(:f)expect(a).toreceive(:f).with(2)a.f(1)a.f(2)a.f(3)我问的原因是a.f的一些调用是由我的代码的上层控制的,所以我不能对这些方法调用添加期望。 最佳答案 RSpecspy是测试这种情况的一种方式。要监视一个方法,用allowstub,除了方法名称之外没有任何约束,调用该方法,然后expect确切的方法调用。例如:allow(a).toreceive(:f)a.f(2)a.f(1)
如果names为nil,则以下中断。我怎样才能让这个map只有在它不是nil时才执行?self.topics=names.split(",").mapdo|n|Topic.where(name:n.strip).first_or_create!end 最佳答案 其他几个选项:选项1(在其上执行map时检查split的结果):names_list=names.try(:split,",")self.topics=names_list.mapdo|n|Topic.where(name:n.strip).first_or_create!e