Provider原理

19 阅读2分钟

在 Flutter 中,Provider是一个状态管理库,其核心原理基于依赖注入(Dependency Injection)InheritedWidget。它允许数据在 widget 树中向下传递而不必显式地通过每个层级的构造函数传递,从而简化了应用程序的状态管理。

1. 核心概念

InheritedWidget

InheritedWidget是 Flutter 中的一个特殊 widget,它允许子 widget 从 widget 树中向上查找并获取其数据。例如:

dart

class MyInheritedWidget extends InheritedWidget {
  final int counter;

  const MyInheritedWidget({
    Key? key,
    required this.counter,
    required Widget child,
  }) : super(key: key, child: child);

  // 允许子widget获取最近的MyInheritedWidget实例
  static MyInheritedWidget? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
  }

  @override
  bool updateShouldNotify(MyInheritedWidget oldWidget) {
    return oldWidget.counter != counter;
  }
}

子 widget 可以通过MyInheritedWidget.of(context)?.counter获取数据,并且当数据变化时会自动重建。

依赖注入(Dependency Injection)

依赖注入是一种设计模式,它将对象的创建和使用分离。Provider通过Provider<T> widget 将数据(如T类型的对象)注入到 widget 树中,子 widget 可以直接获取该数据而不必通过构造函数传递。

2. Provider 的工作流程

步骤 1:创建数据

首先需要一个数据模型,通常是一个ChangeNotifier(用于可监听的状态):

dart

class CounterModel extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners(); // 通知监听器数据已更改
  }
}

步骤 2:在顶层提供数据

使用ChangeNotifierProvider(或其他Provider子类)将数据提供给 widget 树:

dart

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CounterModel(),
      child: const MyApp(),
    ),
  );
}

步骤 3:在子 widget 中消费数据

有多种方式获取数据:

方式 1:使用Provider.of

dart

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 获取CounterModel的实例
    final counter = Provider.of<CounterModel>(context);
    
    return Text('Count: ${counter.count}');
  }
}
方式 2:使用Consumer(更安全)

dart

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<CounterModel>(
      builder: (context, counter, child) {
        return Text('Count: ${counter.count}');
      },
    );
  }
}
方式 3:使用Selector(性能优化)

只在特定值变化时重建 widget:

dart

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Selector<CounterModel, int>(
      selector: (context, counter) => counter.count,
      builder: (context, count, child) {
        return Text('Count: $count');
      },
    );
  }
}

3. 不同类型的 Provider

  • Provider:提供静态数据(如配置对象)。
  • ChangeNotifierProvider:提供可监听的对象(需继承ChangeNotifier)。
  • FutureProvider:提供异步数据(如网络请求结果)。
  • StreamProvider:提供流数据(如实时更新)。
  • ValueListenableProvider:提供ValueNotifier类型的数据。

4. 为什么选择 Provider?

  • 简化状态管理:避免深层级 widget 传递数据("回调地狱")。
  • 解耦组件:widget 只需关注如何使用数据,而不必关心数据来源。
  • 性能优化:通过SelectorConsumer精确控制重建范围。
  • 测试便利:易于模拟和注入测试数据。

5. 示例:完整的计数器应用

以下是一个使用Provider的简单计数器应用:

dart

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

// 1. 创建数据模型
class CounterModel extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

// 2. 主应用
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CounterModel(),
      child: const MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Provider Demo')),
        body: Center(
          // 3. 消费数据
          child: Consumer<CounterModel>(
            builder: (context, counter, child) {
              return Text('Count: ${counter.count}');
            },
          ),
        ),
        floatingActionButton: FloatingActionButton(
          // 4. 更新数据
          onPressed: () => context.read<CounterModel>().increment(),
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}

总结

Provider通过InheritedWidget实现数据在 widget 树中的高效传递,并结合依赖注入模式简化了状态管理。它是 Flutter 官方推荐的状态管理方案之一,适合中小型应用和复杂应用的局部状态管理。