如何将基于Provider的Flutter项目迁移至flutter_riverpod?
Provider转flutter_riverpod 转换方案
一、原Provider核心代码示例(模拟常见业务实现)
1. RosterDataProvider(ChangeNotifier)
import 'package:flutter/material.dart'; class RosterDataProvider extends ChangeNotifier { List<String> _rosterList = ['张三', '李四', '王五']; List<String> _leaveList = []; List<String> get rosterList => _rosterList; List<String> get leaveList => _leaveList; void addLeave(String name) { if (_rosterList.contains(name)) { _rosterList.remove(name); _leaveList.add(name); notifyListeners(); } } void cancelLeave(String name) { if (_leaveList.contains(name)) { _leaveList.remove(name); _rosterList.add(name); notifyListeners(); } } }
2. MainScreen(Provider消费)
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'leave_page.dart'; class MainScreen extends StatelessWidget { const MainScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('排班列表')), body: Consumer<RosterDataProvider>( builder: (context, provider, child) { return ListView.builder( itemCount: provider.rosterList.length, itemBuilder: (context, index) { final name = provider.rosterList[index]; return ListTile( title: Text(name), trailing: ElevatedButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => LeavePage(name: name), ), ); }, child: const Text('申请请假'), ), ); }, ); }, ), ); } }
3. LeavePage(Provider操作)
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'roster_data_provider.dart'; class LeavePage extends StatelessWidget { final String name; const LeavePage({super.key, required this.name}); @override Widget build(BuildContext context) { final provider = Provider.of<RosterDataProvider>(context, listen: false); return Scaffold( appBar: AppBar(title: const Text('申请请假')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('确认${name}请假吗?'), const SizedBox(height: 20), ElevatedButton( onPressed: () { provider.addLeave(name); Navigator.pop(context); }, child: const Text('确认请假'), ), const SizedBox(height: 10), TextButton( onPressed: () => Navigator.pop(context), child: const Text('取消'), ), ], ), ), ); } }
4. 全局注册Provider
void main() { runApp( ChangeNotifierProvider( create: (context) => RosterDataProvider(), child: const MyApp(), ), ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: const MainScreen(), ); } }
二、flutter_riverpod 转换实现
1. 依赖准备
确保pubspec.yaml添加依赖:
dependencies: flutter_riverpod: ^2.3.0 flutter: sdk: flutter
2. RosterState 状态类与 RosterNotifier
import 'package:flutter_riverpod/flutter_riverpod.dart'; // 不可变状态类,存储核心数据 class RosterState { final List<String> rosterList; final List<String> leaveList; RosterState({ required this.rosterList, required this.leaveList, }); // 生成新状态的复制方法,保证状态不可变 RosterState copyWith({ List<String>? rosterList, List<String>? leaveList, }) { return RosterState( rosterList: rosterList ?? this.rosterList, leaveList: leaveList ?? this.leaveList, ); } } // 状态逻辑处理类 class RosterNotifier extends StateNotifier<RosterState> { RosterNotifier() : super(RosterState( rosterList: ['张三', '李四', '王五'], leaveList: [], )); void addLeave(String name) { if (state.rosterList.contains(name)) { final newRoster = List<String>.from(state.rosterList)..remove(name); final newLeave = List<String>.from(state.leaveList)..add(name); state = state.copyWith(rosterList: newRoster, leaveList: newLeave); } } void cancelLeave(String name) { if (state.leaveList.contains(name)) { final newLeave = List<String>.from(state.leaveList)..remove(name); final newRoster = List<String>.from(state.rosterList)..add(name); state = state.copyWith(rosterList: newRoster, leaveList: newLeave); } } } // 全局状态Provider final rosterProvider = StateNotifierProvider<RosterNotifier, RosterState>((ref) { return RosterNotifier(); });
3. MainScreen 转换(ConsumerWidget)
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'leave_page.dart'; class MainScreen extends ConsumerWidget { const MainScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { // 监听状态变化,自动刷新UI final rosterState = ref.watch(rosterProvider); return Scaffold( appBar: AppBar(title: const Text('排班列表')), body: ListView.builder( itemCount: rosterState.rosterList.length, itemBuilder: (context, index) { final name = rosterState.rosterList[index]; return ListTile( title: Text(name), trailing: ElevatedButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => LeavePage(name: name), ), ); }, child: const Text('申请请假'), ), ); }, ), ); } }
4. LeavePage 转换
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'roster_notifier.dart'; class LeavePage extends ConsumerWidget { final String name; const LeavePage({super.key, required this.name}); @override Widget build(BuildContext context, WidgetRef ref) { // 仅获取状态操作类,不监听状态变化 final rosterNotifier = ref.read(rosterProvider.notifier); return Scaffold( appBar: AppBar(title: const Text('申请请假')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('确认${name}请假吗?'), const SizedBox(height: 20), ElevatedButton( onPressed: () { rosterNotifier.addLeave(name); Navigator.pop(context); }, child: const Text('确认请假'), ), const SizedBox(height: 10), TextButton( onPressed: () => Navigator.pop(context), child: const Text('取消'), ), ], ), ), ); } }
5. 全局配置ProviderScope
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'main_screen.dart'; void main() { runApp( // 全局状态容器,替代原ChangeNotifierProvider const ProviderScope( child: MyApp(), ), ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: const MainScreen(), ); } }
三、核心转换要点
- 状态容器替换:用
ProviderScope全局包裹App,单个状态用StateNotifierProvider定义 - 状态更新逻辑:原
notifyListeners()替换为直接赋值state = newState,需通过copyWith生成不可变新状态 - 页面状态消费:原
Consumer/Provider.of替换为ConsumerWidget+ref.watch(监听状态)或ref.read(仅获取操作类) - 状态设计规范:Riverpod强制状态不可变,需通过复制方法生成新状态,避免直接修改原数据
内容的提问来源于stack exchange,提问作者Pannam
相关产品推荐
相关产品推荐

