You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于另一个PopupMenuItem事件重建组件并同步值?

解决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,
            ),
          ],
        ),
      ),
    );
  }
}

关键修改点

  1. 移除了多余的_counter变量,统一依赖Riverpod的providerOfCount状态
  2. 给每个PopupMenuItem的child包裹Consumer,让它们各自监听状态变化,状态更新时自动重建
  3. 点击+按钮时直接更新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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 23:57:23