flutter 模式_如何在Flutter中使用提供者模式

flutter 模式

In this post we'll take a look at the provider pattern in Flutter. Some other patterns, such as BLoC Architecture, use the provider pattern internally. But the provider pattern is far easier to learn and has much less boilerplate code.

在本文中,我们将介绍Flutter中的提供者模式。 其他一些模式(例如BLoC体系结构)在内部使用提供程序模式。 但是提供者模式要容易得多,并且样板代码少得多。

In this post, we’ll take the default Counter app provided by Flutter and refactor it to use the provider pattern.

在本文中,我们将使用Flutter提供的默认Counter应用,并将其重构为使用提供程序模式。

If you want to know what the Flutter team at Google has to say about the provider pattern, check out this 2019 talk.

如果您想知道Google的Flutter团队对供应商模式有何看法,请查看此2019年演讲

If you want to learn more about BLoC Architecture, check it out here.

如果您想了解有关BLoC体系结构的更多信息,请在此处查看。

入门 (Getting Started)

Create a new Flutter project and name it whatever you want.

创建一个新的Flutter项目,并根据需要命名。

First we need to remove all the comments so that we have a clean slate to work with:

首先,我们需要删除所有注释,以便我们可以处理以下所有问题:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

Now add the dependency for the provider pattern in the pubspec.yaml file. At the time of writing, the latest version is 4.1.2.

现在,在pubspec.yaml文件中添加提供程序模式的依赖pubspec.yaml 。 在撰写本文时,最新版本为4.1.2。

Here’s how your pubspec.yaml file will look now:

这是您的pubspec.yaml文件现在的外观:

name: provider_pattern_explained
description: A new Flutter project.

publish_to: 'none' 

version: 1.0.0+1

environment:
  sdk: ">=2.7.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  provider: ^4.1.2

  cupertino_icons: ^0.1.3

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  uses-material-design: true

The default app is basically a stateful widget which rebuilds every time you click the FloatingActionButton (which calls setState() ).

默认应用程序基本上是一个有状态的小部件,每当您单击FloatingActionButton (调用setState() )时,该小部件都会重新生成。

But now we’re going to convert it into a stateless widget.

但是现在,我们将其转换为无状态窗口小部件。

创建提供者 (Creating the Provider)

Let’s go ahead and create our provider. This will be the single source of truth for our app. This is where we’ll store our state, which in this case is the current count.

让我们继续创建我们的提供商。 这将是我们应用程序的唯一事实来源。 这是我们存储状态的位置,在这种情况下,状态就是当前计数。

Create a class named Counter and add the count variable:

创建一个名为Counter的类并添加count变量:

import 'package:flutter/material.dart';

class Counter {
  var _count = 0;
}

To convert it into a provider class, extend ChangeNotifier from the material.dart package. This provides us with the notifyListeners() method, and will notify all the listeners whenever we change a value.

要将其转换为提供程序类,请从material.dart包中扩展ChangeNotifier 。 这为我们提供了notifyListeners()方法,并在我们更改值时通知所有侦听器。

Now add a method to increment the counter:

现在添加一个增加计数器的方法:

import 'package:flutter/material.dart';

class Counter extends ChangeNotifier {
  var _count = 0;
  void incrementCounter() {
    _count += 1;
  }
}

At the end of this method we’ll call notifyListeners(). This will trigger a change all over the app to whichever widget is listening to it.

在此方法的最后,我们将调用notifyListeners() 。 这将触发整个应用程序对正在监听的小部件的更改。

That's the beauty of the provider pattern in Flutter – you don’t have to care about manually dispatching to streams.

这就是Flutter中提供程序模式的优点–您不必担心手动分派到流。

Finally, create a getter to return the counter value. We’ll use this to display the latest value:

最后,创建一个getter以返回计数器值。 我们将使用它来显示最新值:

import 'package:flutter/material.dart';

class Counter extends ChangeNotifier {
  var _count = 0;
  int get getCounter {
    return _count;
  }

  void incrementCounter() {
    _count += 1;
    notifyListeners();
  }
}

聆听按钮的点击 (Listening to button clicks)

Now that we have the provider set up, we can go ahead and use it in our main widget.

现在我们已经设置了提供程序,我们可以继续在主窗口小部件中使用它。

First, let's convert MyHomePage to a stateless widget instead of a stateful one. We'll have to remove the setState() call since that's only available in a StatefulWidget:

首先,让我们将MyHomePage转换为无状态的小部件,而不是有状态的小部件。 我们必须删除setState()调用,因为该调用仅在StatefulWidget可用:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatelessWidget {
  int _counter = 0;
  final String title;
  MyHomePage({this.title});
  void _incrementCounter() {}
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

With this done, we can now use the provider pattern in Flutter to set and get the counter value. On each button click we need to increment the counter value by 1.

完成此操作后,我们现在可以使用Flutter中的提供者模式来设置和获取计数器值。 在每个按钮上单击,我们需要将计数器值增加1。

So, in the _incrementCounter method (which is called when the button is pressed) add this line:

因此,在_incrementCounter方法(按下按钮时调用)中添加以下行:

Provider.of<Counter>(context, listen: false).incrementCounter();

What’s happening here is that you’ve asked Flutter to go up in the widget tree and find the first place where Counter is provided. (I’ll tell you how to provide it in the next section.) This is what Provider.of() does.

这里发生的事情是您已要求Flutter进入小部件树并找到提供Counter的第一个位置。 (在下一节中,我将告诉您如何提供它。)这是Provider.of()作用。

The generics (values inside <> brackets) tell Flutter what type of provider to look for. Then Flutter goes up through the widget tree until it finds the provided value. If the value isn’t provided anywhere then an exception is thrown.

泛型( <>括号内的值)告诉Flutter寻找哪种类型的提供程序。 然后Flutter遍历小部件树,直到找到提供的值。 如果未在任何地方提供该值,则将引发异常。

Finally, once you’ve got the provider, you can call any method on it. Here we call our incrementCounter method.

最后,一旦有了提供者,就可以在其上调用任何方法。 在这里,我们调用了incrementCounter方法。

But we also need a context, so we accept the context as an argument and alter the onPressed method to pass the context as well:

但是我们还需要一个上下文,因此我们接受上下文作为参数,并更改onPressed方法以传递上下文:

void _incrementCounter(BuildContext context) {
  Provider.of<Counter>(context, listen: false).incrementCounter();
}

Note: We’ve set listen to false because we don’t need to listen to any values here. We’re just dispatching an action to be performed.

注意:我们将监听设置为false,因为在这里我们不需要监听任何值。 我们只是调度要执行的动作。

提供提供者 (Providing the Provider)

The provider pattern in Flutter will look for the latest value provided. The diagram below will help you better understand.

Flutter中的提供者模式将寻找提供的最新值。 下图将帮助您更好地理解。

In this diagram the GREEN object A will be available to the rest of the elements below it, that is B, C, D, E, and F.

在此图中, 绿色对象A将可用于其下方的其余元素,即B,C,D,EF。

Now suppose we want to add some functionality to the app and we create another provider, Z. Z is required by E and F.

现在假设我们要向该应用程序添加一些功能,并创建另一个提供程序Z。E和F要求Z。

So where is the best place to add that?

那么添加它的最佳位置在哪里?

We can add it to the root above A. This would work:

我们可以将其添加到A之上的根中。 这将工作:

But this method is not very efficient.

但是这种方法不是很有效。

Flutter will go through all the widgets above and then finally go to the root. If you have very long widget trees – which you definitely will in a production app – then it’s not a good idea to put everything at the root.

Flutter将遍历上面的所有小部件,最后到达根目录。 如果您有非常长的小部件树(肯定会在生产应用程序中使用),那么将所有内容置于根下并不是一个好主意。

Instead, we can look at the common denominator of E and F. That is C. So if we put Z just above E and F, it would work.

相反,我们可以看一下E和F的公分母。那就是C。因此,如果将Z放在E和F的正上方,它将起作用。

But what if we want to add another object X that's required by E and F? We'll do the same thing. But notice how the tree keeps growing.

但是,如果我们要添加另一个对象X E和F要求? 我们将做同样的事情。 但是请注意树是如何继续生长的。

There’s a better way to manage that. What if we provide all the objects at one level?

有更好的方法来管理它。 如果我们在一个级别上提供所有对象怎么办?

This is perfect, and is how we’ll eventually implement our provider pattern in Flutter. We’ll make use of something called MultiProvider which lets us declare multiple providers at one level.

这是完美的,这也是我们最终将在Flutter中实现我们的提供程序模式的方式。 我们将使用称为MultiProvider的东西 这使我们可以在一个级别上声明多个提供程序。

We'll get MultiProvider to wrap the MaterialApp widget:

我们将获得MultiProvider来包装MaterialApp小部件:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider.value(
          value: Counter(),
        ),
      ],
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: MyHomePage(title: "AndroidVille Provider Pattern"),
      ),
    );
  }
}

With this, we’ve provided the provider to our widget tree and can use it anywhere below this level in the tree.

这样,我们就为小部件树提供了提供程序,并且可以在树中此级别以下的任何位置使用它。

There’s just one more thing left: we need to update the value that's displayed.

剩下的只有一件事:我们需要更新显示的值。

更新文字 (Updating the text)

To update the text, get the provider in the build function of your MyHomePage widget. We’ll use the getter we created to get the latest value.

要更新文本,请在MyHomePage小部件的构建功能中获取提供程序。 我们将使用我们创建的getter获取最新的价值。

Then just add this value to the text widget below.

然后只需将此值添加到下面的文本小部件即可。

And we’re done! This is how your final main.dart file should look:

我们完成了! 这是您最终的main.dart文件的外观:

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider.value(
          value: Counter(),
        ),
      ],
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: MyHomePage(title: "AndroidVille Provider Pattern"),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final String title;
  MyHomePage({this.title});
  void _incrementCounter(BuildContext context) {
    Provider.of<Counter>(context, listen: false).incrementCounter();
  }

  @override
  Widget build(BuildContext context) {
    var counter = Provider.of<Counter>(context).getCounter;
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _incrementCounter(context),
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

Note: We haven’t set listen:false in this case because we want to listen to any updates in the count value.

注意:在这种情况下,我们没有设置listen:false ,因为我们想听计数值的任何更新。

Here's the source code on GitHub if you want to have a look: https://github.com/Ayusch/Flutter-Provider-Pattern.

如果您想看一下,这是GitHub上的源代码: https : //github.com/Ayusch/Flutter-Provider-Pattern

Let me know if you have any issues.

如果您有任何问题,请告诉我。

欢迎使用AndroidVille :) (Welcome to AndroidVille :))

AndroidVille is a community of Mobile Developers where we share knowledge related to Android Development, Flutter Development, React Native Tutorials, Java, Kotlin and much more.

AndroidVille是一个移动开发人员社区,我们在其中共享与Android开发,Flutter开发,React Native教程,Java,Kotlin等相关的知识。

Click on this link to join the AndroidVille SLACK workspace. It’s absolutely free!

单击此链接以加入AndroidVille SLACK工作区。 完全免费!

If you liked this article, feel free to share it on Facebook or LinkedIn. You can follow me on LinkedIn, Twitter, Quora, and Medium where I answer questions related to mobile development, Android, and Flutter.

如果您喜欢这篇文章,请随时在Facebook或LinkedIn上分享。 您可以在LinkedInTwitterQuoraMedium上关注我,在这里我回答与移动开发,Android和Flutter有关的问题。

翻译自: https://www.freecodecamp.org/news/provider-pattern-in-flutter/

flutter 模式

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值