Provider 是 Flutter 官方推荐的状态管理解决方案,它基于 InheritedWidget 实现,通过依赖注入的方式高效管理应用状态,避免深层嵌套传值问题。以下从原理、核心组件到实践代码全面解析:
context.dependOnInheritedWidgetOfExactType()
获取最近的父级 InheritedWidget。Provider 设计包含三层:
ChangeNotifier
、ValueNotifier
)。ChangeNotifierProvider
、Provider
)。Consumer
、Selector
)。ChangeNotifier
,数据变化时通知依赖组件。ValueNotifier
,轻量级状态管理。步骤 1:定义状态类
import 'package:flutter/material.dart';
// 继承 ChangeNotifier
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // 通知监听者状态变化
}
}
步骤 2:在顶层提供状态
void main() {
runApp(
// 使用 ChangeNotifierProvider 提供状态
ChangeNotifierProvider(
create: (context) => CounterModel(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider Demo')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 消费状态
Consumer(
builder: (context, counter, child) {
return Text('Count: ${counter.count}');
},
),
ElevatedButton(
onPressed: () {
// 获取状态并调用方法
context.read().increment();
},
child: Text('Increment'),
),
],
),
),
),
);
}
}
步骤 1:定义多个状态类
// 用户状态
class UserModel extends ChangeNotifier {
String _name = 'Guest';
String get name => _name;
void login(String username) {
_name = username;
notifyListeners();
}
}
// 设置状态
class SettingsModel extends ChangeNotifier {
bool _darkMode = false;
bool get darkMode => _darkMode;
void toggleDarkMode() {
_darkMode = !_darkMode;
notifyListeners();
}
}
步骤 2:多层嵌套提供状态
void main() {
runApp(
// 多层 Provider
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => UserModel()),
ChangeNotifierProvider(create: (context) => SettingsModel()),
],
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 获取设置状态
final settings = context.watch();
return MaterialApp(
theme: settings.darkMode ? ThemeData.dark() : ThemeData.light(),
home: Scaffold(
appBar: AppBar(title: Text('Multi-Provider Demo')),
body: Column(
children: [
// 用户信息组件
UserInfoWidget(),
// 设置组件
SettingsWidget(),
],
),
),
);
}
}
// 用户信息组件(消费 UserModel)
class UserInfoWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer(
builder: (context, user, child) {
return Text('Welcome, ${user.name}');
},
);
}
}
// 设置组件(消费 SettingsModel)
class SettingsWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer(
builder: (context, settings, child) {
return SwitchListTile(
title: Text('Dark Mode'),
value: settings.darkMode,
onChanged: (value) {
settings.toggleDarkMode();
},
);
},
);
}
}
场景:仅当特定状态字段变化时重建组件。
class OptimizedCounterWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 仅监听 count 是否变化
return Selector(
selector: (context, counter) => counter.count,
builder: (context, count, child) {
return Text('Optimized Count: $count');
},
);
}
}
步骤 1:定义异步状态类
class AuthModel extends ChangeNotifier {
bool _isLoading = false;
bool _isAuthenticated = false;
String? _error;
bool get isLoading => _isLoading;
bool get isAuthenticated => _isAuthenticated;
String? get error => _error;
Future login(String email, String password) async {
_isLoading = true;
_error = null;
notifyListeners();
try {
// 模拟登录请求
await Future.delayed(Duration(seconds: 2));
_isAuthenticated = true;
} catch (e) {
_error = e.toString();
} finally {
_isLoading = false;
notifyListeners();
}
}
}
步骤 2:在界面中使用
class LoginScreen extends StatelessWidget {
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(controller: emailController),
TextField(controller: passwordController),
// 监听加载状态
Consumer(
builder: (context, auth, child) {
return auth.isLoading
? CircularProgressIndicator()
: ElevatedButton(
onPressed: () async {
await auth.login(
emailController.text,
passwordController.text,
);
},
child: Text('Login'),
);
},
),
// 显示错误信息
if (context.watch().error != null)
Text(context.watch().error!, style: TextStyle(color: Colors.red)),
],
),
),
),
);
}
}
避免过度使用
StatefulWidget
。分层组织状态
UserModel
、CartModel
)。MultiProvider
组合多个状态。生命周期管理
dispose
中释放资源: class MyModel extends ChangeNotifier {
final database = DatabaseConnection();
@override
void dispose() {
database.close(); // 释放资源
super.dispose();
}
}
测试状态
Provider.value
在测试中注入 mock 状态: testWidgets('Counter increments', (tester) async {
final mockCounter = MockCounterModel();
when(mockCounter.count).thenReturn(5);
await tester.pumpWidget(
Provider.value(
value: mockCounter,
child: MyApp(),
),
);
expect(find.text('5'), findsOneWidget);
});
// 1. 定义路由状态类
class RouteState extends ChangeNotifier {
String _currentRoute = '/';
String get currentRoute => _currentRoute;
void navigateTo(String route) {
_currentRoute = route;
notifyListeners();
}
}
// 2. 在顶层提供路由状态
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => RouteState(),
child: MyApp(),
),
);
}
// 3. 自定义导航器
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final routeState = context.watch();
return MaterialApp(
home: Scaffold(
body: Navigator(
pages: [
if (routeState.currentRoute == '/') MaterialPage(child: HomePage()),
if (routeState.currentRoute == '/settings') MaterialPage(child: SettingsPage()),
],
onPopPage: (route, result) => route.didPop(result),
),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'Settings'),
],
onTap: (index) {
final routes = ['/', '/settings'];
context.read().navigateTo(routes[index]);
},
),
),
);
}
}
结合 Flutter 官方路由(替代方案):
// 1. 定义路由状态
class NavigationService {
final GlobalKey navigatorKey = GlobalKey();
Future navigateTo(String routeName) {
return navigatorKey.currentState!.pushNamed(routeName);
}
bool goBack() {
return navigatorKey.currentState!.pop();
}
}
// 2. 提供 NavigationService
void main() {
runApp(
Provider(
create: (context) => NavigationService(),
child: MyApp(),
),
);
}
// 3. 在任意位置使用导航
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final navigationService = context.read();
return ElevatedButton(
onPressed: () => navigationService.navigateTo('/settings'),
child: Text('Go to Settings'),
);
}
}
方案 | 复杂度 | 性能 | 适用场景 | 路由集成方式 |
---|---|---|---|---|
Provider | 低 | 高 | 中大型应用,官方推荐 | 自定义路由状态或结合官方路由 |
Bloc | 中高 | 高 | 复杂业务逻辑,事件驱动 | BlocObserver + 自定义路由 |
GetX | 低 | 高 | 快速开发,路由管理 | 内置 GetX 路由系统 |
Riverpod | 中 | 高 | 强类型,测试友好 | 类似 Provider,更简洁 |
详细对比:
Provider vs Bloc
ChangeNotifier
,简单直观,适合中小规模状态管理。Stream
),强制分离业务逻辑与 UI,适合复杂场景(如表单验证、网络请求状态)。Provider vs GetX
Provider vs Riverpod
InheritedWidget
,需手动管理状态生命周期。方案 | 特点 | 适用场景 |
---|---|---|
Provider 自定义路由 | 完全控制路由状态,适合深度定制导航逻辑(如底部导航、侧边栏)。 | 复杂导航需求,状态与路由强绑定 |
Flutter 官方路由 | 声明式路由(MaterialPageRoute ),简单易用,但深层嵌套时传参复杂。 |
中小型应用,标准导航模式 |
GetX 路由 | 基于字符串路由,支持依赖注入、参数传递和过渡动画,代码简洁。 | 快速开发,需高效路由管理 |
GoRouter | 基于 URL 模式的声明式路由,支持路径参数、导航守卫和深度链接。 | 大型应用,需复杂路由匹配规则 |
选择建议:
状态管理:
路由管理: