我在Flutter应用程序中实现了共享偏好包,其中有一个列表小部件作为单选按钮,它只保存语言偏好,而不保存复选标记。因此,当我关闭"语言"屏幕并返回时,即使保存在共享首选项中的语言是法语或意大利语,语言复选标记也会变为默认复选标记。
这是我的语言屏幕:
class LanguagesScreen extends StatefulWidget {
const LanguagesScreen({Key? key}) : super(key: key);
@override
State<LanguagesScreen> createState() => _LanguagesScreenState();
}
class Item {
final String prefix;
final String? helper;
const Item({required this.prefix, this.helper});
}
var items = [
Item(prefix: 'English', helper: 'English',), //value: 'English'
Item(prefix: 'Français', helper: 'French'),
Item(prefix: 'Italiano', helper: 'Italian'),
];
class _LanguagesScreenState extends State<LanguagesScreen> {
var _selectedIndex = 0;
final _userPref = UserPreferences();
var _selecLangIndex;
int index = 0;
final List<String> entries = <String>['English', 'French', 'Italian'];*/
//init shared preferences
@override
void initState() {
super .initState();
_populateField();
}
void _populateField() async {
var prefSettings = await _userPref.getPrefSettings();
setState((){
_selecLangIndex = prefSettings.language;
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(...
),
body: CupertinoPageScaffold(
child: Container(
child: SingleChildScrollView(
child: CupertinoFormSection.insetGrouped(
children: [
...List.generate(items.length, (index) => GestureDetector(
onTap: () async {
setState(() => _selectedIndex = index);
if (index == 0){
await context.setLocale(Locale('en','US'));
_selecIndex = Language.English;
}
else if (index == 1){
await context.setLocale(Locale('fr','FR'));
_selecIndex = Language.French;
}
child: buildCupertinoFormRow(
items[index].prefix,
items[index].helper,
selected: _selectedIndex == index,
)
)),
TextButton(onPressed:
_saveSettings,
child: Text('save',
)
buildCupertinoFormRow(String prefix, String? helper, {bool selected = false,}) {
return CupertinoFormRow(
prefix: Text(prefix),
helper: helper != null
? Text(helper, style: Theme.of(context).textTheme.bodySmall,)
:null, child: selected ? const Icon(CupertinoIcons.check_mark,
color: Colors.blue, size: 20,) :Container(),
);
}
void _saveSettings() {
final newSettings = PrefSettings(language:_selecIndex);
_userPref.saveSettings(newSettings);
Navigator.pop(context);
}
}
这是UserPreference:
class UserPreferences {
Future saveSettings(PrefSettings prefSettings) async {
final preferences = await SharedPreferences.getInstance();
await preferences.setInt('language' , prefSettings.language.index );
}
Future<PrefSettings> getPrefSettings() async {
final preferences = await SharedPreferences.getInstance();
final language = Language.values[preferences.getInt('language') ?? 0 ];
return PrefSettings(language: language);
}
}
enum Language { English, French, Italian}
class PrefSettings{
final Language language;
PrefSettings (
{required this.language});
}
我打赌问题在initState
中。您正在调用_populateField
,但它在构建之前没有完成,因为它是一个async
方法,您不能为它调用await
:所以小部件得到构建,加载复选标记的默认位置,并且只有在_populateField
完成之后。。。但是现在要正确显示保存的数据已经太晚了。
根据我的经验,如果我还没有在代码中的其他地方实例化SharedPreferences
对象,我会用这个来加载它:
class _LanguagesScreenState extends State<LanguagesScreen> {
[...]
@override
Widget build(BuildContext context) {
return FutureBuilder(
//you can put any async method here, just be
//sure that you use the type it returns later when using 'snapshot.data as T'
future: await SharedPreferences.getInstance(),
builder: (context, snapshot) {
//error handling
if (!snapshot.hasData || snapshot.connectionState != ConnectionState.done) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text(snapshot.error.toString()));
}
var prefs= snapshot.data as SharedPreferences;
//now you have all the preferences available without need to await for them
return Scaffold((
[...]
);
编辑
我开始写另一条评论,但这里有太多的选择,以至于没有足够的空间。
首先,我发布的代码应该放在_LanguagesScreenState
构建方法中。我建议的FutureBuilder
应该包装任何取决于你必须等待才能完成的未来的东西。我把它放在了Scaffold
的根目录上,但您可以根据需要将它移到小部件的树下,只需记住,需要读取首选项的所有内容都必须在FutureBuilder
中。
其次,关于SharedPreferences.getInstance()
,有两种方法:第一种是将其声明为全局变量,甚至在一切都开始的main
方法中加载它。通过这样做,您将能够从代码中的任何位置引用它,只要每次需要时都小心保存更改即可。第二种是每次需要时都加载它,但最终会大量使用FutureBuilder
。我不知道这两个选项中的任何一个是否比另一个更好:如果SharedPreferences
对象丢失,第一个选项可能会出现问题,而第二个选项需要更多的代码才能工作。