Riverpod修改Provider数据后UI未更新问题求助
Riverpod修改数据后UI不更新的问题修复
你遇到的问题是:使用Riverpod的StateNotifier管理BoardModel状态时,点击网格项修改玩家分数后,UI不会自动刷新,只有热重载后才能看到新分数。
问题根源
- 状态未真正更新:BoardModel中的
boardData是可变Map,你直接修改了这个Map的值,然后把同一个BoardModel实例重新赋值给state。Riverpod通过对比状态实例的引用来判断是否需要通知监听者,引用未改变时,不会触发UI重建。 - 子组件依赖旧值:BoardPlayerGridItem组件依赖初始化时传入的
widget.score显示分数,即使父组件重建,子组件也不会主动获取最新状态中的分数。
修复方案
1. 让BoardModel成为不可变类并实现copyWith方法
修改BoardModel,添加copyWith方法确保每次修改都创建新实例,而非修改原有属性:
import 'package:flutter/material.dart'; import 'package:uuid/uuid.dart'; import 'package:counter/models/player_model.dart'; const uuid = Uuid(); class BoardModel { BoardModel({ required this.name, required this.icon, required this.initialValue, required this.increment, required this.boardData, required this.owner, required this.isManaged, required this.isOnline, required this.autosaveEnabled, required this.lastUpdated, String? id, }) : id = id ?? uuid.v4(); final String id; final String name; final Icon icon; final int initialValue; final int increment; final Map<PlayerModel, int> boardData; final PlayerModel owner; final bool isManaged; final bool isOnline; final bool autosaveEnabled; final DateTime lastUpdated; // 用于创建新实例的copyWith方法 BoardModel copyWith({ String? name, Icon? icon, int? initialValue, int? increment, Map<PlayerModel, int>? boardData, PlayerModel? owner, bool? isManaged, bool? isOnline, bool? autosaveEnabled, DateTime? lastUpdated, String? id, }) { return BoardModel( name: name ?? this.name, icon: icon ?? this.icon, initialValue: initialValue ?? this.initialValue, increment: increment ?? this.increment, boardData: boardData ?? this.boardData, owner: owner ?? this.owner, isManaged: isManaged ?? this.isManaged, isOnline: isOnline ?? this.isOnline, autosaveEnabled: autosaveEnabled ?? this.autosaveEnabled, lastUpdated: lastUpdated ?? DateTime.now(), id: id ?? this.id, ); } }
2. 修改BoardNotifier,创建新状态实例
更新incrementPlayerScore方法,不再直接修改原Map,而是创建新的Map和BoardModel实例:
class BoardNotifier extends StateNotifier<BoardModel> { BoardNotifier() : super(sampleBoard); void loadBoard(BoardModel board) { state = board; } void incrementPlayerScore(PlayerModel player) { // 创建新Map并复制原有数据,更新指定玩家分数 final newBoardData = Map<PlayerModel, int>.from(state.boardData); newBoardData.update( player, (value) => value + state.increment, ifAbsent: () => state.initialValue, ); // 使用copyWith创建新实例并更新状态 state = state.copyWith( boardData: newBoardData, lastUpdated: DateTime.now(), ); } } final boardProvider = StateNotifierProvider<BoardNotifier, BoardModel>((ref) { return BoardNotifier(); });
3. 修复BoardPlayerGridItem,读取最新状态
将子组件改为ConsumerWidget,直接从当前状态中获取最新分数,不再依赖传入的旧值:
class BoardPlayerGridItem extends ConsumerWidget { const BoardPlayerGridItem({ super.key, required this.player, }); final PlayerModel player; @override Widget build(BuildContext context, WidgetRef ref) { final providedBoard = ref.watch(boardProvider); // 从当前状态获取最新分数 final score = providedBoard.boardData[player] ?? providedBoard.initialValue; return GestureDetector( onTap: () { ref.read(boardProvider.notifier).incrementPlayerScore(player); }, onLongPress: () {}, child: Container( color: Theme.of(context).highlightColor, child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( player.name, style: Theme.of(context) .textTheme .titleMedium! .copyWith(color: Theme.of(context).colorScheme.onBackground), ), const SizedBox(height: 10), Text( score.toString(), style: Theme.of(context) .textTheme .displayLarge! .copyWith(color: Theme.of(context).colorScheme.onBackground), ), ], ), ), ); } }
4. 优化父组件GridView构建
移除冗余的boardDataList转换,直接在itemBuilder中获取数据:
// 父组件body部分 body: GridView.builder( padding: const EdgeInsets.all(16), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, childAspectRatio: 3 / 2, crossAxisSpacing: 10, mainAxisSpacing: 10, ), itemCount: providedBoard.boardData.length, itemBuilder: (context, index) { final entry = providedBoard.boardData.entries.elementAt(index); return BoardPlayerGridItem( player: entry.key, ); }, ),
内容的提问来源于stack exchange,提问作者CSF90
相关产品推荐
相关产品推荐

