Flutter:在 Stream 类中访问 SharedPreferences Provider / ChangeNotifier

Posted

技术标签:

【中文标题】Flutter:在 Stream 类中访问 SharedPreferences Provider / ChangeNotifier【英文标题】:Flutter: Access SharedPreferences Provider / ChangeNotifier in a Stream Class 【发布时间】:2020-11-03 19:30:56 【问题描述】:

我在 *** 中环顾四周,并没有找到解决此问题的方法。 设想: 我有一个带有 ChangeNotifier 类的 Flutter SharedPreferences Provider,它将使用当前登录用户信息进行更新。

简化内容:

class SharedPreferences 
  final String userId;
  final String userName;

  SharedPreferences(
    @required this.userId,
    @required this.userName,
  );

class SharedPreferencesData with ChangeNotifier 
  var _sharedPreferencesData = SharedPreferences(
    userId: 'testUserId',
    userName: 'testUserName',
  );

还有一个包含 DataBaseServices 类的 database.dart 文件,用于从快照中获取 FireStore 流:

class DatabaseService 
  final CollectionReference companiesProfilesCollection =
      Firestore.instance.collection('companiesProfiles');

  List<CompanyProfile> _companiesProfilesFromSnapshot(QuerySnapshot snapshot) 
    return snapshot.documents.map((doc) 
      return CompanyProfile(
        docId: doc.documentID,
        companyName: doc.data['companyName'] ?? '',
        maxLocationsNumber: doc.data['maxLocationsNumber'] ?? 0,
        maxUsersNumber: doc.data['maxUsersNumber'] ?? 0,
      );
    ).toList();
  

  Stream<List<CompanyProfile>> get getCompaniesProfiles 
    return companiesProfilesCollection
        .where('userId', isEqualTo: _userIdFromProvider) 
        // My problem is above -----
        .snapshots()
        .map(_companiesProfilesFromSnapshot);
  

我不想获取整个 Stream 数据,因为它对于其他 Stream 来说可能很大,我只想在 .where('userId', isEqualTo:_userIdFromProvider) 下传递用户 ID。

我无法访问此类中的上下文以从 Provider 获取数据

无法将 userId 发送到 getCompaniesProfiles getter,因为 getter 不带参数

如果我将此 getter 转换为常规方法,我将无法将用户 ID 发送给它,因为它必须在 void main() runApp(MyApp()); / return MultiProvider 下运行(提供者:[ 到那时我无法使用不包含提供者信息的上下文调用 fetch sharedPreferences ...

无法弄清楚如何在此类中接收上下文作为构造函数,当我这样做时,我得到了以下只有静态成员可以在类 DatabaseService 的初始化程序中访问

我还是一个初学者,所以如果你能与我分享处理这个问题的最佳方法,我将不胜感激。 谢谢!

**************** 通过添加以下内容重新编辑:************** 我正在尝试实现相同的场景,这是我的代码: 主文件:

return MultiProvider(
  providers: [
    ChangeNotifierProvider<SpData>(
      create: (context) => SpData(),
    ),
    ProxyProvider<SpData, DS>(
      create: (context) => DS(),
      update: (ctx, spData, previousDS) 
        print('ChangeNotifierProxyProvider RAN');
        previousDS.dbData = spData;
        return previousDS;
      ,
    ),
  ],

SP 文件:

class SP 
  final String companyId;
  SP(
    @required this.companyId,
  );


class SpData with ChangeNotifier 
  var _sPData = SP(
    companyId: '',
  );
  void setCompanyId(String cpID) 
    final newSharedPreferences = SP(
      companyId: cpID,
    );
    _sPData = newSharedPreferences;
    print('_spData companyId:$_sPData.companyId');
    notifyListeners();
  

  String get getCompanyId 
    return _sPData.companyId;
  

DS 文件:

class DS with ChangeNotifier 
  SpData dbData;
  void printCompanyId() 
    var companyId = dbData.getCompanyId;
    print('companyId from DataBase: $companyId');
  

SpData dbData; DS 类内部不会更新。我已经添加了打印来确定什么正在运行,什么没有运行。当我运行我的代码时,main.dart 文件中的打印函数 print('ChangeNotifierProxyProvider RAN');不运行。

我错过了什么?为什么没有触发 ChangeNotifierProxyProvider 来更新 DS 文件中的 dbData?谢谢!

【问题讨论】:

【参考方案1】:

您可以为此使用ProxyProvider

ProxyProvider 是一个基于其他提供者构建价值的提供者。

你说你有一个MultiProvider,所以我猜你在这个MultiProvider 中有SharedPreferencesData 提供者,然后是DatabaseService 提供者。您需要做的是将ProxyProvider 用于DatabaseService 而不是常规提供程序,并将其基于SharedPreferencesData 提供程序。

这是一个例子:

MultiProvider(
  providers: [
    ChangeNotifierProvider<SharedPreferencesData>(
      create: (context) => SharedPreferencesData(),
    ),
    ProxyProvider<SharedPreferencesData, DatabaseService>(
      create: (context) => DatabaseService(),
      update: (context, sharedPreferencesData, databaseService) 
        databaseService.sharedPreferencesData = sharedPreferencesData;
        return databaseService;
      ,
      dispose: (context, databaseService) => databaseService.dispose(),
    ),
  ],
  child: ...

这是上面代码 sn-p 中发生的情况:

    每次SharedPreferencesData 发生变化时,ProxyProvider 都会调用 updateDatabaseService 将其 sharedPreferencesData 变量设置在 update 中。

现在您可以访问DatabaseService 实例中的sharedPreferencesData,您可以轻松地做您想做的事。

【讨论】:

这种方法是完全合法的。但这对我不起作用,可能与我正在做的一些错误的实现有关,原因如下:当 sharedPreferencesData 第一次传递给 DatabaseService 时,它​​是空的。我没有在 sharedPreferencesData 中使用 notifyListeners,因为我收到一个错误:构建错误:无法 notifyListeners => setState => 在构建应用程序时重建。然而,我的应用程序在没有 notifyListeners 的情况下运行良好,因为 sharedPreferencesData 包含只会在应用程序运行和登录时更新的变量。所以我再次陷入困境,如何解决这个问题。 我认为你应该用你的notifyListeners 来解决这个问题。该错误告诉我您在 build 方法中调用了 notifyListeners 是错误的。 嗨 Shahin,我已经使用 WidgetsBinding.instance.addPostFrameCallback 解决了我的 notifyListeners 问题。现在 sharedPreferencesData 应该通知侦听器,因此会更新以通知代理提供者.. 我做了以下事情: ChangeNotifierProvider.value(value: SharedPreferencesData(), ), ProxyProvider( create: (context) => DatabaseService(), update: (context, sharedPreferencesData, databaseService) databaseService. sharedPreferencesData = sharedPreferencesData; return databaseService; , // dispose: (context, databaseService) => databaseService.dispose(), ), 注意,dispose 给了我:方法 'dispose' 没有为类型 'DatabaseService' 定义. 你能告诉我如何在DatabaseService中实现Dispose吗?在 DatabaseService 中: class DatabaseService SharedPreferencesData sharedPreferencesData;最终 sPData = SharedPreferencesData().getSharedPreferencesData(); .... 我在 DatabaseService StreamProvider 中添加了一些打印代码,但是在运行我的应用程序时,我得到以下信息: I/flutter (6795): sPData.companyId: is empty I/flutter (6795): sPData. companyId: is empty 运行 2 次,可能是第一次加载应用程序时,另一次是在 notifylisteners 之后。但 sPData.companyId 在 DatabaseService 中仍然为空

以上是关于Flutter:在 Stream 类中访问 SharedPreferences Provider / ChangeNotifier的主要内容,如果未能解决你的问题,请参考以下文章

如何访问和更改在 FLUTTER/DART 的另一个类中声明的字符串变量的值

Flutter - 获取 SHA-1 证书 - Android Studio 3.2.1

从多个小部件访问 StreamController Stream

Flutter状态管理——单Stream和广播Stream

Flutter状态管理——单Stream和广播Stream

Flutter状态管理——单Stream和广播Stream