Flutter 中 Provider 的使用指南

目录

前言

1.什么是 Provider

2.如何安装 Provider

3.Provider的常见类型

4.常见类型的用法

1.ChangeNotifierProvider

1.ChangeNotifier

2.使用Provider.of 手动读取状态

3.Consumer

2.使用Provider

3.使用FutureProvider

4.使用StreamProvider


前言

        在 Flutter 开发中,状态管理是一个常见的需求。Provider 是 Flutter 官方推荐的一种简单而强大的状态管理解决方案。本文将介绍 Provider 的基本用法和一些常见场景下的应用。

1.什么是 Provider

        Provider 是一个 Flutter 的插件包,旨在简化状态管理和依赖注入。它使用 InheritedWidget 作为底层实现,通过提供一种订阅与更新的机制,能够让应用在状态变化时自动刷新对应的 UI,极大地提升了开发体验。

2.如何安装 Provider

        在项目的 pubspec.yaml 文件中添加 Provider:

dependencies:

        provider: ^6.1.2

        然后执行以下命令安装依赖:

flutter pub get

3.Provider的常见类型

        在使用 Provider 时,以下几种类型是最常见的:

ChangeNotifierProvider:适用于可变状态。

Provider:用于提供静态数据。

FutureProvider:用于处理异步任务。

StreamProvider:用于处理流式数据。

4.常见类型的用法

1.ChangeNotifierProvider

        ChangeNotifierProvider 是 Flutter 中 Provider 的一种,专为管理需要状态变化通知的情况设计。它使用 ChangeNotifier 类来处理状态管理并通知依赖其状态的组件更新。

1.ChangeNotifier

        ChangeNotifier 是 Flutter SDK 中的一个简单的类。它用于向监听器发送通知。换言之,如果被定义为 ChangeNotifier,你可以订阅它的状态变化。(这和大家所熟悉的观察者模式相类似)。

        在 provider 中,ChangeNotifier 是一种能够封装应用程序状态的方法。对于特别简单的程序,你可以通过一个 ChangeNotifier 来满足全部需求。在相对复杂的应用中,由于会有多个模型,所以可能会有多个 ChangeNotifier。 

        我们以下面的计时器为例,点击++或者--按钮计时器分别执行加一和减一操作。

        图1.使用Provider实现的计时器的例子

        在我们的应用程序中,我们使用新建一个CounterModel类,用来存储count属性,在方法中调用notifyListeners方法,这样它的值发生变化的时候,这个类的监听者都会收到通知。

        代码如下:

class CounterModel with ChangeNotifier{
  int _count = 0;
  int get count=>_count;
  void increment(){
    _count++;
    notifyListeners();
  }
  void decrement(){
    _count--;
    notifyListeners();
  }
}

        然后我们通过ChangeNotifierProvider wiget把我们的app包裹起来之后,子结点就可以访问到我们这个类。

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

2. 使用 Consumer 或 Provider.of 读取状态

1.Provoder.of读取状态

        完成上述的配置之后,我们可以通过实例化一个Provider.of来获取和调用我们定义的类。

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

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Provider用法'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    final counter = Provider.of<CounterModel>(context);
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              '计数器:${counter.count}',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
            const SizedBox(height: 20,),
            ElevatedButton(
                onPressed: () {
                  counter.increment();
                },
                child: const Text("counter++")),
            ElevatedButton(
                onPressed: () {
                  counter.decrement();
                },
                child: const Text("counter--")),
          ],
        ),
      ),
    );
  }
}

2.使用Provider.of 手动读取状态

        Provider.of 可以用于获取 Provider 提供的对象。若不需要 UI 自动更新,可以使用 listen: false 参数,以避免不必要的重建。

floatingActionButton: FloatingActionButton(

  onPressed: () {

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

  },

  child: Icon(Icons.add),

)

3.Consumer

        我们还可以使用Consumer来获取我们要监听的类。

        在使用Consumer类的回时候,我们需要制定访问的模型类型。例如在计时器的例子中,我们就要写上Consumer<CounterModel>。

        Consumer在被调用的时候会用到三个参数。第一个是context,在每个build方法中都能找到这个参数。第二个参数就是ChangeNotifier的实例。他就是我们最开始得到的实例。第三个参数是child,是使用我们定义的数据模型的widget。

        这里需要注意的是,我们尽量把Consumer放在 widget 树尽量低的位置上。

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

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Provider用法'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Consumer<CounterModel>(builder: (context,counter,child){
              return Text(
                '计数器:${counter.count}',
                style: Theme.of(context).textTheme.headlineMedium,
              );
            }),
            const SizedBox(height: 20,),
            Consumer<CounterModel>(builder: (context,counter,child){
              return ElevatedButton(
                  onPressed: () {
                    counter.increment();
                  },
                  child: const Text("counter++"));
            }),
            Consumer<CounterModel>(builder: (context,counter,child){
              return ElevatedButton(
                  onPressed: () {
                    counter.decrement();
                  },
                  child: const Text("counter--"));
            }),
          ],
        ),
      ),
    );
  }
}

2.使用Provider

        Provider 是最基础的 Provider 类型,通常用于提供静态数据或者不可变对象。以下是一个简单的例子,提供一个用户模型

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

// 数据模型
class User {
  final String name;
  final int age;

  User(this.name, this.age);
}

void main() {
  runApp(
    Provider<User>(
      create: (context) => User('John Doe', 25),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: UserScreen(),
    );
  }
}

class UserScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final user = Provider.of<User>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Provider 示例'),
      ),
      body: Center(
        child: Text(
          '用户名: ${user.name}, 年龄: ${user.age}',
          style: TextStyle(fontSize: 24),
        ),
      ),
    );
  }
}

3.使用FutureProvider

        FutureProvider 用于处理异步任务,例如从网络获取数据或从本地数据库加载信息。以下是一个模拟从网络获取用户数据的示例。

        示例代码:

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

// 模拟网络服务
class UserService {
  Future<String> fetchUserName() async {
    await Future.delayed(Duration(seconds: 2)); // 模拟网络延迟
    return "John Doe";
  }
}

void main() {
  runApp(
    FutureProvider<String>(
      create: (context) => UserService().fetchUserName(),
      initialData: '加载中...',
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: UserScreen(),
    );
  }
}

class UserScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final userName = Provider.of<String>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('FutureProvider 示例'),
      ),
      body: Center(
        child: Text(
          '用户名: $userName',
          style: TextStyle(fontSize: 24),
        ),
      ),
    );
  }
}

4.使用StreamProvider

        StreamProvider 用于处理流数据,例如监听实时更新数据流。以下是一个模拟数字计时器的例子。

        示例代码:

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

// 模拟一个计数器流
class CounterService {
  Stream<int> counterStream() async* {
    for (int i = 0; i <= 10; i++) {
      await Future.delayed(Duration(seconds: 1)); // 模拟延迟
      yield i; // 每次发送一个新值
    }
  }
}

void main() {
  runApp(
    StreamProvider<int>(
      create: (context) => CounterService().counterStream(),
      initialData: 0,
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: CounterScreen(),
    );
  }
}

class CounterScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = Provider.of<int>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('StreamProvider 示例'),
      ),
      body: Center(
        child: Text(
          '当前计数值: $counter',
          style: TextStyle(fontSize: 24),
        ),
      ),
    );
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫柱子哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值