每天一个Flutter开发小项目 (9) : Flutter状态管理进阶 - Provider构建你的简易购物车应用


引言

欢迎再次回到 每天一个Flutter开发小项目 系列博客! 在前八篇博客中,我们已经系统学习了 Flutter UI 构建、用户交互、路由导航、数据持久化,以及网络请求等一系列关键技能。 您已经具备了构建功能较为全面的 Flutter 应用的能力。

随着应用功能的日益复杂,页面和组件之间的数据共享和状态同步变得越来越重要。 如果应用状态管理不当,代码将变得难以维护、难以扩展,甚至容易出现各种难以调试的 Bug。 因此,状态管理 (State Management) 是构建大型、复杂 Flutter 应用的核心技术之一。 今天,我们将聚焦 Flutter 应用的 “大脑” —— 状态管理,并使用 Flutter 社区广泛认可的状态管理方案 Provider,构建一个功能完备的 简易购物车应用,让您掌握 Flutter 应用状态管理的精髓。

通过本篇博客,您将深入学习:

  • Flutter 状态管理的核心概念: 理解状态管理的重要性,掌握 Flutter 中常用的状态管理方案,以及 Provider 的核心优势。
  • Provider 状态管理方案的专业应用: 深入学习 Provider 插件,掌握在 Flutter 应用中使用 Provider 进行状态管理的全流程,包括 Provider 的安装、配置、状态Provider 创建、状态消费、状态更新等。
  • Provider 核心组件: 熟练掌握 Provider 提供的核心 Widget,例如 Provider, Consumer, Provider.of, ChangeNotifier, ChangeNotifierProvider 等,理解它们的作用和使用场景。
  • Provider 状态管理最佳实践: 学习如何使用 Provider 构建清晰、可维护的状态管理架构,包括状态隔离、状态复用、状态更新等最佳实践。
  • 简易购物车应用的功能实现: 构建一个功能完善的简易购物车应用,包括商品列表展示、商品加入购物车、购物车商品数量增减、购物车总价计算、购物车页面展示等核心功能。
  • Flutter 应用架构设计能力的专业技能: 从状态管理方案选择到应用架构设计,全面提升 Flutter 应用架构设计和状态管理能力。

项目简介: 简易购物车应用

我们的简易购物车应用将围绕以下核心功能展开:

  • 商品列表展示: 应用主页以列表形式展示商品信息,包括商品名称、图片、价格等。
  • 加入购物车: 在商品列表页,用户可以点击 “加入购物车” 按钮,将商品添加到购物车。
  • 购物车数量展示: 在应用顶部 AppBar 或底部导航栏等位置,实时展示购物车中的商品数量。
  • 购物车页面: 用户可以点击购物车图标,进入购物车页面,查看购物车中的所有商品,包括商品名称、图片、价格、数量、小计等信息。
  • 购物车商品数量增减: 在购物车页面,用户可以增加或减少购物车中商品的数量。
  • 购物车总价计算: 在购物车页面,实时计算购物车中所有商品的总价。
  • 状态管理: 购物车商品数据、购物车商品数量、购物车总价等状态将使用 Provider 进行集中管理,实现跨组件、跨页面的状态共享和同步。

通过构建简易购物车应用,我们将重点实践:

  • Provider 插件集成: 在 Flutter 应用中集成 provider 插件,搭建 Provider 状态管理环境。
  • 状态Provider 创建: 创建 ChangeNotifierProviderProvider 来管理应用状态 (例如,购物车数据)。
  • 状态消费: 使用 ConsumerProvider.of 在 Widget 中消费状态,获取状态数据。
  • 状态更新: 在 Widget 中触发状态更新,并通知所有依赖该状态的 Widget 重新构建。
  • 购物车应用完整功能实现: 从 UI 界面到状态管理逻辑,完整构建一个实用的简易购物车应用。

Flutter 状态管理核心概念与 Provider 方案解析

在开始构建购物车应用之前,我们先来深入理解 Flutter 状态管理的核心概念,并重点解析 Provider 状态管理方案,为后续的实战打牢理论基础。

  • 状态 (State) 的概念: 在 Flutter 应用中,状态 (State) 指的是 Widget 在构建和生命周期中可以变化的数据。 例如,一个计数器应用的计数器值、一个文本输入框的文本内容、一个开关按钮的开关状态,都是状态。 状态是驱动 UI 变化的根本原因,当状态发生改变时,Flutter 会根据新的状态重新构建 UI,从而实现动态的 UI 效果。

  • 状态管理 (State Management) 的重要性: 对于简单的 Flutter 应用,可以使用 setState() 方法在 Widget 内部管理状态。 但是,当应用变得复杂,组件之间的状态共享和同步变得频繁时,使用 setState() 方法进行状态管理会变得非常困难,容易导致代码耦合度高、难以维护、状态更新混乱等问题。 状态管理 (State Management) 的目的是为了更好地组织、管理和共享应用状态,使得状态变化可预测、可追踪,代码结构更清晰、更易维护、更易扩展。

  • Flutter 中常用的状态管理方案: Flutter 社区提供了多种状态管理方案,各有优缺点,适用于不同的应用场景。 常用的状态管理方案包括:

    • setState() (Widget 内部状态管理): Flutter 内置的状态管理方案,适用于简单的、局部的状态管理。 优点: 简单易用,无需引入第三方插件。 缺点: 不适合管理复杂、全局的状态,状态共享和同步困难。
    • Provider (声明式、响应式状态管理): Flutter 社区广泛认可的状态管理方案,基于 InheritedWidgetChangeNotifier 实现,简单易用,功能强大,性能良好,适用于中小型到大型应用的状态管理。 优点: 简单易用,学习曲线平缓,代码简洁,性能良好,易于测试,社区支持活跃。 缺点: 对于非常大型、状态非常复杂的应用,可能需要结合更复杂的状态管理方案 (例如,Bloc/Cubit, Riverpod, Redux 等)。
    • Bloc/Cubit (业务逻辑组件状态管理): 基于 Bloc (Business Logic Component)Cubit 模式实现的状态管理方案,适用于业务逻辑复杂、状态变化较为复杂的应用。 优点: 结构清晰,状态变化可预测,易于测试,适合大型应用。 缺点: 学习曲线较陡峭,代码相对较为冗余。
    • Riverpod (类型安全、依赖注入式状态管理): Provider 的作者 Remi Rousselet 开发的新一代状态管理方案,解决了 Provider 的一些痛点,并引入了类型安全和依赖注入。 优点: 类型安全,编译时检查错误,依赖注入,解耦组件和状态,更灵活,性能更好。 缺点: 学习曲线相对 Provider 稍陡峭,社区生态不如 Provider 完善。
    • Redux (单向数据流状态管理): 基于 Redux 架构的状态管理方案,适用于状态变化复杂、需要状态回溯和时间旅行的应用。 优点: 状态变化可预测,易于调试和测试,适合大型、状态复杂的应用。 缺点: 学习曲线较陡峭,代码较为冗余,配置较为繁琐。
  • Provider 状态管理方案的核心优势: Provider 凭借其 简单易用、功能强大、性能良好、社区活跃 等优势,成为 Flutter 社区最受欢迎的状态管理方案之一。 Provider 的核心优势包括:

    • 简单易用: API 设计简洁明了,学习曲线平缓,易于上手,即使是初学者也能快速掌握。
    • 声明式、响应式: 基于 Flutter 的 Widget 树结构,以声明式的方式管理状态,当状态发生变化时,Provider 会自动通知所有依赖该状态的 Widget 重新构建,实现响应式 UI 更新。
    • 代码简洁: 使用 Provider 可以显著减少状态管理相关的样板代码,使代码更简洁、更易读、更易维护。
    • 性能良好: 基于 InheritedWidget 和 ChangeNotifier 实现,性能良好,能够满足绝大多数 Flutter 应用的性能需求。
    • 易于测试: Provider 的状态和逻辑可以方便地进行单元测试和集成测试。
    • 社区活跃: Flutter 社区对 Provider 的支持非常活跃,拥有大量的文档、示例和教程,遇到问题容易找到解决方案。

Provider 核心组件详解

Provider 提供了多个核心 Widget 和类,用于实现状态管理,我们来重点了解以下几个核心组件:

  • Provider<T>: 最基础的 Provider 组件,用于提供任意类型的值 (T) 给其子树Provider<T> 本身并不具备状态管理能力,只是简单的值传递。 通常与其他 Provider 组件 (例如,ChangeNotifierProvider) 结合使用。 Provider<T> 可以用于提供各种类型的值,例如,常量、变量、对象、函数等。

  • ChangeNotifier: 一个 Flutter SDK 提供的类,用于封装可变状态,并提供状态变更通知机制。 任何继承自 ChangeNotifier 的类,都可以作为 Provider 的状态Provider。 ChangeNotifier 提供了 notifyListeners() 方法,用于通知所有监听器 (Consumer) 状态已发生改变,需要重新构建 UI。

  • ChangeNotifierProvider<T extends ChangeNotifier>: 一个 Provider 组件,用于创建并提供 ChangeNotifier 类型的状态Provider 给其子树ChangeNotifierProvider 负责创建 ChangeNotifier 对象,并在 Widget 树中提供该对象。 ChangeNotifierProvider 通常用于管理可变状态,例如,应用数据、UI 状态等。

  • Consumer<T>: 一个 Provider 组件,用于在其子树中消费由 Provider<T>ChangeNotifierProvider<T> 提供的状态Consumer<T> 会在其 builder 函数中接收状态值 (T),并在状态值发生变化时,自动重新构建其 builder 函数返回的 Widget。 Consumer<T>最常用的状态消费方式,可以精确地控制 Widget 的重新构建范围,提高性能。

  • Provider.of<T>(BuildContext context): 一个静态方法,用于在 Widget 树中向上查找最近的 Provider<T>ChangeNotifierProvider<T> 提供的状态Provider.of<T>(context) 会返回状态值 (T),但不会监听状态变化,状态变化时不会自动重新构建 Widget。 Provider.of<T>(context) 通常用于在 Widget 的 build 函数之外 (例如,在事件处理函数中) 获取状态值。 如果需要在 Widget 的 build 函数中获取状态值并监听状态变化,应该使用 Consumer<T>

实战步骤: 构建简易购物车应用

接下来,我们将一步步使用 Provider 构建我们的简易购物车应用。

步骤 1: 创建新的 Flutter 项目并添加 provider 依赖

首先,创建一个新的 Flutter 项目,命名为 shopping_cart_app

然后在 pubspec.yaml 文件中添加 provider 依赖:

dependencies:flutter:sdk: flutterprovider: ^6.0.0 #  使用最新版本,请查阅 pub.dev 获取最新版本号

运行 flutter pub get 命令获取依赖。

步骤 2: 定义商品数据模型 (Product)

我们需要定义一个 Product 类来表示商品数据,包含商品ID、名称、图片、价格等信息。

创建 lib/models/product.dart 文件,定义 Product 类:

class Product {final int id; //  商品 IDfinal String name; //  商品名称final double price; //  商品价格final String imageUrl; //  商品图片 URLconst Product({ //  使用 const 构造函数required this.id,required this.name,required this.price,required this.imageUrl,});
}

步骤 3: 定义购物车状态Provider (CartProvider)

我们需要创建一个 CartProvider 类,继承自 ChangeNotifier,用于管理购物车状态,包括购物车商品列表、购物车商品数量、购物车总价等状态,并提供状态更新和通知机制。

创建 lib/providers/cart_provider.dart 文件,定义 CartProvider 类:

import 'package:flutter/material.dart';import '../models/product.dart'; //  导入 Product 类class CartProvider extends ChangeNotifier {final List<Product> _products = []; //  购物车商品列表 (私有)List<Product> get products => _products; //  购物车商品列表 Getter (只读)double get totalPrice => _products.fold(0, (sum, product) => sum + product.price); //  购物车总价 Getter (只读)int get itemCount => _products.length; //  购物车商品数量 Getter (只读)void addProduct(Product product) { //  添加商品到购物车_products.add(product); //  将商品添加到购物车商品列表notifyListeners(); //  通知所有监听器 (Consumer) 状态已发生改变,需要重新构建 UI}void removeProduct(Product product) { //  从购物车移除商品_products.remove(product); //  从购物车商品列表移除商品notifyListeners(); //  通知所有监听器 (Consumer) 状态已发生改变,需要重新构建 UI}void clearCart() { //  清空购物车_products.clear(); //  清空购物车商品列表notifyListeners(); //  通知所有监听器 (Consumer) 状态已发生改变,需要重新构建 UI}
}

代码解释:

  • CartProvider: 继承自 ChangeNotifier,作为购物车状态Provider。
  • _products: List<Product> 类型,私有变量,用于存储购物车商品列表。 使用 _ 开头的变量名表示私有变量,只能在当前类内部访问。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/27271.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Notepad++下载地址【亲测好用】

网上下载链接五花八门&#xff0c;最终找到了一个官方下载链接。 本文章就用来记录Notepad下载网址 https://github.com/notepad-plus-plus/notepad-plus-plus/tags

LeetCode 873. Length of Longest Fibonacci Subsequence(2025/2/27每日一题)

昨天工作耽搁了&#xff0c;没来得及打卡每日一题&#xff0c;今日补上&#xff1a; 标题&#xff1a;Length of Longest Fibonacci Subsequence 题目&#xff1a; 例子&#xff1a; Example 1: Input: arr [1,2,3,4,5,6,7,8] Output: 5 Explanation: The longest subsequ…

【uniapp】在UniApp中实现持久化存储:安卓--生成写入数据为jsontxt

在移动应用开发中&#xff0c;数据存储是一个至关重要的环节。对于使用UniApp开发的Android应用来说&#xff0c;缓存&#xff08;Cache&#xff09;是一种常见的数据存储方式&#xff0c;它能够提高应用的性能和用户体验。然而&#xff0c;缓存数据在用户清除缓存或清除应用数…

【小羊肖恩】小羊杯 Round 2 C+K

题目链接&#xff1a;https://ac.nowcoder.com/acm/contest/100672#question C.是毛毛虫吗&#xff1f; 思路&#xff1a; 其实很简单&#xff0c;假设我们要满足题目所给条件&#xff0c;那么这个毛毛虫最坏情况下肯定是一条如下图所示的无向图 右端省略号为对称图形 &…

【定昌Linux系统】部署了java程序,设置开启启动

将代码上传到相应的目录&#xff0c;并且配置了一个.sh的启动脚本文件 文件内容&#xff1a; #!/bin/bash# 指定JAR文件的路径&#xff08;如果JAR文件在当前目录&#xff0c;可以直接使用文件名&#xff09; JAR_FILE"/usr/local/java/xs_luruan_client/lib/xs_luruan_…

17、什么是智能指针,C++有哪几种智能指针【高频】

智能指针其实不是指针&#xff0c;而是一个&#xff08;模板&#xff09;类&#xff0c;用来存储指向某块资源的指针&#xff0c;并自动释放这块资源&#xff0c;从而解决内存泄漏问题。主要有以下四种&#xff1a; auto_ptr 它的思想就是当当一个指针对象赋值给另一个指针对…

基于SpringBoot和PostGIS的省域“地理难抵点(最纵深处)”检索及可视化实践

目录 前言 1、研究背景 2、研究意义 一、研究目标 1、“地理难抵点”的概念 二、“难抵点”空间检索实现 1、数据获取与处理 2、计算流程 3、难抵点计算 4、WebGIS可视化 三、成果展示 1、华东地区 2、华南地区 3、华中地区 4、华北地区 5、西北地区 6、西南地…

Git学习

Git命令 1、管理文件夹&#xff0c;创建版本仓库 创建文件夹 mkdir repos初始化命令 git init2、查看工作区的文件状态 注&#xff1a;新增和修改过后的文件都是红色 git status3、提交缓存区 注&#xff1a;加入缓存区后的文件变成绿色 git add . git add 文件名4、生…

数据库拓展操作

目录 一、截断表&#xff1a; 操作目的&#xff1a; 操作内容&#xff1a; 性能影响&#xff1a; 基本语法&#xff1a; 例子&#xff1a; 二、插入查询结果&#xff1a; 基本语法&#xff1a; 例子&#xff1a; 三、聚合函数&#xff1a; 常用函数&#xff1a; 基…

【Java分布式】Nacos注册中心

Nacos注册中心 SpringCloudAlibaba 也推出了一个名为 Nacos 的注册中心&#xff0c;相比 Eureka 功能更加丰富&#xff0c;在国内受欢迎程度较高。 官网&#xff1a;https://nacos.io/zh-cn/ 集群 Nacos就将同一机房内的实例划分为一个集群&#xff0c;一个服务可以包含多个集…

鸿蒙兼容Mapbox地图应用测试

鸿蒙Next已经发布一段时间了&#xff0c;很多之前的移动端地图应用&#xff0c;纷纷都要求适配鸿蒙Next。作为开发者都清楚&#xff0c;所谓的适配其实都是重新开发&#xff0c;鸿蒙的开发语言和纯前端的Javascript不同&#xff0c;也可以Android原始开发的语言不同。鸿蒙自带的…

老牌工具,16年依然抗打!

在电脑还没普及、操作系统为Windows XP/7的时代&#xff0c;多媒体文件的转换操作常常面临格式不兼容的问题。这时一款名为格式工厂的软件成为了众多用户的首选工具。格式工厂以其简洁易用的界面和强大的功能&#xff0c;轻松地进行各种文件格式的转换。成为很多修小伙伴的喜爱…

前缀和算法 算法4

算法题中帮助复习的知识 vector<int > dp( n ,k); n为数组大小 ,k为初始化 哈希表unordered_map<int ,int > hash; hash.find(k)返回值是迭代器 ,找到k返回其迭代器 没找到返回hash.end() hash.count(k)返回值是数字 ,找到k返回1 ,没找到返回0. C和java中 负数…

如何使用Spring Boot框架整合Redis:超详细案例教程

目录 # 为什么选择Spring Boot与Redis整合&#xff1f; 1. 更新 pom.xml 2. 配置application.yml 3. 创建 Redis 配置类 4. Redis 操作类 5. 创建控制器 6. 启动应用程序 7. 测试 # 为什么选择Spring Boot与Redis整合&#xff1f; 将Spring Boot与Redis整合可以充分利…

DeepSeek开源周,第五弹再次来袭,3FS

Fire-Flyer 文件系统&#xff08;3FS&#xff09;总结&#xff1a; 一、核心特点 3FS 是一个专为 AI 训练和推理工作负载设计的高性能分布式文件系统&#xff0c;利用现代 SSD 和 RDMA 网络&#xff0c;提供共享存储层&#xff0c;简化分布式应用开发。其主要特点包括&#xf…

爬虫系列之【数据解析之JSON】《三》

目录 前置知识 一、 json.loads()&#xff1a;JSON 转 Python 数据 二、json.dump()&#xff1a;python数据 转 json 并写入文件 三、json.loads() &#xff1a;json 转 python数据 四、json.load() &#xff1a;json 转 python数据&#xff08;在文件操作中更方便&#xf…

FastExcel vs EasyExcel vs Apache POI:三者的全面对比分析

一、核心定位与历史沿革 Apache POI&#xff08;1990s-&#xff09; 作为Java生态中最古老的Excel处理库&#xff0c;提供对.xls/.xlsx文件的全功能支持。其核心价值在于对Excel规范的完整实现&#xff0c;包括单元格样式、公式计算、图表操作等深度功能。但存在内存消耗大&…

创建一个MCP服务器,并在Cline中使用,增强自定义功能。

MCP介绍 MCP 是一个开放协议&#xff0c;它标准化了应用程序如何向LLMs提供上下文。可以将 MCP 视为 AI 应用程序的 USB-C 端口。正如 USB-C 提供了一种标准化的方法来将您的设备连接到各种外围设备和配件一样&#xff0c;MCP 提供了一种标准化的方法来将 AI 模型连接到不同的…

【计算机网络入门】初学计算机网络(七)

目录 1. 滑动窗口机制 2. 停止等待协议&#xff08;S-W&#xff09; 2.1 滑动窗口机制 2.2 确认机制 2.3 重传机制 2.4 为什么要给帧编号 3. 后退N帧协议&#xff08;GBN&#xff09; 3.1 滑动窗口机制 3.2 确认机制 3.3 重传机制 4. 选择重传协议&#xff08;SR&a…

[Windows] 免费电脑控制手机软件 极限投屏_正式版_3.0.1 (QtScrcpy作者开发)

[Windows] 极限投屏_正式版 链接&#xff1a;https://pan.xunlei.com/s/VOKJf8Z1u5z-cHcTsRpSd89tA1?pwdu5ub# 新增功能(Future)&#xff1a; 支持安卓14(Supports Android 14)提高投屏成功率(Improve the success rate of mirror)加快投屏速度(Accelerate screen mirrorin…