Flutter Material 3实现类似Atlassian的AppBar用户头像下拉菜单
实现Material3风格的Atlassian式用户下拉菜单
针对你的需求,下面提供手动基于MenuAnchor扩展实现和第三方包替代两种方案:
一、手动实现(基于Material3的MenuAnchor)
MenuAnchor本身支持自定义menuChildren,你可以通过嵌套Column、Text、Divider、MenuItemButton等Widget来构建分区、标题和不可点击区域,完全不需要依赖第三方包:
完整代码示例
AppBar( actions: [ MenuAnchor( builder: (context, controller, child) { return IconButton( icon: const CircleAvatar( backgroundImage: NetworkImage('你的头像URL'), ), onPressed: () { controller.isOpen ? controller.close() : controller.open(); }, ); }, menuChildren: [ // 不可点击的账户信息区域(蓝色标记) Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( 'Guiu Mateu', style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 4), Text( 'guiu@example.com', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), ], ), ), // 分区分隔线 const Divider(height: 1), // 第一个分区:账户(绿色标记) Padding( padding: const EdgeInsets.only(left: 16, top: 12, bottom: 8), child: Text( '账户', // 分区标题(黄色标记) style: Theme.of(context).textTheme.labelSmall?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, fontWeight: FontWeight.w500, ), ), ), MenuItemButton( leadingIcon: const Icon(Icons.person_outline), child: const Text('查看个人资料'), onPressed: () {}, ), MenuItemButton( leadingIcon: const Icon(Icons.settings_outlined), child: const Text('账户设置'), onPressed: () {}, ), // 分区分隔线 const Divider(height: 1), // 第二个分区:最近工作区(绿色标记) Padding( padding: const EdgeInsets.only(left: 16, top: 12, bottom: 8), child: Text( '最近工作区', // 分区标题(黄色标记) style: Theme.of(context).textTheme.labelSmall?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, fontWeight: FontWeight.w500, ), ), ), MenuItemButton( leadingIcon: const Icon(Icons.work_outline), child: const Text('项目A工作区'), onPressed: () {}, ), MenuItemButton( leadingIcon: const Icon(Icons.work_outline), child: const Text('个人沙箱'), onPressed: () {}, ), // 底部退出按钮分区 const Divider(height: 1), MenuItemButton( leadingIcon: const Icon(Icons.logout), child: const Text('退出登录'), style: MenuItemButton.styleFrom( foregroundColor: Theme.of(context).colorScheme.error, ), onPressed: () {}, ), ], ), ], )
关键实现点说明
- 不可点击区域:直接用Padding+Column包裹文本,不添加任何点击回调,通过主题文本样式适配Material3风格
- 分区标题:用Text组件配合
labelSmall文本样式,颜色设为onSurfaceVariant,添加左侧内边距实现对齐 - 分区分隔:用
Divider组件实现分区之间的分割线,和Atlassian的样式一致 - 菜单交互:保留
MenuItemButton实现可点击菜单项,保证Material3的交互一致性
二、第三方包替代方案
如果不想手动布局,可以使用popover包,它支持完全自定义下拉内容,适配Material3风格:
使用步骤
- 添加依赖到
pubspec.yaml:
dependencies: popover: ^0.2.8
- 实现示例
AppBar( actions: [ Popover( bodyBuilder: (context) => SizedBox( width: 280, child: Column( mainAxisSize: MainAxisSize.min, children: [ // 不可点击的账户信息区域 Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Guiu Mateu', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 4), Text('guiu@example.com', style: Theme.of(context).textTheme.bodySmall?.copyWith(color: Theme.of(context).colorScheme.onSurfaceVariant)), ], ), ), const Divider(height: 1), // 账户分区 Padding( padding: const EdgeInsets.only(left: 16, top: 12, bottom: 8), child: Text('账户', style: Theme.of(context).textTheme.labelSmall?.copyWith(color: Theme.of(context).colorScheme.onSurfaceVariant, fontWeight: FontWeight.w500)), ), ListTile( leading: const Icon(Icons.person_outline), title: const Text('查看个人资料'), onTap: () { Navigator.pop(context); // 执行操作 }, ), // 后续分区和内容同上... ], ), ), direction: PopoverDirection.bottom, child: const CircleAvatar( backgroundImage: NetworkImage('你的头像URL'), ), ), ], )
这个包的优势是可以更灵活地控制下拉菜单的位置、大小和内容样式,不需要受限于MenuAnchor的默认布局。
内容的提问来源于stack exchange,提问作者Guiu Mateu
相关产品推荐
相关产品推荐

