[英]Flutter: How to use SharedPreferences synchronously?
I am using Shared Preferences in my Flutter app and what I would like to do is store SharedPreferences
as a field on startup and then use it synchronously in the app.我在我的 Flutter 应用程序中使用共享首选项,我想做的是在启动时将
SharedPreferences
存储为一个字段,然后在应用程序中同步使用它。 However I'm not sure if I'm not missing anything.但是我不确定我是否没有遗漏任何东西。 What I want to achieve is instead of:
我想要实现的是:
method1() async {
SharedPreferences sp = await SharedPreferences.getInstance();
return sp.getString('someKey');
}
to到
SharedPreferences sp;
//I would probably pass SharedPreferences in constructor, but the idea is the same
someInitMethod() async {
sp = await SharedPreferences.getInstance();
}
method1() {
return sp.getString('someKey');
}
method2() {
return sp.getString('someKey2');
}
method3() {
return sp.getString('someKey3');
}
In that way I would achieve synchronous access to sharedPrefs.这样我就可以实现对 sharedPrefs 的同步访问。 Is it bad solution?
这是不好的解决方案吗?
EDIT:编辑:
What is worth mentioning is that getInstance
method will only check for instance and if there is any than it returns it, so as I see it, is that async
is only needed to initialize instance.值得一提的是,
getInstance
方法将只检查实例,如果有则返回它,所以在我看来, async
只需要初始化实例。 And both set
and get
methods are sync anyway. set
和get
方法无论如何都是同步的。
static Future<SharedPreferences> getInstance() async {
if (_instance == null) {
final Map<String, Object> fromSystem =
await _kChannel.invokeMethod('getAll');
assert(fromSystem != null);
// Strip the flutter. prefix from the returned preferences.
final Map<String, Object> preferencesMap = <String, Object>{};
for (String key in fromSystem.keys) {
assert(key.startsWith(_prefix));
preferencesMap[key.substring(_prefix.length)] = fromSystem[key];
}
_instance = new SharedPreferences._(preferencesMap);
}
return _instance;
}
I use the same approach as the original poster suggests ie I have a global variable (actually a static field in a class that I use for all such variables) which I initialise to the shared preferences something like this: 我使用的方法与原始发布者建议的方法相同,即,我有一个全局变量(实际上是我用于所有此类变量的类中的静态字段),并初始化为共享首选项,如下所示:
in globals.dart
: 在
globals.dart
:
class App {
static SharedPreferences localStorage;
static Future init() async {
localStorage = await SharedPreferences.getInstance();
}
}
in main.dart
: 在
main.dart
:
void main() {
start();
}
Async.Future start() async {
await App.init();
localStorage.set('userName','Bob');
print('User name is: ${localStorage.get('userName)'}'); //prints 'Bob'
}
The above worked fine but I found that if I tried to use App.localStorage
from another dart file eg settings.dart
it would not work because App.localStorage
was null but I could not understand how it had become null. 上面的工作正常,但我发现如果我尝试从另一个dart文件(例如
settings.dart
使用App.localStorage
,则将无法正常工作,因为App.localStorage
为null,但我无法理解它如何变为null。
Turns out the problem was that the import statement in settings.dart
was import 'package:<packagename>/src/globals.dart';
原来的问题是
settings.dart
中的import语句是import 'package:<packagename>/src/globals.dart';
when it should have been import 'globals.dart;
什么时候应该
import 'globals.dart;
. 。
I made a simple way to using this PrefUtil class:我做了一个简单的方法来使用这个 PrefUtil class:
import 'package:shared_preferences/shared_preferences.dart';
class PrefUtil {
static late final SharedPreferences preferences;
static bool _init = false;
static Future init() async {
if (_init) return;
preferences = await SharedPreferences.getInstance();
_init = true;
return preferences;
}
static setValue(String key, Object value) {
switch (value.runtimeType) {
case String:
preferences.setString(key, value as String);
break;
case bool:
preferences.setBool(key, value as bool);
break;
case int:
preferences.setInt(key, value as int);
break;
default:
}
}
static Object getValue(String key, Object defaultValue) {
switch (defaultValue.runtimeType) {
case String:
return preferences.getString(key) ?? "";
case bool:
return preferences.getBool(key) ?? false;
case int:
return preferences.getInt(key) ?? 0;
default:
return defaultValue;
}
}
}
In main.dart
:在
main.dart
:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
PrefUtil.init();
.....
Save it like:像这样保存:
PrefUtil.setValue("isLogin", true);
Get the value like:获取如下值:
PrefUtil.getValue("isLogin", false) as bool
By this, it will initialize only once and get it where ever you need.通过这种方式,它只会初始化一次并在您需要的任何地方获取它。
You can use FutureBuilder
to render the loading screen while waiting for SharedPreferences
to be intialized for the first time in a singleton-like class. After that, you can access it synchronously inside the children.您可以使用
FutureBuilder
渲染加载屏幕,同时等待SharedPreferences
在单例 class 中首次初始化。之后,您可以在孩子内部同步访问它。
class LocalStorage {
static late final SharedPreferences instance;
static bool _init = false;
static Future init() async {
if (_init) return;
instance = await SharedPreferences.getInstance();
_init = true;
return instance;
}
}
final Future _storageFuture = LocalStorage.init();
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _storageFuture,
builder: (context, snapshot) {
Widget child;
if (snapshot.connectionState == ConnectionState.done) {
child = MyPage();
} else if (snapshot.hasError) {
child = Text('Error: ${snapshot.error}');
} else {
child = Text('Loading...');
}
return Scaffold(
body: Center(child: child),
);
},
);
}
return Text(LocalStorage.instance.getString(kUserToken) ?? 'Empty');
@iBob101 's answer is good, but still, you have to wait before you use the SharedPreferences for the first time. @ iBob101的答案很好,但是仍然必须等待第一次使用SharedPreferences。
The whole point is NOT to await
for your SharedPreferences and be sure that it will always be NOT NULL. 重点不是要
await
您的SharedPreferences,并确保它始终为非NULL。
Since you'll have to wait anyway let's do it in the main() method: 由于您仍然必须等待,因此请在main()方法中进行操作:
class App {
static SharedPreferences localStorage;
static Future init() async {
localStorage = await SharedPreferences.getInstance();
}
}
And the main method: 和主要方法:
void main() async{
await SharedPref.initSharedPref();
runApp(MyApp());
}
the line await SharedPref.initSharedPref();
该行
await SharedPref.initSharedPref();
takes ~100ms to execute. 大约需要100毫秒才能执行。 This is the only drawback as far as I can see.
据我所知,这是唯一的缺点。
But you definitely know that in every place in the app your sharedPreferenes instance in NOT NULL and ready for accessing it: 但是您肯定知道,在应用程序的每个位置,您的sharedPreferenes实例都为NOT NULL并可以访问它:
String s = App.localStorage.getString(PREF_MY_STRING_VALUE);
I think it's worthwhile 我认为值得
The cleanest way is to retrieve SharedPreferences
in main
method and pass it to MyApp
as a dependency: 最干净的方法是在
main
方法中检索SharedPreferences
并将其作为依赖项传递给MyApp
:
void main() async {
// Takes ~50ms to get in iOS Simulator.
final SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
runApp(MyApp(sharedPreferences: sharedPreferences));
}
class MyApp extends StatefulWidget {
final SharedPreferences sharedPreferences;
const MyApp({Key key, this.sharedPreferences})
: assert(sharedPreferences != null),
super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
// You can access shared preferences via widget.sharedPreferences
return ...
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.