如何基于另一个PopupMenuItem事件重建组件并同步值?
你需要实现的是点击PopupMenu第一个菜单项的+按钮后,第二个菜单项能实时同步显示更新后的数值,关闭菜单后再次打开数值保持一致。下面针对你的Riverpod代码给出两种可行方案:
问题根源
当前PopupMenuButton的itemBuilder只会在菜单首次打开时生成一次菜单项,后续Riverpod状态变化不会触发它重新生成,导致第二个菜单项无法更新。同时你额外维护的_counter变量容易造成状态不一致,完全可以统一用Riverpod的状态管理。
方案一:用Consumer包裹每个PopupMenuItem
让每个PopupMenuItem独立监听Riverpod状态变化,状态更新时自动重建自身:
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; final providerOfCount = StateProvider<int>((ref) => 0); void main() { runApp(const ProviderScope(child: MyApp())); } class MyApp extends ConsumerWidget { const MyApp({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends ConsumerStatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override ConsumerState<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends ConsumerState<MyHomePage> { @override Widget build(BuildContext context) { var count = ref.watch(providerOfCount); return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ PopupMenuButton( child: const Text('Push me!', style: TextStyle( color: Colors.purple, fontSize: 24, )), itemBuilder: (BuildContext context) { return [ PopupMenuItem( child: Consumer(builder: (context, ref, _) { final count = ref.watch(providerOfCount); return Row( children: [ IconButton( onPressed: () { ref.read(providerOfCount.notifier).state++; }, icon: const Icon( Icons.add, color: Colors.purple, ), ), Text(' $count'), ], ); }), ), PopupMenuItem( child: Consumer(builder: (context, ref, _) { final count = ref.watch(providerOfCount); return Text( 'Count: $count', style: const TextStyle(color: Colors.red), ); }), ), ]; }, ), const SizedBox(height: 72), const Text('You have pushed the button this many times:'), Text( '$count', style: Theme.of(context).textTheme.headlineMedium, ), ], ), ), ); } }
关键修改点
- 移除了多余的
_counter变量,统一依赖Riverpod的providerOfCount状态 - 给每个PopupMenuItem的child包裹
Consumer,让它们各自监听状态变化,状态更新时自动重建 - 点击+按钮时直接更新Riverpod状态,无需额外调用
setState
方案二:给PopupMenuButton添加动态Key
通过给PopupMenuButton设置随count变化的Key,强制count更新时重建整个PopupMenuButton,从而重新生成菜单项:
// 其他代码和方案一一致,仅修改PopupMenuButton部分 PopupMenuButton( key: ValueKey(count), // 关键:用count作为key,count变化时触发重建 child: const Text('Push me!', style: TextStyle( color: Colors.purple, fontSize: 24, )), itemBuilder: (BuildContext context) { final count = ref.watch(providerOfCount); return [ PopupMenuItem( child: Row( children: [ IconButton( onPressed: () { ref.read(providerOfCount.notifier).state++; }, icon: const Icon( Icons.add, color: Colors.purple, ), ), Text(' $count'), ], ), ), PopupMenuItem( child: Text( 'Count: $count', style: const TextStyle(color: Colors.red), ), ), ]; }, ),
原理
Flutter中当Widget的key变化时,会销毁旧Widget并创建新的实例,这样PopupMenuButton会重新调用itemBuilder生成最新状态的菜单项,实现数值同步。
两种方案都能解决你的问题,方案一更灵活(仅更新需要变化的菜单项),方案二更简洁。
内容的提问来源于stack exchange,提问作者user3103070
相关产品推荐
相关产品推荐

