[Flutter] 扩展一个支持弹出菜单的IconButton
作者:互联网
Flutter有很多的基础Widget,其中IconButton很常用,还有 PopupButton, 这里扩展的这个 AppBarButton 是将两者融合一起,用起来更方便了。
import 'package:flutter/material.dart'; class AppBarButton<T> extends StatelessWidget { final Widget child; final Color color, focusColor; final double iconSize; final String tooltip; final bool autofocus; /// 菜单列表 /// /// [[{'title': '分享', 'icon': FIcons.share_2, 'type': SHARE}]] final List<Map<String, dynamic>> menus; final Offset menuOffset; final MenusWidgetBuilder<T> menuItemBuilder; final T menuInitialValue; final T menuSelected; final double minWidth, splashRadius; final EdgeInsetsGeometry padding; final VoidCallback onPressed; final ValueChanged<T> onSelected; const AppBarButton({Key key, Widget child, Widget icon, this.color, this.focusColor, this.iconSize, this.tooltip, this.minWidth, this.splashRadius, this.autofocus, this.menus, this.menuOffset, this.menuItemBuilder, this.menuInitialValue, this.menuSelected, this.onSelected, this.padding, VoidCallback onPressed, VoidCallback onTap}): child = child ?? icon, onPressed = onPressed ?? onTap, super(key: key); @override Widget build(BuildContext context) { if ((menus != null && menus.isNotEmpty) || menuItemBuilder != null) return _buildButton(context, () { showButtonMenu(context, menus: menus, menuItemBuilder: menuItemBuilder, menuInitialValue: menuInitialValue, menuOffset: menuOffset, menuSelected: menuSelected, onSelected: onSelected ); }); return _buildButton(context, onPressed); } Widget _buildButton(BuildContext context, VoidCallback onPressed) { final _btn = IconButton( iconSize: iconSize ?? Theme.of(context).appBarTheme.iconTheme.size, icon: child, autofocus: autofocus ?? false, focusColor: focusColor, tooltip: tooltip, color: color, splashRadius: splashRadius, padding: padding ?? const EdgeInsets.all(8.0), onPressed: onPressed, ); return minWidth == null ? _btn : ButtonTheme( minWidth: minWidth, child: _btn, ); } /// 显示弹出菜单 static void showButtonMenu<T>(BuildContext context, { Offset menuOffset, Offset local, List<Map<String, dynamic>> menus, MenusWidgetBuilder<T> menuItemBuilder, T menuInitialValue, T menuSelected, ValueChanged<T> onSelected, VoidCallback onMenuCancel, }) { final PopupMenuThemeData popupMenuTheme = PopupMenuTheme.of(context); final RenderBox button = context.findRenderObject() as RenderBox; final RenderBox overlay = Overlay.of(context).context.findRenderObject() as RenderBox; final RelativeRect position = RelativeRect.fromRect( Rect.fromPoints( local != null ? local : button.localToGlobal(menuOffset ?? Offset(0, 40), ancestor: overlay), button.localToGlobal(button.size.bottomRight(Offset.zero), ancestor: overlay), ), Offset.zero & overlay.size, ); final _primaryColor = Theme.of(context).primaryColor; final _textStyle = Theme.of(context).popupMenuTheme?.textStyle ?? Theme.of(context).textTheme.subtitle1; final List<PopupMenuEntry<T>> items = menuItemBuilder != null ? menuItemBuilder(context) : menus.map((element) { final _value = element['type']; return PopupMenuItem<T>( textStyle: _textStyle, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ (menuSelected == _value) ? DefaultTextStyle( style: _textStyle.copyWith(color: _primaryColor), child: Text(element['title'])) : Text(element['title']), if (element['icon'] != null) Icon(element['icon'], color: _primaryColor, size: 20), ], ), value: _value, ); }).toList(); // Only show the menu if there is something to show if (items.isNotEmpty) { showMenu<T>( context: context, elevation: popupMenuTheme.elevation, items: items, position: position, shape: popupMenuTheme.shape, color: popupMenuTheme.color, initialValue: menuInitialValue, ).then<void>((T newValue) { if (newValue == null) { if (onMenuCancel != null) onMenuCancel(); return null; } if (onSelected != null) onSelected(newValue); return newValue; }); } } } typedef MenusWidgetBuilder<T> = List<PopupMenuEntry<T>> Function(BuildContext context);
使用示例:
AppBarButton( icon: Icon(FIcons.save), iconSize: 21, tooltip: "保存", onPressed: () => _saveRule(context), ), AppBarButton( icon: Icon(FIcons.share_2), tooltip: "分享", menus: [ {'title': '分享', 'icon': FIcons.share_2, 'type': SHARE}, {'title': '扫码分享到远程', 'icon': FIcons.bscan, 'type': SHARE_QRSCAN}, ], onSelected: (value) => onPopupMenuClick(value, provider), ), AppBarButton<SourceSortType>( icon: Icon(Icons.sort_by_alpha), tooltip: "排序", menus: [ {'title': '按类型', 'icon': _type == SourceSortType.type ? _axisIcon : null, 'type': SourceSortType.type}, {'title': '按名称', 'icon': _type == SourceSortType.name ? _axisIcon : null, 'type': SourceSortType.name}, {'title': '按作者', 'icon': _type == SourceSortType.author ? _axisIcon : null, 'type': SourceSortType.author}, {'title': '按分组', 'icon': _type == SourceSortType.group ? _axisIcon : null, 'type': SourceSortType.group}, {'title': '按修改时间', 'icon': _type == SourceSortType.updateTime ? _axisIcon : null, 'type': SourceSortType.updateTime}, {'title': '按创建时间', 'icon': _type == SourceSortType.createTime ? _axisIcon : null, 'type': SourceSortType.createTime}, ], menuSelected: _type, onSelected: (value) { provider.sort(value, _profile) .whenComplete(() => refreshData(provider)); }, )
标签:菜单,SourceSortType,IconButton,Flutter,context,null,type,final,icon 来源: https://www.cnblogs.com/yangyxd/p/14026104.html