Flutter的状态管理之Provider

简介: Flutter的状态管理之Provider

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(context)方法的Widget。这意味着,当我们在应用程序中的任何地方调用Provider.of(context)时,我们都可以获取到同一个MyModel对象的实例。如果我们在MyModel对象中修改了数据,这些变化将自动通知依赖它的Widget进行更新。


注意

需要注意的是,Provider的作用域是有限的。也就是说,当我们在Provider的子树之外的Widget中调用Provider.of(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 above this TestPage Widget


71255e3856630b690f0c4bffff45a1e4.png

原因分析

这个错误通常是由于没有正确的将 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 实例。


修改后的结果

按这个思路修改后,按+按钮,没有报错了,计数能正常刷新了。

9988d0cf0b495e5a2527fefa7fbc6ca1.png

相关文章
|
19天前
Flutter 状态管理新境界:多Provider并行驱动UI
Flutter 状态管理新境界:多Provider并行驱动UI
20 0
|
19天前
|
Dart API
状态管理的艺术:探索Flutter的Provider库
状态管理的艺术:探索Flutter的Provider库
15 0
|
1月前
|
容器
flutter 布局管理【详解】
flutter 布局管理【详解】
21 3
|
1月前
flutter的状态管理学习
flutter的状态管理学习
|
3月前
|
前端开发 开发者 UED
【Flutter前端技术开发专栏】Flutter中的图标、字体与样式管理
【4月更文挑战第30天】本文介绍了在Flutter中管理图标、字体和样式的做法。Flutter提供`Icons`类用于内置矢量图标,支持第三方图标库如FontAwesome。自定义字体可通过添加字体文件至`assets`目录并配置`pubspec.yaml`,然后使用`TextStyle`设置。借助`ThemeData`,开发者能统一管理应用主题样式,局部样式可覆盖全局。通过集中管理样式,提升代码复用性和应用一致性。
110 0
【Flutter前端技术开发专栏】Flutter中的图标、字体与样式管理
|
3月前
|
存储 JavaScript 前端开发
【Flutter 前端技术开发专栏】Flutter 中的状态管理框架(如 Provider、Redux 等)
【4月更文挑战第30天】本文探讨了 Flutter 开发中的状态管理,重点介绍了 Provider 和 Redux 两种框架。Provider 以其简单易用性适合初学者和小项目,而 Redux 则适用于大型复杂应用,保证状态一致性。此外,还提到了 Riverpod 和 BLoC 等其他框架。选择框架时要考虑项目规模、团队技术水平和个人偏好。文章通过购物车应用示例展示了不同框架的使用,并展望了状态管理框架的未来发展。
100 0
【Flutter 前端技术开发专栏】Flutter 中的状态管理框架(如 Provider、Redux 等)
|
3月前
|
JavaScript 前端开发 开发者
【Flutter前端技术开发专栏】Flutter中的Widget与状态管理
【4月更文挑战第30天】本文探讨了Flutter的Widget和状态管理。Widget是Flutter构建UI的基础,分为有状态和无状态两种。状态管理确保UI随应用状态变化更新,影响应用性能和可维护性。文章介绍了`setState`、`Provider`、`Riverpod`、`Bloc`和`Redux`等状态管理方法,并通过计数器应用展示了其实现。选择合适的状态管理策略对高效开发至关重要。
37 0
【Flutter前端技术开发专栏】Flutter中的Widget与状态管理
|
3月前
|
前端开发 开发者 iOS开发
【Flutter前端技术开发专栏】Flutter中的路由管理与页面跳转
【4月更文挑战第30天】本文介绍了Flutter的路由管理与页面跳转,包括基本和命名路由管理。基本路由使用`Navigator`的`push`和`pop`方法,如`MaterialPageRoute`和`CupertinoPageRoute`。命名路由则通过路由表注册名称进行跳转,如`Navigator.pushNamed`。此外,还展示了如何通过构造函数、`arguments`和`PageRouteBuilder`进行路由传值。掌握这些知识能提升Flutter开发效率。
52 0
【Flutter前端技术开发专栏】Flutter中的路由管理与页面跳转
|
3月前
|
存储 UED 开发者
Flutter的状态管理:setState、Provider、Bloc的使用详解
【4月更文挑战第26天】Flutter状态管理详解:涵盖setState基础,Provider的跨组件共享及Bloc的复杂场景处理。了解这三种方法的优缺点,助力优化应用数据一致性与用户体验。当状态管理需求升级,从简单的setState到Provider的便利,再到Bloc的强大功能,开发者可根据项目规模和复杂度选择合适策略。
|
JavaScript 前端开发
Flutter官方推荐的状态管理库-Provider简单入门
Flutter官方推荐的状态管理库-Provider简单入门
205 0