
作者:爱吃大芒果
本文所属专栏 Flutter
更多专栏
Ascend C 算子开发教程(进阶) 鸿蒙集成 从0到1自学C++
setState ,而当应用复杂度提升时,Provider 则是替代 setState 的优选方案。本文将从零开始,带你理解状态管理的核心意义,掌握 setState 与 Provider 的基础用法,并理清两者的适用场景。在 Flutter 中,“状态(State)”可以理解为 驱动 UI 变化的数据。比如:
Flutter 是“声明式 UI”框架,UI 是数据的“映射”——当状态发生变化时,UI 会自动根据新状态重新构建。而状态管理,就是规范“如何修改状态”“如何让 UI 感知状态变化”的一套逻辑。
对于简单的单组件状态变化,Flutter 内置的 setState 是最直观的解决方案。它的核心作用是:通知 Flutter 框架“状态已变,请重新构建当前组件的 UI”。
在 StatefulWidget(有状态组件)中,状态被维护在其对应的 State 类中。当我们调用 setState(() { ... }) 时,会执行括号内的状态修改逻辑,之后 Flutter 会自动调用 build 方法,根据新的状态重新绘制 UI。
我们用一个最简单的案例演示 setState 的用法:创建一个页面,点击按钮后,文本从“未点击”变为“已点击”,再次点击则切换回去。
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("setState 演示")),
body: const ClickSwitchWidget(),
),
);
}
}
// 有状态组件:维护“是否点击”的状态
class ClickSwitchWidget extends StatefulWidget {
const ClickSwitchWidget({super.key});
@override
State<ClickSwitchWidget> createState() => _ClickSwitchWidgetState();
}
class _ClickSwitchWidgetState extends State<ClickSwitchWidget> {
// 定义状态:是否已点击(默认未点击)
bool _isClicked = false;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// UI 依赖状态:根据 _isClicked 显示不同文本
Text(
_isClicked ? "已点击" : "未点击",
style: const TextStyle(fontSize: 24),
),
const SizedBox(height: 20),
// 点击按钮修改状态
ElevatedButton(
onPressed: () {
// 关键:用 setState 包裹状态修改逻辑
setState(() {
_isClicked = !_isClicked; // 取反切换状态
});
},
child: const Text("点击切换"),
),
],
),
);
}
}setState 会导致状态“分散”或“冗余”(比如父子组件传递状态需要通过构造函数,跨多层级传递则非常繁琐);
setState 会重新构建整个组件树(当前 StatefulWidget 及其所有子组件),当组件复杂时,会造成不必要的性能消耗;
setState 会让代码变得混乱、难以维护。
当应用复杂度提升(比如需要跨组件共享状态、状态逻辑复杂)时,我们需要更优雅的状态管理方案。Provider 是 Flutter 官方推荐的轻量级状态管理库,它基于“依赖注入”思想,能轻松实现状态的集中管理与跨组件共享。
Provider 的核心是“提供者(Provider)”与“消费者(Consumer)”:
简单理解:Provider 就像一个“状态仓库”,所有需要这个状态的组件(消费者)都可以从仓库中获取状态,当仓库中的状态变化时,所有消费者都会自动更新。
我们改造上面的案例:创建两个组件(TextWidget 和 ButtonWidget),让它们共享同一个“是否点击”的状态——点击 ButtonWidget 中的按钮,TextWidget 中的文本自动切换。
首先在 pubspec.yaml 文件中添加 Provider 依赖(注意查看最新版本):
dependencies: flutter: sdk: flutter provider: ^6.1.1 # 添加 Provider 依赖
添加后执行 flutter pub get 安装依赖。
创建一个类来持有需要共享的状态,这个类需要继承 ChangeNotifier(Provider 提供的“通知者”类,用于在状态变化时通知消费者):
dependencies:
flutter:
sdk: flutter
provider: ^6.1.1 # 添加 Provider 依赖在应用的根组件外层包裹 ChangeNotifierProvider(Provider 的一种,用于提供继承了 ChangeNotifier 的状态模型),让整个应用都能访问到该状态:
import 'package:flutter/foundation.dart';
// 状态模型:持有“是否点击”的状态
class ClickState extends ChangeNotifier {
bool _isClicked = false;
// 提供对外的“只读”访问(避免外部直接修改状态,保证状态修改的可控性)
bool get isClicked => _isClicked;
// 提供修改状态的方法(修改后调用 notifyListeners 通知消费者)
void toggleClick() {
_isClicked = !_isClicked;
notifyListeners(); // 关键:通知所有监听该状态的消费者
}
}分别创建 TextWidget(消费状态,显示文本)和 ButtonWidget(消费状态,修改状态),通过 Consumer 获取状态:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'click_state.dart';
// 显示状态的组件(消费者)
class TextWidget extends StatelessWidget {
const TextWidget({super.key});
@override
Widget build(BuildContext context) {
// 通过 Consumer 获取 ClickState 状态
return Consumer<ClickState>(
builder: (context, clickState, child) {
// builder 方法会在状态变化时重新执行,构建新的 UI
return Text(
clickState.isClicked ? "已点击" : "未点击",
style: const TextStyle(fontSize: 24),
);
},
);
}
}
// 修改状态的组件(消费者)
class ButtonWidget extends StatelessWidget {
const ButtonWidget({super.key});
@override
Widget build(BuildContext context) {
return Consumer<ClickState>(
builder: (context, clickState, child) {
return ElevatedButton(
onPressed: () {
// 调用状态模型中的方法修改状态
clickState.toggleClick();
},
child: const Text("点击切换"),
);
},
);
}
}两者没有绝对的“优劣”,只有“适用场景”的差异,初学者可以根据应用复杂度灵活选择:
状态管理的核心是“规范状态的创建、修改与传递”。对于 Flutter 初学者,setState 是入门的最佳起点,能帮助你快速理解“状态驱动 UI”的核心思想;当应用复杂度提升,需要跨组件共享状态时,Provider 是轻量且高效的选择,它能让代码结构更清晰、性能更优。
setState 的用法,再逐步过渡到 Provider,理解“状态集中管理”的优势。后续还可以学习更复杂的状态管理方案(如 Bloc、GetX 等),但 Provider 作为官方推荐的基础方案,是必备的入门知识点。