如何在 Flutter 中正确重用 Provider

Posted

技术标签:

【中文标题】如何在 Flutter 中正确重用 Provider【英文标题】:How to properly reuse a Provider in Flutter 【发布时间】:2020-06-03 13:22:48 【问题描述】:

所以我在我的小部件树中将这个 ChangeNotifierProvider 放在了很高的位置,因为我看到许多子小部件来监听它的值。

我目前正在做的是将 Provider.of(context) 对象从父小部件传递给它的子小部件,每当我要在子小部件上重用某些值/函数时.例如,每次我为我的子小部件创建一个 Provider.of(context) 对象时,它似乎并没有继承我在父提供者上拥有的更新值,而是这个具有我的默认值null/0/'empty' 就像它只是被创建的一样。这导致我将初始 Provider.of(context) 对象传递给每个将使用 ChangeNotifier 的更新值和函数的子对象。

此设置对我有用,但是,当我的 Widget Tree 开始变得复杂时,我会不断将值传递给每个小部件,并传递给一些甚至根本不使用它的小部件,只是为了让其子级收听主目录提供者。

我认为我现在可能正在做的是提供者架构的反模式,我希望你们可以帮助我以更优化和更有效的方式来做这件事。

非常感谢!

附:文档中有一些内容我还没有完全掌握。

在下面进行编辑以包括示例代码和可视化:

provider_type.dart

class ProviderType extends ChangeNotifier
    String valueA = '';
    String valueB = '';

home.dart

import ..provider_type.dart
...
    Widget build(BuildContext context)
        return ChangeNotifierProvider<ProviderType>(
            create: (context) => ProviderType(),
            child: ScreenColumn();
        );
    
...

screen_column.dart

import ..screen_a.dart
import ..screen_b.dart
class ScreenColumn extends StatelessWidget
    Widget build(BuildContext context)
        var providerType = Provider.of<ProviderType>(context);

        return Column(
            children: <Widget>[
                ScreenA(providerType: providerType),
                ScreenB(providerType: providerType),
            ],
        );
    

screen_a.dart

class ScreenA extends StatelessWidget
    final ProviderType providerType;

    ScreenA(this.providerType);

    Widget build(BuildContext context)
        return Text(
            '$providerType.valueA'
        );
    

screen_b.dart

import ..screen_c.dart
class ScreenB extends StatelessWidget
    final ProviderType providerType;

    ScreenB(this.providerType);

    Widget build(BuildContext context)
        return ScreenC(providerType: providerType);
    

screen_c.dart

class ScreenC extends StatelessWidget
    final ProviderType providerType;

    ScreenB(this.providerType);

    Widget build(BuildContext context)
        return Column(
        children: <Widget>[
            Text(
                '$providerType.valueA'
            )
            Text(
                '$providerType.valueB'
            )
            Text(
                '$providerType.valueC'
            )
        ]
        );
    

可视化

所以我目前正在做的是将对象 providerType 从 ScreenColumn 传递到屏幕 A、B 和 C,这样它们每个都具有相同的“值源”。因为当我尝试制作不同的 Provider.of 对象并使用它们时,当我进行一些计算时,它们不会共享相同的更新值。

我可以做些什么来提高效率还是有更好的方法需要做?

【问题讨论】:

我不太明白你的问题。你能分享一个小代码 sn-p 来帮助可视化问题吗? 天哪,是你!我只是做一些简单的样本 嘿@RémiRousselet,我已经用你问的样本更新了我的问题。希望对您有所帮助 “当我进行一些计算时,它们不共享相同的更新值”是什么意思。 当我没有将父级的相同 Provider.of 对象继承给子级时,该短语适用。例如,在我的父 Provider.of 对象中,我已将“x”的值更新为“1”,当我将其传递给子代时,该值会传递给子代。但是当我创建另一个 Provider.of 对象时,当我尝试再次调用“x”的值时,它会告诉我“x”的值是我的初始值“0”,而不像父 Provider.of 对象。 【参考方案1】:

对于那些可能想知道或正在寻找同一问题的答案的人,请查看下面的示例代码,该示例代码展示了如何在小部件树中的任何位置重用/共享您的 Provider 值和函数,只要它们是在您的父提供者下。

是的,您实际上可以在任何地方创建 Provider.of Objects 你的树没有传递你的初始 Provider.of 对象 已创建。

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class ProviderType extends ChangeNotifier 
  String value = DateTime.now().toString();

  changeValue() 
    value = DateTime.now().toString();
    notifyListeners();
  


void main() => runApp(AppIndex());

class AppIndex extends StatelessWidget 
  const AppIndex(
    Key key,
  ) : super(key: key);

  @override
  Widget build(BuildContext context) 
    return ChangeNotifierProvider<ProviderType>(
      create: (context) => ProviderType(),
      child: MaterialApp(
        home: Home(),
      ),
    );
  


class Home extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    var providerType = Provider.of<ProviderType>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Sample App'),
      ),
      body: ScreenColumn(),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () => providerType.changeValue(),
        label: Text('ChangeValue'),
      ),
    );
  


class ScreenColumn extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return SingleChildScrollView(
        child: Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: <Widget>[
        ScreenA(),
        ScreenB(),
        ScreenC(),
        ScreenC(),
      ],
    ));
  


class ScreenA extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    var providerType = Provider.of<ProviderType>(context);
    return Card(
      color: Colors.red,
      elevation: 8.0,
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Text(providerType.value),
      ),
    );
  


class ScreenB extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    var providerType = Provider.of<ProviderType>(context);
    return Card(
      color: Colors.blue,
      elevation: 8.0,
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Text(providerType.value),
            ScreenC(),
            ScreenC(),
          ],
        ),
      ),
    );
  


class ScreenC extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    // var providerType = Provider.of<ProviderType>(context);

    return Card(
      color: Colors.green,
      elevation: 8.0,
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Text('This is Screen B with no Provider.of Object'),
            ScreenD(),
            ScreenD(),
            ScreenD(),
          ],
        ),
      ),
    );
  


class ScreenD extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    var providerType = Provider.of<ProviderType>(context);
    return Card(
      color: Colors.yellow,
      elevation: 8.0,
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Text(
                'This is Screen D. A Provider.of object was created here without inheriting the Parent\'s Provider.of object.'),
            Text(providerType.value),
          ],
        ),
      ),
    );
  

【讨论】:

嗨 @AverageCoder,这是否也适用于 Flutter 中的 buildMethods,或者将 1 Provider 作为参数传递到此处是否更好? 你好@Nuqo,你的意思是将提供者的实例作为参数传递给每个小部件吗?根据我的理解,这样做是违反直觉的,因为这对于小部件树中的小部件来说会很麻烦——你会对不使用它的小部件进行不必要的传递。提供者的好处是您可以使用“provider.of”或其他帮助器在小部件树中的任何位置调用***提供者对象的实例,这样您就不必将它们传递给每个小部件。就我而言,我有一个用于整个小部件树的主提供程序。 很好的答案!

以上是关于如何在 Flutter 中正确重用 Provider的主要内容,如果未能解决你的问题,请参考以下文章

Flutter - 如何在多个地方重用一些代码

flutter 状态保持之provide

Flutter initState 初始化调用 Provide报错

Flutter initState 初始化调用 Provide报错

如何在 Flutter 中重用 Stack 小部件的子级

Flutter:如何在构造函数中传递值,以便我可以重用我的小部件?