Flutter3.7新增Menu菜單組件的使用教程分享

菜單組件介紹

本次Flutter穩定版本菜單系列組件新增瞭 MenuAnchorMenuBarSubmenuButtonMenuItemButton 組件, 這四個組件可以單獨使用也可以相互配合使用。他們都位於menu_anchor.dart文件內,下面對這幾個組件詳細介紹下。

MenuAnchor組件

這是一個具有子菜單的獨立區域組件,點進去我們可以看到是一個StatefulWidget組件, 這四個組件除瞭MenuBar是靜態組件,其他都是動態組件。

說明我們可以當作普通的Widget組件去使用它們,MenuAnchor可以獨立使用,通過這一個組件可以簡單的實現下拉菜單的功能。

構造函數:

  const MenuAnchor({
    super.key,
    this.controller,// 控制器
    this.childFocusNode,//如果菜單是輸入框,焦點控制
    this.style, //菜單樣式
    this.alignmentOffset = Offset.zero,//相對於組件左下角位置
    this.clipBehavior = Clip.none,// 超出屏幕剪切 不常用
    this.anchorTapClosesMenu = false,// 設置為true時,菜單打開時,點擊會重復打開。
    this.onOpen,//打開回調
    this.onClose,//關閉回調
    this.crossAxisUnconstrained = true,
    required this.menuChildren,//下拉菜單列表
    this.builder,//組件本身,通常是控制菜單的按鈕
    this.child,//傳遞給上方builder裡的child組件
  });

官方示例:

官方示例菜單後面的字母是自定義快捷鍵的操作,我們重點看下菜單的聯動功能,菜單聯動是和SubmenuButton實現的,例如官方示例中的設置背景色的菜單就是使用它實現的。接下來介紹下這個組件。

SubmenuButton 聯級菜單按鈕

通過這個按鈕可以實現菜單的聯級調用,一般用來該選項下還有下級菜單時使用。該組件一般和MenuAnchorMenuBar配合使用。

  const SubmenuButton({
    super.key,
    this.onHover,//按鈕是否選中回調 在pc端屬於鼠標指針在此菜單上
    this.onFocusChange,//是否獲取焦點回調
    this.onOpen,//打開下級菜單回調
    this.onClose,//關閉下級菜單回調
    this.style,//按鈕本身樣式
    this.menuStyle,//下級菜單樣式
    this.alignmentOffset,//相對位置偏移量 默認和組件上邊對齊
    this.clipBehavior = Clip.none,
    this.focusNode,
    this.statesController,//組件狀態擴展
    this.leadingIcon,//左邊可選圖標
    this.trailingIcon,//右邊可選圖標
    required this.menuChildren,//聯級菜單
    required this.child,//組件本身
  });

MenuItemButton 菜單按鈕組件

具體菜單的選項,一般菜單選項沒有下一級菜單時具有具體的功能使用,通過構造方法可以自定義快捷鍵,快捷鍵功能一般在PC端上使用。

構造方法:

  const MenuItemButton({
    super.key,
    this.onPressed,//點擊事件
    this.onHover,//選中回調
    this.requestFocusOnHover = true,//指針懸停是否聚焦
    this.onFocusChange,//是否獲取焦點回調
    this.focusNode,//焦點控制
    this.shortcut,//快捷鍵設置
    this.style,//本身樣式
    this.statesController,//組件狀態擴展
    this.clipBehavior = Clip.none,
    this.leadingIcon,//...
    this.trailingIcon,//...
    required this.child,//...
  });

MenuBar 多菜單聯級菜單頭部Bar

此組件是管理多個聯級菜單頭部的組件,例如掘金編輯器下圖,如果菜單選項隻有1個可以使用MenuAnchor,多個時使用MenuBar.

紅框內的組件集就是MenuBar組件的作用,它可以管理各個菜單之間的聯動,默認他們共用一個控制器。一般和SubmenuButtonMenuItemButton配合使用。

 const MenuBar({
    super.key,
    this.style,// 菜單樣式
    this.clipBehavior = Clip.none,
    this.controller,
    required this.children,
  });

示例效果:

左邊的菜單1、2、3是一組MenuBar組件,右邊是可以獨立的MenuAnchor組件。

示例源碼:

相較於官方示例,該示例下方展示瞭上方四個菜單組件的單獨使用以及聯合使用的簡單示例,去掉瞭快捷鍵設置的屬性,更直觀的瞭解菜單組件的使用。快捷鍵的使用一般在PC端使用。

import 'package:flutter/material.dart';
void main() => runApp(const MenuApp());
enum MenuEntry {
  about('About'),
  showMessage('Show Message'),
  hideMessage('Hide Message'),
  colorMenu('Color Menu'),
  colorRed('Red Background'),
  colorGreen('Green Background'),
  colorBlue('Blue Background');

  final String label;
  const MenuEntry(this.label);
}

class MyCascadingMenu extends StatefulWidget {
  const MyCascadingMenu({super.key, required this.message});

  final String message;

  @override
  State<MyCascadingMenu> createState() => _MyCascadingMenuState();
}

class _MyCascadingMenuState extends State<MyCascadingMenu> {
  MenuEntry? _lastSelection;
  final FocusNode _buttonFocusNode = FocusNode(debugLabel: 'Menu Button');

  Color get backgroundColor => _backgroundColor;
  Color _backgroundColor = Colors.red;
  set backgroundColor(Color value) {
    if (_backgroundColor != value) {
      setState(() {
        _backgroundColor = value;
      });
    }
  }

  bool get showingMessage => _showingMessage;
  bool _showingMessage = false;
  set showingMessage(bool value) {
    if (_showingMessage != value) {
      setState(() {
        _showingMessage = value;
      });
    }
  }

  @override
  void dispose() {
    _buttonFocusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        MenuBar(
            style: MenuStyle(
                backgroundColor:
                    MaterialStateColor.resolveWith((states) => Colors.white),),
            children: [
              SubmenuButton(menuChildren: _meunList(), child: const Text("菜單1")),
              SubmenuButton(menuChildren: _meunList(), child: const Text("菜單2")),
              SubmenuButton(menuChildren: _meunList(), child: const Text("菜單3")),
               MenuAnchor(
          childFocusNode: _buttonFocusNode,
          menuChildren: _meunList(),
          builder:
              (BuildContext context, MenuController controller, Widget? child) {
            return TextButton(
              focusNode: _buttonFocusNode,
              onPressed: () {
                if (controller.isOpen) {
                  controller.close();
                } else {
                  controller.open();
                }
              },
              child: const Text('OPEN MENU'),
            );
          },
        ),
            ]),
       
        Expanded(
          child: Container(
            alignment: Alignment.center,
            color: backgroundColor,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.all(12.0),
                  child: Text(
                    showingMessage ? widget.message : '',
                    style: Theme.of(context).textTheme.headlineSmall,
                  ),
                ),
                Text(_lastSelection != null
                    ? 'Last Selected: ${_lastSelection!.label}'
                    : ''),
              ],
            ),
          ),
        ),
      ],
    );
  }

  void _activate(MenuEntry selection) {
    setState(() {
      _lastSelection = selection;
    });

    switch (selection) {
      case MenuEntry.about:
        showAboutDialog(
          context: context,
          applicationName: 'MenuBar Sample',
          applicationVersion: '1.0.0',
        );
        break;
      case MenuEntry.hideMessage:
      case MenuEntry.showMessage:
        showingMessage = !showingMessage;
        break;
      case MenuEntry.colorMenu:
        break;
      case MenuEntry.colorRed:
        backgroundColor = Colors.red;
        break;
      case MenuEntry.colorGreen:
        backgroundColor = Colors.green;
        break;
      case MenuEntry.colorBlue:
        backgroundColor = Colors.blue;
        break;
    }
  }

  List<Widget> _meunList() {
    return <Widget>[
      MenuItemButton(
        child: Text(MenuEntry.about.label),
        onPressed: () => _activate(MenuEntry.about),
      ),
      if (_showingMessage)
        MenuItemButton(
          onPressed: () => _activate(MenuEntry.hideMessage),
          child: Text(MenuEntry.hideMessage.label),
        ),
      if (!_showingMessage)
        MenuItemButton(
          onPressed: () => _activate(MenuEntry.showMessage),
          child: Text(MenuEntry.showMessage.label),
        ),
      SubmenuButton(
        leadingIcon: const Icon(Icons.ac_unit_sharp),
        menuChildren: <Widget>[
          MenuItemButton(
            onPressed: () => _activate(MenuEntry.colorRed),
            child: Text(MenuEntry.colorRed.label),
          ),
          MenuItemButton(
            onPressed: () => _activate(MenuEntry.colorGreen),
            child: Text(MenuEntry.colorGreen.label),
          ),
          MenuItemButton(
            onPressed: () => _activate(MenuEntry.colorBlue),
            child: Text(MenuEntry.colorBlue.label),
          ),
        ],
        child: const Text('Background Color'),
      ),
    ];
  }
}

class MenuApp extends StatelessWidget {
  const MenuApp({super.key});

  static const String kMessage = '"Talk less. Smile more." - A. Burr';

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(body: MyCascadingMenu(message: kMessage)),
    );
  }
}

菜單樣式 MenuStyle

構造方法:

構造方法內大多數參數使用的是 MaterialStateProperty<T>具有狀態選擇設置,這樣做的好處是在PC端例如懸停、點擊、不可點擊等狀態設置不同樣式時,會非常的方便。例如系統自帶的顏色、邊框MaterialStateColorMaterialStateBorderSide等都是通過 MaterialStateProperty擴展的。

const MenuStyle({
    this.backgroundColor,
    this.shadowColor,
    this.surfaceTintColor,
    this.elevation,
    this.padding,
    this.minimumSize,
    this.fixedSize,
    this.maximumSize,
    this.side,
    this.shape,
    this.mouseCursor,
    this.visualDensity,
    this.alignment,
  });

原生系統菜單系列組件

使用平臺原生菜單組件實現,非Flutter渲染,例如在MacOS系統上特別有用,因為在MacOS上需要一個系統級菜單。

  • PlatformMenuBar
  • PlatformMenu
  • PlatformMenuItem
  • PlatformMenuItemGroup

使用方法大同小異,區別就是這是基於不同平臺實現的系統菜單選項。

小結

上面就是本次更新新增的菜單相關使用的組件,可以看出這一系列組件更傾向於桌面端使用,裡面加入瞭實現快捷鍵的操作,反而對於移動端操作需要的菜單以外部分的陰影,菜單彈出動畫都沒有找到支持的方法。

以上就是Flutter3.7新增Menu菜單組件的使用教程分享的詳細內容,更多關於Flutter Menu菜單組件的資料請關註WalkonNet其它相關文章!

推薦閱讀: