Provider简介
Flutter Provider是Flutter中一个非常流行的状态管理库,它可以帮助开发者更加方便地管理应用程序中的状态。Provider提供了一种简单的方式来共享和管理应用程序中的数据,并且可以根据数据的变化来自动更新UI界面。
Provider的核心思想是将数据作为一个全局的单例对象,然后通过InheritedWidget的上下文来共享这个对象。当数据发生变化时,Provider会通知依赖它的UI组件进行更新。这种设计模式非常适合Flutter应用程序中的状态管理,因为它可以避免使用全局变量和回调函数来管理状态。
使用
在Provider中,我们需要定义一个数据模型类,这个类通常包含了我们需要共享的一些数据和状态。例如,一个计数器应用程序的数据模型类可能如下所示:
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
在这个数据模型类中,我们定义了一个名为CounterModel的类,并继承了ChangeNotifier类,这个类是Provider库中提供的一个基类,它实现了通知UI组件更新的功能。我们还定义了一个私有的计数器变量_count和一个公有的计数器变量count,以及一个increment方法用于增加计数器的值,并调用notifyListeners方法来通知UI组件更新。
接下来,在我们的应用程序中,我们需要使用Provider来共享这个CounterModel对象。这可以通过Provider的of方法来实现,例如:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => CounterModel(),
child: MaterialApp(
home: CounterPage(),
),
);
}
}
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = Provider.of<CounterModel>(context);
return Scaffold(
appBar: AppBar(title: Text('Counter Example')),
body: Center(
child: Text('${counter.count}'),
),
floatingActionButton: FloatingActionButton(
onPressed: () => counter.increment(),
child: Icon(Icons.add),
),
);
}
}
在这个例子中,我们在MyApp类中创建了一个ChangeNotifierProvider对象,并将CounterModel对象作为create方法的返回值传递进去。然后,在CounterPage类中,我们使用Provider.of方法获取CounterModel对象,并将其传递给UI组件。当用户点击浮动按钮时,我们调用CounterModel的increment方法来增加计数器的值,并且由于我们已经使用了Provider来共享数据,所以UI组件会自动更新显示计数器的值。
Flutter Provider是一个非常方便和强大的状态管理库,它可以帮助我们更加方便地管理应用程序中的状态,并且可以避免一些常见的状态管理问题。
Provider(create: (_) => MyModel(), child: ...)
是Provider库中的一个构造函数,用于创建一个共享MyModel对象的Provider。这个构造函数有两个参数:
-
create
: 一个回调函数,用于创建MyModel对象。这个回调函数的参数是BuildContext对象,但在这个例子中,我们没有使用这个参数,所以使用了一个下划线(_)来表示它是一个未使用的参数。在这个回调函数中,我们可以创建并返回MyModel对象。 -
child
: 一个Widget,它是Provider的子节点。在这个例子中,我们没有提供具体的Widget,所以使用了省略号(...)表示这是需要替换成其他的Widget的占位符。
当我们使用Provider(create: (_) => MyModel(), child: ...)
构造函数创建一个Provider时,Provider库会自动将MyModel对象共享给所有使用Provider.of<MyModel>(context)方法的Widget。这意味着,当我们在应用程序中的任何地方调用Provider.of<MyModel>(context)时,我们都可以获取到同一个MyModel对象的实例。如果我们在MyModel对象中修改了数据,这些变化将自动通知依赖它的Widget进行更新。
注意
需要注意的是,Provider的作用域是有限的。也就是说,当我们在Provider的子树之外的Widget中调用Provider.of<MyModel>(context)时,它将会抛出一个异常。因此,在使用Provider时,我们需要将它放在需要共享数据的Widget的父节点上,以确保Provider的作用域覆盖所有需要使用共享数据的Widget。
总结来说,Provider(create: (_) => MyModel(), child: ...)
是一个用于创建共享MyModel对象的Provider的构造函数,它可以帮助我们更加方便地管理应用程序中的状态,并且可以根据数据的变化来自动更新UI界面。
踩坑
遇到的错误
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
class TestPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Counter Page'),
),
body: ChangeNotifierProvider(
create: (context) => CounterModel(),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'You have pushed the button this many times:',
),
Consumer<CounterModel>(
builder: (context, counter, child) => Text(
'${counter.count}',
style: Theme.of(context).textTheme.headlineMedium,
),
),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Provider.of<CounterModel>(context, listen: false).increment();
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
以上源码报错 Error: Could not find the correct Provider<CounterModel> above this TestPage Widget
原因分析
这个错误通常是由于没有正确的将 ChangeNotifierProvider 注册在 TestPage 的父级 widget 中引起的。在这种情况下,您需要确保 TestPage 的父级 widget 包括 ChangeNotifierProvider。
解决方法
一种解决方法是将 ChangeNotifierProvider
注册在 MaterialApp
的顶级 widget 中,如下所示:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => CounterModel(),
child: MaterialApp(
title: 'My App',
home: TestPage(),
),
);
}
}
在这个例子中,ChangeNotifierProvider
注册在 MyApp
widget 中,并将 CounterModel
提供给整个应用程序。这样,当 TestPage
被创建时,它将能够访问 CounterModel
实例。
如果您不想在 MyApp
中注册 ChangeNotifierProvider
,则可以将其注册在 TestPage
的父级 widget 中。例如,您可以创建一个新的 widget 并将其包装在 ChangeNotifierProvider
中,然后将该 widget 用作 TestPage
的父级 widget,如下所示:
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => CounterModel(),
child: TestPage(),
);
}
}
class TestPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Counter Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'You have pushed the button this many times:',
),
Consumer<CounterModel>(
builder: (context, counter, child) => Text(
'${counter.count}',
style: Theme.of(context).textTheme.headlineMedium,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Provider.of<CounterModel>(context, listen: false).increment();
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
在这个例子中,我们创建了一个新的 widget MyHomePage
,并将 ChangeNotifierProvider
包装在其中。然后,我们将 TestPage
用作 MyHomePage
的子 widget,并在 TestPage
中访问 CounterModel
。这样,当 TestPage
被创建时,它将能够访问 CounterModel
实例。
修改后的结果
按这个思路修改后,按+按钮,没有报错了,计数能正常刷新了。