基于 Flutter 从零开发一款产品(三)—— 自定义菜单栏

547 阅读2分钟

自定义菜单栏

在 winodws 和 macOS 当中,都会有默认的菜单栏样式,如果我们不喜欢这种样式,是否可以对其进行修改呢?答案是肯定的,现在我们就来看看在 Flutter 当中如何修改默认的菜单栏,对默认的菜单栏进行隐藏,实现一个我们喜欢的菜单栏样式与功能。

windows 当中默认的菜单栏样式如下图所示:

image.png

macOS 中菜单栏的默认样式:

image.png

隐藏默认的菜单栏,并实现自己的样式:

image.png

下面我们来看看如何实现自定义菜单栏的需求:主要是借用window_manager这个依赖包进行实现,使用这个依赖包,我们可以方便的实现调整窗口的大小、对窗口进行定位等功能。

  • 添加依赖包
flutter pub add window_manager
  • 隐藏默认菜单栏,实现自定义菜单栏
class WindowAdapter {
  static Future<void> setSize() async {
    //判断是否为桌面端应用
    if (Platform.isMacOS || Platform.isWindows || Platform.isLinux) {
      //等待 windows manager 完成初始化
      await windowManager.ensureInitialized();
      //设置在桌面端展示窗口的大小、应用在桌面当中的定位、背景色等
      WindowOptions windowOptions = const WindowOptions(
        size: Size(920, 680),
        minimumSize: Size(920, 690),
        center: true,
        backgroundColor: Colors.transparent,
        skipTaskbar: false,
        titleBarStyle: TitleBarStyle.hidden,
      );
      windowManager.waitUntilReadyToShow(windowOptions, () async {
        //隐藏默认的菜单栏
        await windowManager.setTitleBarStyle(TitleBarStyle.hidden,
            windowButtonVisibility: false);
        await windowManager.show();
        await windowManager.focus();
      });
    }
  }
}

由于隐藏了默认的菜单栏,会导致无法对应用窗口进行拖拽、放大和缩小,所以在这里我们新增了两个组件:一拖拽组件、另一个放大、缩小、关闭按钮组件,并调用 window_manager 提供的API实现对窗口的操作。

  • 拖拽组件:只要对另一个组件进行修饰,那么另外一个组件便可以获得拖拽功能

class DragToMoveArea extends StatelessWidget {
  final Widget child;

  const DragToMoveArea({
    super.key,
    required this.child,
  });

  @override
  Widget build(BuildContext context) {
    if (Platform.isAndroid || Platform.isIOS) return child;
    return GestureDetector(
      behavior: HitTestBehavior.translucent,
      onPanStart: (details) {
        //调用 window_manager 的 API 实现拖拽功能
        windowManager.startDragging();
      },
      child: child,
    );
  }
}
  • 缩小、放大、关闭按钮组件:

class WindowButtons extends StatefulWidget {
  final List<Widget>? actions;
  const WindowButtons({super.key, this.actions});

  @override
  State<WindowButtons> createState() => _WindowButtonsState();
}

class _WindowButtonsState extends State<WindowButtons> {
  @override
  Widget build(BuildContext context) {
    Brightness brightness = Theme.of(context).brightness;
    return Align(
      alignment: Alignment.topRight,
      child: Wrap(
        spacing: 5,
        children: [
          if (widget.actions != null) ...widget.actions!,
          SizedBox(
            width: 30,
            height: 30,
            child: WindowCaptionButton.minimize(
              brightness: brightness,
              onPressed: () async {
                bool isMinimized = await windowManager.isMinimized();
                if (isMinimized) {
                  windowManager.restore();
                } else {
                  windowManager.minimize();
                }
              },
            ),
          ),
          SizedBox(
            width: 30,
            height: 30,
            child: FutureBuilder<bool>(
              future: windowManager.isMaximized(),
              builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
                if (snapshot.data == true) {
                  return WindowCaptionButton.unmaximize(
                    brightness: brightness,
                    onPressed: () async {
                      await windowManager.unmaximize();
                      setState(() {});
                    },
                  );
                }
                return WindowCaptionButton.maximize(
                  brightness: brightness,
                  onPressed: () async {
                    await windowManager.maximize();
                    setState(() {});
                  },
                );
              },
            ),
          ),
          SizedBox(
            height: 30,
            width: 30,
            child: WindowCaptionButton.close(
              brightness: brightness,
              onPressed: () {
                windowManager.close();
              },
            ),
          ),
        ],
      ),
    );
  }
}

  • 使用组件,在 AppBar 当中进行使用,就可以让其获得菜单栏的默认功能:

image.png

    return Scaffold(
      appBar: PreferredSize(
          preferredSize: const Size.fromHeight(30),
          child: DragToMoveArea(
            child: AppBar(
              actions: const [WindowButtons()],
            ),
          )),

其他章节