在本文中,我们将介绍Flutter中的Provider模式。 Google的工作小组建议使用提供程序模式。 他们还在Flutter的Pragmatic State Management中的 Google I / O 2019上进行了介绍。
其他一些模式(例如BLoC体系结构)在内部使用提供程序模式。 但是提供者模式要容易得多,并且样板代码少得多。
在本文中,我们将使用flutter提供的默认Counter应用,并将其重构为使用Provider Pattern。
注意:如果您想学习BLoC架构,请在此处查看。
创建一个新的flutter项目,并根据需要命名。
首先,我们需要删除所有注释,以便我们可以处理所有内容。
import 'package:flutter/material.dart' ; void main() {
runApp(MyApp()); } MyApp class extends StatelessWidget {
// This widget is the root of your application.
@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),
),
);
} }
现在,在pubspec.yaml文件中添加Provider的依赖项 。 在我写作本书的时候,最新的版本是4.1.2这是你的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
默认的应用程序基本上是一个有状态的小部件,每当您单击FloatingActionButton (调用setState ())时,该小部件都会重新生成。
但是,我们将其转换为无状态小部件!
让我们继续创建我们的提供商。 这将是我们应用程序的唯一事实来源。 这是我们存储状态的地方,在这种情况下,这是当前计数。
因此,创建一个名为Counter的类并添加count变量。
import 'package:flutter/material.dart' ; class Counter {
var _count = 0 ; }
要将其转换为提供程序类,请从material.dart包的ChangeNotifier中进行扩展。 这为我们提供了notifyListeners ()方法。 每当我们更改值时,这将通知所有侦听器。
由于我们在这里所做的只是增加计数器,因此添加一种增加计数器的方法。
import 'package:flutter/material.dart' ; class Counter extends ChangeNotifier {
var _count = 0 ;
void incrementCounter() {
_count += 1 ;
} }
在此方法的最后,我们将调用notifyListeners()。 这将触发整个应用程序对正在监听的小部件的更改。 这就是扑扑中提供者模式的美。 您不必在意手动分派到流。
还创建一个getter以返回计数器值。 我们将使用它来显示最新值。
import 'package:flutter/material.dart' ; class Counter extends ChangeNotifier {
var _count = 0 ;
int get getCounter {
return _count;
}
void incrementCounter() {
_count += 1 ;
notifyListeners();
} }
现在我们有了提供程序的设置,我们可以继续在主窗口小部件中使用它了。
首先,让MyHomePage小部件转换为无状态小部件,而不是有状态小部件。 我们必须删除setState()调用,因为该调用仅在StatefulWidget中可用。
import 'package:flutter/material.dart' ; void main() {
runApp(MyApp()); } MyApp class 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),
),
);
} }
完成此操作后,现在我们可以在flutter中使用提供程序模式来设置和获取计数器值。 单击按钮后,我们需要将计数器值增加1。
因此,在_incrementCounter方法(按下按钮时调用)中添加以下行:
Provider.of<Counter>(context, listen: false ).incrementCounter();
这里发生的事情是,您已要求Flutter进入Widget 树并找到提供Counter的第一个位置(下一节将告诉您如何提供Counter)。 这就是Provider.of()所做的。
泛型( <>括号内的值)告诉flutter要查找的Provider类型是什么。 然后,颤动在小部件树上方向上移动,直到找到提供的值。 如果未在任何地方提供该值,则将引发异常。
最后,一旦有了提供程序,就可以在其上调用任何方法。 在这里,我们调用了递增计数方法。 但是我们还需要一个上下文,因此我们接受上下文作为参数,并更改onPressed方法以传递上下文:
void _incrementCounter(BuildContext context) {
Provider.of<Counter>(context, listen: false ).incrementCounter(); }
注意:我们设置了listen:false,因为我们不需要在这里监听任何值。 我们只是调度要执行的动作。
波动中的提供者模式将寻找提供的最新值。 下图将帮助您更好地理解。
在此图中, 绿色对象A将可用于其下面的其余元素,即B,C,D,E,F。
现在假设我们要向应用程序添加一些功能,并创建另一个提供程序Z。E和F要求Z。 那么添加该功能的最佳位置是什么?
我们可以将其添加到根即。 在A以上。 这会起作用。
但这不是很有效。 Flutter将遍历上面的所有小部件,最后到达根目录。 如果您有非常长的小部件树(肯定会在生产应用程序中使用),那么将所有内容置于根下不是一个好主意。
相反,我们可以看看E和F的共同点是什么? 那就是C。因此,如果将Z放在E和F的上方,它将起作用。
但是,如果我们要添加E和F所需的另一个对象X ,该怎么办? 我们将再次执行相同的操作。 请注意树是如何继续生长的。
有一种更好的方法来管理它。 如果我们在一个级别上提供所有对象怎么办?
太棒了。 这就是我们最终将如何在flutter中实现我们的提供者模式。 我们将使用一种称为MultiProvider的东西,让我们在一个级别上声明多个提供者!
我们将获得MultiProvider来包装MaterialApp小部件:
MyApp class 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" ),
),
);
} }
这样,我们就为小部件树提供了提供程序,并且可以在树中此级别以下的任何位置使用它。
只剩下一件事了。 我们需要更新显示的值。 来做吧。
要更新文本,请在MyHomePage小部件的构建功能中获取提供程序。 我们将使用创建的getter获得最新的价值。
然后只需将此值添加到下面的文本小部件即可。 我们完成了! 这就是您最终的main.dart文件的样子:
import 'package:flutter/material.dart' ; import 'package:provider/provider.dart' ; import 'package:provider_pattern_explained/counter.dart' ; void main() {
runApp(MyApp()); } MyApp class 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),
),
);
} }
注意:在这种情况下,我们没有设置listen:false,因为我们想听计数值的任何更新。
结论
如果您想看看,我也将这个项目托管在GitHub上: https : //github.com/Ayusch/Flutter-Provider-Pattern 。 如果您在下面的评论部分遇到任何问题,请告诉我。
翻译自: https://www.javacodegeeks.com/2020/05/flutter-provider-pattern-explained.html