如何将数据从一个提供者模型传递到另一个提供者模型?

Posted

技术标签:

【中文标题】如何将数据从一个提供者模型传递到另一个提供者模型?【英文标题】:How to pass data from one provider model to another? 【发布时间】:2019-11-09 21:20:42 【问题描述】:

我想使用provider (ChangeNotifierProvider) 和ChangeNotifier 来管理应用程序状态。但是我如何在另一个模型中访问一个模型的状态呢?

用例:在聊天应用中,一种用于存储用户信息的模型。其他模型使用用户信息(例如用户 ID)来调用数据库(Firestore)并获取聊天数据流。

例如:

class Model1 extends ChangeNotifier 
  final List<Item> items = [];

class Model2 extends ChangeNotifier 
//Access items from Model1 here
items;

这可能吗?我不喜欢太大的模型,因为它很难维护。

谢谢!

【问题讨论】:

【参考方案1】:

使用provider,一个模型不能访问另一个模型。

相反,您应该使用ProxyProvider 来组合其他模型。

您的模型如下所示:

class Foo with ChangeNotifier 
  int count = 0;

  void increment() 
    count++;
    notifyListeners();
  


class Bar with ChangeNotifier 
  int _count;
  int get count => _count;
  set count(int value) 
    if (value != count) 
      _count = value;
      notifyListeners();
    
  

然后您可以通过这种方式使用ChangeNotifierProxyProvider(假设您的小部件树中有一个更高的`ChangeNotifierProvider):

ChangeNotifierProxyProvider<Foo, Bar>(
  initialBuilder: (_) => Bar(),
  builder: (_, foo, bar) => bar
    ..count = foo.count, // Don't pass `Foo` directly but `foo.count` instead
)

【讨论】:

感谢您的回复!我试着了解它是如何进行 API 调用的。你是说我们不能从一个模型中访问另一个模型,所以我们使用ChangeNotifierProxyProvider 将状态从Model1 传递到Model2 你好雷米,我有一个问题。如果在您的示例中Bar 需要调用在Foo 中定义的某些业务逻辑函数,会发生什么情况? Bar 可以得到 Foo ChangeNotifier 实例还是应该始终避免?在我的用例中,我有一个带有“父 ChangeNotifier”标签的屏幕,每个标签都有不同的 ChangeNotifier。有时一个选项卡需要触发“父级”的更改。还有其他更好的选择吗?感谢您的出色工作! @Rémi Rousselet 您在调用 count 之前添加“..”的原因是什么 @Rémi Rousselet,我有三个 Provider,其中一个,我想访问另外两个。我试过: ChangeNotifierProxyProvider( create: (BuildContext context) => Einkauf(null), update: (context, provider1, provider2, myNotifier) => myNotifier..update(provider1, provider2))... 但是它只允许用于一个参数。除了这个挑战,你的包裹太棒了! 根据我使用ChangeNotifierProxyProvider 的经验,当事情随着时间的推移变得更加复杂时,它会变成一场噩梦。我不使用这种方法。相反,我按照 Clean Architecture 使用服务。服务对业务逻辑一无所知,它们通过服务定位器在应用程序之间共享,例如 get_it 包。【参考方案2】:

从v4 到ChangeNotifierProxyProvider 提供者可以这样构造:

ChangeNotifierProxyProvider<Foo, Bar>(
  create: (_) => Bar(),
  update: (_, foo, bar) => bar
    ..count = foo.count,
)

请注意,initialBuilder: 已更改为 create: 并且 builder:update:

【讨论】:

嗨,我有一个愚蠢的问题,你为什么用bar..count = foo.count 而不是Bar(foo.count)?如果你这样做,你会重用旧实例,对吗? @user1354934 这对新手来说并不愚蠢!如果我们要使用update: (_, foo, bar) =&gt; Bar(foo.count),我们最终会作为道具传入,并且不需要设置器。因为ProxyProvider,每个提供者都会在依赖项更改时更新;这就是我们想要的。但主要区别在于,存储在Bar 提供程序中的每个值如果没有被最后一个参数 (bar) 传递和使用,则将丢失,即previousState。【参考方案3】:

我试图简单地实现这一点。

提供程序版本 ^4.3.3

这里我举一个简单的例子

main.dart

ChangeNotifierProxyProvider<AuthProvider, ProductsProvider>(
       create: (context) => ProductsProvider(),
       update: (context, auth, previousProducts) => previousProducts
            ..update(auth.token, previousProducts.items == null ? [] : previousProducts.items),
),

authProvider.dart

class AuthProvider with ChangeNotifier 
  String _token;
  // _token is a response token.
  String get token 
    return _token;

productsProvider.dart

产品是一类单品

class ProductsProvider with ChangeNotifier 
  List<Product> _items = [];
  String _authToken;
  void update(authToken, items) 
    _items = items;
    _authToken = authToken;
    notifyListeners();
  

产品.dart

class Product with ChangeNotifier 
  final String id;
  final String title;
  final String description;
  final double price;
  final String imageUrl;
  bool isFavorite;

  Product(
    @required this.id,
    @required this.title,
    @required this.description,
    @required this.price,
    @required this.imageUrl,
    this.isFavorite = false,
  );

【讨论】:

这段代码,我想我们使用的是相同的 Udemy 课程。我正在使用提供程序:^5.0.0。当我单击注销按钮时, ProductsOverviewScreen 再次重新渲染并再次调用 fetchAndSetProducts 函数。然后我得到了错误,因为我们已经清除了 userId 和 token。你知道如何解决这个问题吗?【参考方案4】:

我正在尝试实现相同的场景,这是我的代码: 主文件:

        ChangeNotifierProvider<SharedPreferencesData>(
      create: (context) => SharedPreferencesData(),
    ),
    ChangeNotifierProxyProvider<SharedPreferencesData, DatabaseService>(
      create: (_) => DatabaseService(),
      update: (_, sharedPreferencesData, databaseService) 
        print('ChangeNotifierProxyProvider RAN');
        databaseService..setSpCompanyId = sharedPreferencesData.sPCompanyId;
        return null;
      ,
    ),

SharedPreferencesData 文件:

    class SharedPreferencesData with ChangeNotifier 
    String sPCompanyId = '';
    void setCompanyId(String cpID) 
     sPCompanyId = cpID;
     print('setCompanyId sPCompanyId:$sPCompanyId');
     notifyListeners();
    
  

DataBaseService 文件:

class DatabaseService with ChangeNotifier 
  String sPCompanyId = 'empty';
  String get getSpCompanyId => sPCompanyId;

  set setSpCompanyId(String value) 
    print('value in setter database before if: $value');
    if (value != getSpCompanyId) 
      print('value in setter database: $value');
      sPCompanyId = value;
      notifyListeners();
    
  

DatabaseService 类不更新。我已经添加了打印来确定什么正在运行,什么没有运行。 当我运行我的代码时,Shared_Preferences / setCompanyId 方法中的打印正确运行。 然而 main.dart 文件中的打印功能 print('ChangeNotifierProxyProvider RAN');数据库服务文件中的 AND 打印('setter 数据库中的值:$value');不运行。

我错过了什么?为什么 SharedPreferencesData 中的 notify sPCompanyId setter notifyListeners() 没有通过 ChangeNotifierProxyProvider 更新 DatabaseService String sPCompanyId?谢谢!

【讨论】:

【参考方案5】:

我做了这样的事情。我们正在传递“项目”。

>class Model1 extends ChangeNotifier 
  final List<Item> items = [];


>class Model2 extends ChangeNotifier 
//Access items from Model1 here
 Model1 items;
Model2(this.items);
// Now I can access items in the entire class. 


>ChangeNotifierProxyProvider<Model1, Model2>(
//create the class to pass the value, which is M2. 
  create: (_) => Model2(Provider.of<Model1>(context)),
  update: (_, model1, model2) => model2..items
)



>Model2 model2 = Provider.of<Model2>(context, listen: false);
>>Text(model2.items),

【讨论】:

以上是关于如何将数据从一个提供者模型传递到另一个提供者模型?的主要内容,如果未能解决你的问题,请参考以下文章

如何从模型实例(具有来自 api 的值)传递数据并将其提供给 Text() 小部件

如何将模型属性从一个 Spring MVC 控制器传递到另一个控制器?

将模型从一个屏幕传递到另一个 FLUTTER 的更好方法

如何在 asp.net MVC 中使用 RedirectToAction 将模型从一个 Action 方法传递到另一个?尽管我发送了一个填充模型,但我得到的是空模型

Thymeleaf 形式 - 如何将模型属性的值从一个控制器方法传递到另一个控制器方法

如何将外键从一个视图传递到另一个视图?