Flutter:五分钟学会使用动态管理Provider

1.概念

Flutter Provider是一种状态管理库,它允许通过一个中心组件来传递和共享数据。它使用了InheritedWidget,建立了一个数据传递的上下文树来实现数据共享。

2.优点

Provider的核心理念是将数据放置在顶层共享,在应用中的其他部分可以通过调用Provider.of来获取共享数据。这种方式在大型应用中能够更好地组织和管理数据,并且减少了冗余的代码和组件之间的耦合。

3.Provider类型

除了ChangeNotifierProvider和StreamProvider外,Provider还提供了一些其他类型的Provider,以满足不同的数据共享需求。这些包括:

  1. ValueListenableProvider:用于共享支持监听的值列表。它可以监听ValueListenable对象的变化,并在变化时更新依赖于该值的UI部分。
  2. FutureProvider:用于共享异步操作的结果。它可以包装一个Future,并在Future完成时更新UI。
  3. ProxyProvider:用于根据其他Provider的值动态生成新的数据。它可以接收其他Provider的值,并根据这些值生成一个新的数据,并将其共享给依赖的UI部分。
  4. MultiProvider:用于在一个组件中共享多个不同类型的Provider。它可以简化在一个组件中使用多个Provider的情况。

除了这些内置的Provider类型,Provider还提供了一些辅助类和功能,以便更好地组织和管理共享数据。例如:

  • Provider.of:用于从Provider中获取共享数据。
  • Provider.listen:用于监听共享数据的变化。
  • Consumer和Selector:用于在UI中订阅共享数据的变化,并更新相应的UI。

总结起来,Provider是一个功能强大的状态管理库,它提供了多种类型的Provider来满足不同的数据共享需求,并通过一些辅助类和功能来帮助我们更好地组织和管理共享数据。

4.ChangeNotifierProvider案例

假设我们有一个计数器应用程序,需要共享一个计数器值给UI部分。我们可以创建一个名为Counter的ChangeNotifier类来代表计数器的状态。

scala 复制代码
class Counter extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;

    notifyListeners();
  }
}

接下来,我们可以在顶层使用ChangeNotifierProvider封装我们的Counter实例,以便在整个应用程序中共享该实例。在此示例中,我们将在MaterialApp的顶层widget中使用Provider

scala 复制代码
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {

@override
Widget build(BuildContext context) {

return ChangeNotifierProvider(
  create: (context) => Counter(),
  child: MaterialApp(
    title: 'Counter App',
    home: HomePage(),
   ),
  );
 }
}

现在,我们可以在UI部分使用Provider.of来获取共享的Counter实例,并根据需要更新UI。

less 复制代码
`class HomePage extends StatelessWidget {

 @override
 Widget build(BuildContext context) {

final counter = Provider.of<Counter>(context);

return Scaffold(
  appBar: AppBar(
    title: Text('Counter App'),
  ),
  body: Center(
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(
          'Count: ${counter.count}',
          style: TextStyle(fontSize: 24),
        ),
        SizedBox(height: 16),
        RaisedButton(
          onPressed: () {
            counter.increment();
          },
          child: Text('Increment'),
        ),
      ],
    ),
  ),
);
}
}`

final counter = Provider.of(context);当你调用Provider.of方法时,默认情况下它会将listen参数设置为true,这意味着它将尝试在找不到数据模型时监听数据模型的变化,以便在数据发生变化时重新构建相关的部件。

但是,如果你不希望监听数据模型的变化,可以将listen参数设置为false。这样,当你调用Provider.of(context, listen: false)时,它将返回最近的父级数据模型而不会监听其变化。这对于一些只需要获取数据模型一次而不需要实时监听变化的情况非常有用。

Consumer

less 复制代码
`Consumer<Counter>( 
   builder: (context, counter, child) { 
     return Text( 
       'Count: ${counter.count}', 
        style: TextStyle(fontSize: 24), 
               ); }, ),`

我们使用Consumer部件来订阅Counter的变化,替换final counter = Provider.of(context);。在builder函数中,我们可以直接访问Counter实例并根据需要构建UI。这样,在Counter的状态发生变化时,只有与Counter相关的部分会重新构建。

Selector

less 复制代码
class CounterModel {
  int counter;

  CounterModel(this.counter);
}

// Selector的selector函数
int counterSelector(CounterModel model) => model.counter;

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
  return Selector<CounterModel, int>(
    selector: (context, model) => counterSelector(model),
    builder: (context, counter, _) {
      return Column(
        children: [
        Text('Counter: $counter'),
        RaisedButton(
          child: Text('Increment'),
          onPressed: () {
            Provider.of<CounterModel>(context, listen:     false).counter++;
          },
        ),
        RaisedButton(
          child: Text('Decrement'),
          onPressed: () {
            Provider.of<CounterModel>(context, listen: false).counter--;
          },
        ),
      ],
    );
  },
);
}
}

void main() {
  runApp(
    ChangeNotifierProvider(
    create: (context) => CounterModel(0),
    child: MyWidget(),
 ),
  );
   }`

在上面的例子中,我们定义了一个CounterModel类来表示计数器的状态。我们使用Selector组件来监听CounterModel的变化,并按需重建MyWidget。我们通过传递一个selector函数来指定我们所需的计数器值。当counter发生变化时,Selector将调用builder函数来构建新的Widget

Provider.listen

scss 复制代码
`void main() { 
   runApp( 
     ChangeNotifierProvider<CounterModel>( 
       create: (context) => CounterModel(), 
       child: MyWidget(), ), ); 
   // 使用Provider.listen监听共享数据的变化 
   Provider.of<CounterModel>   (context,listen:false).addListener((counterModel) { 
   // 在共享数据发生变化时进行相应的操作 
   print('CounterModel变化了:${counterModel.counter}'
); }); }`

如果我们想要监听共享数据的变化并在数据发生变化时进行相应的操作,可以使用Provider.listen方法。

5.StreamProvider

  1. 实时聊天应用:StreamProvider可以用来管理和共享表示用户聊天数据的流。当聊天数据更新时,StreamProvider会自动通知相关的监听者,并更新UI。
  2. 股票市场应用:StreamProvider可以用来管理和共享代表股票价格的流。当股票价格更新时,StreamProvider会自动通知相关的监听者并更新UI。这可以用于显示实时的股票价格变动。

使用NotifyProvider也可以实现类似的功能,但它的主要用途是在应用程序的不同组件之间共享状态,并通知监听者更新。在股票市场应用中,虽然我们也可以使用NotifyProvider来管理股票价格的状态,并在价格变动时通知监听者更新UI,但使用StreamProvider可能更适合处理实时数据流的情况。

kotlin 复制代码
 ` class Stock {
     String symbol;
     double price;
     Stock({this.symbol, this.price});
}`

我们可以创建一个StockStreamProvider类来管理和共享股票价格的流。它可以提供订阅(subscribe)和取消订阅(unsubscribe)特定股票的方法,并在价格更新时通知监听者:

typescript 复制代码
class StockStreamProvider {
  StreamController<Stock>   _streamController=StreamController<Stock>();

  void subscribe(String symbol) {
  // 订阅特定股票的价格变动
  // 这里可以通过一些方式获取实时的股票价格,比如连接股票交易所的API
  // 假设我们假定的获取价格的方法是getStockPrice(symbol)
  var stockPriceStream = getStockPrice(symbol);

  // 监听价格变动,将变动的股票信息添加到流中
  stockPriceStream.listen((price) {
    var stock = Stock(symbol: symbol, price: price);
    _streamController.add(stock);
  });
}

void unsubscribe(String symbol) {
// 取消订阅特定股票的价格变动
// 这里可以根据需要关闭连接或取消监听
// 假设我们假定的取消订阅的方法是  
cancelStockPriceSubscription(symbol)
  cancelStockPriceSubscription(symbol);
  }

  Stream<Stock> get stockStream =>  _streamController.stream;
 }

最后,我们可以在UI部分使用StreamProvider来订阅股票价格的流,并在价格变动时更新UI。假设我们在Flutter中使用StreamBuilder来构建UI:

kotlin 复制代码
`StreamBuilder<Stock>(
   stream: stockStreamProvider.stockStream,
   builder: (context, snapshot) {
     if (snapshot.hasData) {
         var stock = snapshot.data;
         return Text('${stock.symbol}: \$${stock.price}');
     } else {
         return Text('Loading...');
      }
     },
    );`

当股票价格流发生变化时,StreamProvider会自动通知StreamBuilder,并将最新的股票价格传递给它。然后,StreamBuilder会根据股票价格的变化来更新UI,显示实时的股票价格变动。

MultiProvider

ini 复制代码
// 创建一个MultiProvider
MultiProvider multiProvider = new MultiProvider();

// 创建并添加多个数据源
DataSource salesDataSource = new   SalesDataSource();
multiProvider.addDataSource(salesDataSource);

DataSource inventoryDataSource = new  InventoryDataSource();
multiProvider.addDataSource(inventoryDataSource);

DataSource customerDataSource = new     CustomerDataSource();
multiProvider.addDataSource(customerDataSource);

// 获取多个数据源的数据
List<Data> salesData =   multiProvider.getData(salesDataSource);
List<Data> inventoryData =    multiProvider.getData(inventoryDataSource);
List<Data> customerData = multiProvider.getData(customerDataSource);

// 根据需要处理数据
// 比如可以将销售数据、库存数据和客户数据整合在一起进行分析 和报告生成
List<Data> integratedData = new ArrayList<>();
integratedData.addAll(salesData);
integratedData.addAll(inventoryData);
integratedData.addAll(customerData);

// 对整合后的数据进行处理和分析
// ...

// 获取特定数据源的数据
List<Data> specificData =   multiProvider.getData(salesDataSource);

// 根据需要更新数据源
multiProvider.updateDataSource(salesDataSource,   newData);

// 删除数据源

  multiProvider.removeDataSource(inventoryDataSource);

MultiProvider 可以用于任何需要从多个数据源中提取和整合数据的场景,以实现数据的集成和共享,从而提供更全面和综合的信息供用户进行分析和决策

ProxyProvider

less 复制代码
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<CartProvider>(
          create: (_) => CartProvider(),
        ),
        ProxyProvider<CartProvider, int>(
          update: (_, cartProvider, __) =>   cartProvider.cart.items.length * 10,
      // ProxyProvider通过update回调函数自动计算购物车总价
    ),
  ],
  child: MaterialApp(
    title: 'ProxyProvider Example',
    home: HomePage(),
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
  appBar: AppBar(
    title: Text('ProxyProvider Example'),
  ),
  body: Center(
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(
          'Cart Total: ${context.watch<int>()}', // 从ProxyProvider获取购物车总价
          style: TextStyle(fontSize: 24),
        ),
        ElevatedButton(
          onPressed: () {
            context.read<CartProvider>().addItemToCart(0); // 添加商品到购物车
          },
          child: Text('Add Item to Cart'),
        ),
      ],
    ),
  ),
);
}
}

使用ChangeNotifierProvider创建CartProvider实例,并通过ProxyProvider来获取购物车总价。在HomePage中,我们使用context.watch()来获取购物车总价,并在添加商品到购物车时,调用context.read().addItemToCart()来更新购物车数据,并自动重新计算购物车总价。

小结

使用方式总结差不多了,五分钟过后阅读别人的provider代码可以变得清晰,根据需要选择适合自己的使用风格。

相关推荐
duanyuehuan18 分钟前
Vue 组件定义方式的区别
前端·javascript·vue.js
veminhe22 分钟前
HTML5简介
前端·html·html5
洪洪呀22 分钟前
css上下滚动文字
前端·css
搏博1 小时前
基于Vue.js的图书管理系统前端界面设计
前端·javascript·vue.js·前端框架·数据可视化
掘金安东尼2 小时前
前端周刊第419期(2025年6月16日–6月22日)
前端·javascript·面试
bemyrunningdog2 小时前
AntDesignPro前后端权限按钮系统实现
前端
重阳微噪2 小时前
Data Config Admin - 优雅的管理配置文件
前端
Hilaku2 小时前
20MB 的字体文件太大了,我们把 Icon Font 压成了 10KB
前端·javascript·css
fs哆哆2 小时前
在VB.net中,文本插入的几个自定义函数
服务器·前端·javascript·html·.net
专注VB编程开发20年2 小时前
C# .NET多线程异步记录日声,队列LOG
java·开发语言·前端·数据库·c#