Flutter - 如何在非异步方法中从共享首选项获取值

22

我试图从类的getter方法中获取保存在SharedPreferences中的一些值。但是SharedPreferences.getInstance()返回一个Future。有没有一种方法可以在非async getter方法中获取SharedPreferences对象,例如:

import 'package:shared_preferences/shared_preferences.dart';

class MyClass {
  get someValue {
    return _sharedPreferencesObject.getString("someKey");
  } 
}

Dart 中是否有类似 C# 的 .Result 属性的东西,例如 getSomethingAsync().Result?(https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1.result?view=netframework-4.7.2)


使用 FutureBuilder https://stackoverflow.com/questions/54634418/how-to-properly-wait-until-future-is-complete-in-dart/54634695#54634695 - Mazin Ibrahim
可能是重复的问题:Flutter:如何从其他页面访问存储的SharedPreference值 - Richard Heap
嗨@CopsOnRoad,我知道那些解决方案,但我的问题更多地是关于是否有一种方法可以在非异步方法中等待未来对象返回。我编辑了我的问题,希望它更清晰。 - Dean
@Dean,我不知道你的使用情况,但好的做法是在应用程序启动时获取“SharedPreferences”,并在需要时使用它。你也可以在应用程序的启动画面中实例化它。 - CopsOnRoad
@CopsOnRoad,这就是我目前正在做的事情。对于我的问题,答案是“不可能的”。 - Dean
6个回答

27

您可以使用FutureBuilder()

late final SharedPreferences _prefs;
late final _prefsFuture = SharedPreferences.getInstance().then((v) => _prefs = v);
  
@override
Widget build(BuildContext context) {
  return Scaffold(
    body: FutureBuilder(
      future: _prefsFuture,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return YourFinalWidget(); // `_prefs` is ready for use.
        }

        // `_prefs` is not ready yet, show loading bar till then.
        return CircularProgressIndicator(); 
      },
    ),
  );
}

1
您真是个救命恩人,我看了那个问题的许多答案,但都不适用于我,而您却拯救了我的一天,向您致敬! - Lidor Eliyahu Shelef

20
您可以在initState()中完成,然后调用setState()更新您的build()方法。另一种方法是使用FutureBuilder()
SharedPreferences sharedPrefs;

@override
void initState() {
  super.initState();
  SharedPreferences.getInstance().then((prefs) {
    setState(() => sharedPrefs = prefs);
  });
}

我认为那不会起作用,因为async在initState中不起作用。 - user3697484
@LatheeshVMVilla setState 在一个匿名函数中被调用(而不是在 initState 函数中),所以上面的代码是有效的。只要有一定的道理,我不介意被踩。 - CopsOnRoad
抱歉,也许我错了,只是想理解一下。await SharedPreferences.getInstance(); 这是一个异步调用,因此根据文档,initState 无法处理其中的异步调用。在调用 setState 上没有问题。https://pub.dev/packages/shared_preferences - user3697484
@LatheeshVMVilla,我在initState中使用了await吗? - CopsOnRoad
你的代码中没有await调用,所以我觉得它不正确。SharedPreferences prefs = await SharedPreferences.getInstance(); - user3697484
@LatheeshVMVilla,你不需要使用 await 来等待未来的结果。then 也可以做到这一点,因此我的代码存在于 initState 中。 - CopsOnRoad

8

我建议您使用GetStorage

https://pub.dev/packages/get_storage

您将能够在无需等待的情况下获取值

main.dart

await GetStorage.init();
runApp(MyApp());



storage.dart

  static bool isFirstOpen() {
    final box = GetStorage();
    return box.read("isFirstOpen") ?? true;
  }

  static  setFirstOpen(bool isopen) {
    final box = GetStorage();
    box.write("isFirstOpen", isopen);
  }

不错的替代方案,可以替代共享首选项。 - emmaakachukwu
1
这个方法是可行的,但由于你需要在 main() async 中使用 await GetStorage.init(),所以你可以在同样的位置调用 prefs = await SharedPreferences.getInstance()。这样你就不需要切换到另一个库(GetStorage 只能存储字符串,而 SharedPreferences 可以存储类型数据)。请参考我的回答。 - Luke Hutchison

2
SharedPreferences 中唯一的异步调用是获取初始实例。这可以在整个应用程序中重复使用。main 可以是 async。因此,您只需在 mainawait SharedPreferences 实例即可:
late SharedPreferences prefs;

main() async {
  prefs = await SharedPreferences.getInstance();
  runApp(App());
}

现在,您可以随处使用prefs而不必使用async代码。 SharedPreferences将作为非阻塞的写入缓存,在后台异步运行写操作。

1

你可以像这样做:

  • 创建一个单独的共享首选项服务
  • 在构造函数中传入SharedPreferences
  • 然后,您可以进行不带异步等待的get调用

示例:

class SharedPreferencesService {
  final SharedPreferences _prefs;

  const SharedPreferencesService(this._prefs);

  Future<void> setString(String key, String value) async {
    await _prefs.setString(key, value);
  }

  String? getString(String key) {
    if (containsKey(key)) {
      return _prefs.getString(key);
    } else {
      return null;
    }
  }
  bool containsKey(String key) {
    return _prefs.containsKey(key);
  }
}

-1

您可以在initState()上调用函数并将SharedPreferences保存在变量中,以便您可以在非异步方法中使用,如下所示:

SharedPreferences prefs;

Future<void> loadPrefs() async {
  prefs = await SharedPreferences.getInstance();
}

@override
void initState(){
  super.initState();
  loadPrefs();
}

那行不通--loadPrefs()会立即返回并计划进行异步执行。任何访问prefs的内容可能会或可能不会获取实际实例,这取决于时间(这是竞态条件)。此外,您需要将prefs的类型设置为SharedPreferences?,否则无法编译。 - Luke Hutchison

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接