基于flutter3.x+window_manager+getx桌面端仿macOS系统

flutter3_macui桌面端仿macOS系统实战项目完结啦!

原创研发flutter3.19+dart3.3+window_manager+getx等技术构建桌面版macOS系统。支持自定义毛玻璃虚化背景、Dock菜单多级嵌套+自由拖拽排序、可拖拽弹窗等功能。

在这里插入图片描述
支持macOS和windows11两种风格。

在这里插入图片描述

使用技术

  • 编辑器:VScode
  • 框架技术:Flutter3.19.2+Dart3.3.0
  • 窗口管理:window_manager^0.3.8
  • 路由/状态管理:get^4.6.6
  • 本地存储:get_storage^2.1.1
  • 拖拽排序:reorderables^0.6.0
  • 图表组件:fl_chart^0.67.0
  • 托盘插件:system_tray^2.0.3

在这里插入图片描述
在这里插入图片描述

特性

  1. 桌面菜单支持二级弹窗菜单
  2. 整体界面虚化毛玻璃模糊效果
  3. 经典Dock菜单
  4. 程序坞Dock菜单可拖拽式排序、支持二级弹窗式菜单
  5. 丰富视觉效果,自定义桌面主题换肤背景
  6. 可视化多窗口路由,支持弹窗方式打开新路由页面

在这里插入图片描述

项目结构

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

flutter-os布局模板

在这里插入图片描述

如上图:整体布局分为顶部导航条+桌面菜单+底部Dock菜单三部分。

return Scaffold(
  key: scaffoldKey,
  body: Container(
    // 背景图主题
    decoration: skinTheme(),
    // DragToResizeArea缩放窗口
    child: DragToResizeArea(
      child: Flex(
        direction: Axis.vertical,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // 导航栏
          WindowTitlebar(
            onDrawer: () {
              // 自定义打开右侧drawer
              scaffoldKey.currentState?.openEndDrawer();
            },
          ),

          // 桌面区域
          Expanded(
            child: GestureDetector(
              child: Container(
                color: Colors.transparent,
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Expanded(
                      child: GestureDetector(
                        child: const WindowDesktop(),
                        onSecondaryTapDown: (TapDownDetails details) {
                          posDX = details.globalPosition.dx;
                          posDY = details.globalPosition.dy;
                        },
                        onSecondaryTap: () {
                          debugPrint('桌面图标右键');
                          showDeskIconContextmenu();
                        },
                      ),
                    ),
                  ],
                ),
              ),
              onSecondaryTapDown: (TapDownDetails details) {
                posDX = details.globalPosition.dx;
                posDY = details.globalPosition.dy;
              },
              onSecondaryTap: () {
                debugPrint('桌面右键');
                showDeskContextmenu();
              },
            ),
          ),

          // Dock菜单
          settingController.settingData['dock'] == 'windows' ?
          const WindowTabbar()
          :
          const WindowDock()
          ,
        ],
      ),
    ),
  ),
  endDrawer: Drawer(
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0.0)),
    width: 300,
    child: const Settings(),
  ),
);

在这里插入图片描述
在这里插入图片描述

底部Dock菜单滤镜模糊效果,支持macos和windows11两种风格。

在这里插入图片描述

MouseRegion(
  cursor: SystemMouseCursors.click,
  onEnter: (event) {
    setState(() {
      hoveredIndex = index;
    });
    controller.forward(from: 0.0);
  },
  onExit: (event) {
    setState(() {
      hoveredIndex = -1;
    });
    controller.stop();
  },
  child: GestureDetector(
    onTapDown: (TapDownDetails details) {
      anchorDx = details.globalPosition.dx;
    },
    onTap: () {
      if(item!['children'] != null) {
        showDockDialog(item!['children']);
      }
    },
    // 缩放动画
    child: ScaleTransition(
      alignment: Alignment.bottomCenter,
      scale: hoveredIndex == index ? 
      controller.drive(Tween(begin: 1.0, end: 1.5).chain(CurveTween(curve: Curves.easeOutCubic)))
      :
      Tween(begin: 1.0, end: 1.0).animate(controller)
      ,
      child: UnconstrainedBox(
        child: Stack(
          alignment: AlignmentDirectional.topCenter,
          children: [
            // tooltip提示
            Visibility(
              visible: hoveredIndex == index && !draggable,
              child: Positioned(
                top: 0,
                child: SizedOverflowBox(
                  size: Size.zero,
                  child: Container(
                    alignment: Alignment.center,
                    padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 1.0),
                    margin: const EdgeInsets.only(bottom: 20.0),
                    decoration: BoxDecoration(
                      color: Colors.black54,
                      borderRadius: BorderRadius.circular(3.0),
                    ),
                    child: Text('${item!['tooltip']}', style: const TextStyle(color: Colors.white, fontSize: 8.0, fontFamily: 'arial')),
                  ),
                ),
              ),
            ),
            // 图片/图标
            item!['children'] != null ?
            thumbDock(item!['children'])
            :
            SizedBox(
              height: 35.0,
              width: 35.0,
              child: item!['type'] != null && item!['type'] == 'icon' ? 
              IconTheme(
                data: const IconThemeData(color: Colors.white, size: 32.0),
                child: item!['imgico'],
              )
              :
              Image.asset('${item!['imgico']}')
              ,
            ),
            // 圆点
            Visibility(
              visible: item!['active'] != null,
              child: Positioned(
                bottom: 0,
                child: SizedOverflowBox(
                  size: Size.zero,
                  child: Container(
                    margin: const EdgeInsets.only(top: 2.0),
                    height: 4.0,
                    width: 4.0,
                    decoration: BoxDecoration(
                      color: Colors.black87,
                      borderRadius: BorderRadius.circular(10.0),
                    ),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    ),
  ),
)
List dockList = [
  {'tooltip': 'Flutter3.19', 'imgico': 'assets/images/logo.png'},
  {'tooltip': 'Safari', 'imgico': 'assets/images/mac/safari.png', 'active': true},
  {
    'tooltip': 'Launchpad',
    'imgico': 'assets/images/mac/launchpad.png',
    'children': [
      {'tooltip': 'Podcasts', 'imgico': 'assets/images/mac/podcasts.png'},
      {'tooltip': 'Quicktime', 'imgico': 'assets/images/mac/quicktime.png'},
      {'tooltip': 'Notes', 'imgico': 'assets/images/mac/notes.png'},
      {'tooltip': 'Reminder', 'imgico': 'assets/images/mac/reminders.png'},
      {'tooltip': 'Calc', 'imgico': 'assets/images/mac/calculator.png'},
    ]
  },
  {'tooltip': 'Appstore', 'imgico': 'assets/images/mac/appstore.png',},
  {'tooltip': 'Messages', 'imgico': 'assets/images/mac/messages.png', 'active': true},

  {'type': 'divider'},
  
  ...
  
  {'tooltip': 'Recycle Bin', 'imgico': 'assets/images/mac/bin.png'},
];

在这里插入图片描述
Dock二级菜单采用showDialogPositioned组件实现定位弹窗。

void showDockDialog(data) {
  anchorDockOffset();
  showDialog(
    context: context,
    barrierColor: Colors.transparent,
    builder: (context) {
      return Stack(
        children: [
          Positioned(
            top: anchorDy - 210,
            left: anchorDx - 120,
            width: 240.0,
            height: 210,
            child: ClipRRect(
              borderRadius: BorderRadius.circular(16.0),
              child: BackdropFilter(
                filter: ImageFilter.blur(sigmaX: 20.0, sigmaY: 20.0),
                child: Container(
                  padding: const EdgeInsets.symmetric(vertical: 10.0),
                  decoration: const BoxDecoration(
                    backgroundBlendMode: BlendMode.overlay,
                    color: Colors.white,
                  ),
                  child: ListView(
                    children: [
                      Container(
                        padding: const EdgeInsets.symmetric(horizontal: 10.0,),
                        child: Wrap(
                          runSpacing: 5.0,
                          spacing: 5.0,
                          children: List.generate(data.length, (index) {
                            final item = data[index];
                            return MouseRegion(
                              cursor: SystemMouseCursors.click,
                              child: GestureDetector(
                                child:  Column(
                                  children: [
                                    // 图片/图标
                                    SizedBox(
                                      height: 40.0,
                                      width: 40.0,
                                      child: item!['type'] != null && item!['type'] == 'icon' ? 
                                      IconTheme(
                                        data: const IconThemeData(color: Colors.black87, size: 35.0),
                                        child: item!['imgico'],
                                      )
                                      :
                                      Image.asset('${item!['imgico']}')
                                      ,
                                    ),
                                    SizedBox(
                                      width: 70,
                                      child: Text(item['tooltip'], style: const TextStyle(color: Colors.black87, fontSize: 12.0), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center,),
                                    )
                                  ],
                                ),
                                onTap: () {
                                  // ...
                                },
                              ),
                            );
                          }),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ),
        ],
      );
    },
  );
}

flutter3实现桌面菜单

在这里插入图片描述
在这里插入图片描述
桌面菜单使用垂向排列展示。


Widget build(BuildContext context) {
  return Container(
    padding: const EdgeInsets.all(10.0),
    child: Wrap(
      direction: Axis.vertical,
      spacing: 5.0,
      runSpacing: 5.0,
      children: List.generate(deskList.length, (index) {
        final item = deskList[index];
        return MouseRegion(
          cursor: SystemMouseCursors.click,
          onEnter: (event) {
            setState(() {
              hoveredIndex = index;
            });
          },
          onExit: (event) {
            setState(() {
              hoveredIndex = -1;
            });
          },
          child: GestureDetector(
            onTapDown: (TapDownDetails details) {
              anchorDx = details.globalPosition.dx;
              anchorDy = details.globalPosition.dy;
            },
            onTap: () {
              if(item!['children'] != null) {
                showDeskDialog(item!['children']);
              }else {
                showRouteDialog(item);
              }
            },
            child: Container(
              ...
            ),
          ),
        );
      }),
    ),
  );
}

点击桌面图标,采用自定义弹窗组件显示页面内容。

/**
  桌面弹窗式路由页面  Q:282310962
*/
void showRouteDialog(item) async {
  // 链接
  if(item!['link'] != null) {
    await launchUrl(Uri.parse(item!['link']));
    return;
  }
  // 弹窗图标
  Widget dialogIcon() {
    if(item!['type'] != null && item!['type'] == 'icon') {
      return IconTheme(
        data: const IconThemeData(size: 16.0),
        child: item!['imgico'],
      );
    }else {
      return Image.asset('${item!['imgico']}', height: 16.0, width: 16.0, fit: BoxFit.cover);
    }
  }

  // Fdialog参数
  dynamic dialog = item!['dialog'] ?? {};

  navigator?.push(FdialogRoute(
    child: Fdialog(
      // 标题
      title: dialog!['title'] ?? Row(
        children: [
          dialogIcon(),
          const SizedBox(width: 5.0,),
          Text('${item!['title']}',),
        ],
      ),
      // 内容
      content: dialog!['content'] ?? ListView(
        padding: const EdgeInsets.all(10.0),
        children: [
          item!['component'] ?? const Center(child: Column(children: [Icon(Icons.layers,), Text('Empty~'),],)),
        ],
      ),
      titlePadding: dialog!['titlePadding'], // 标题内间距
      backgroundColor: dialog!['backgroundColor'] ?? Colors.white.withOpacity(.85), // 弹窗背景色
      barrierColor: dialog!['barrierColor'], // 弹窗遮罩层颜色
      offset: dialog!['offset'], // 弹窗位置(坐标点)
      width: dialog!['width'] ?? 800, // 宽度
      height: dialog!['height'] ?? 500, // 高度
      radius: dialog!['radius'], // 圆角
      fullscreen: dialog!['fullscreen'] ?? false, // 是否全屏
      maximizable: dialog!['maximizable'] ?? true, // 是否显示最大化按钮
      closable: dialog!['closable'] ?? true, // 是否显示关闭按钮
      customClose: dialog!['customClose'], // 自定义关闭按钮
      closeIcon: dialog!['closeIcon'], // 自定义关闭图标
      actionColor: dialog!['actionColor'], // 右上角按钮组颜色
      actionSize: dialog!['actionSize'], // 右上角按钮组大小
      draggable: dialog!['draggable'] ?? true, // 是否可拖拽
      destroyOnExit: dialog!['destroyOnExit'] ?? false, // 鼠标滑出弹窗是否销毁关闭
    ),
  ));
}

桌面菜单json配置列表。

List deskList = [
  {'title': 'Flutter3.19', 'imgico': 'assets/images/logo.png', 'link': 'https://flutter.dev/'},
  {
    'title': '首页', 'imgico': const Icon(Icons.home_outlined), 'type': 'icon',
    'component': const Home(),
    'dialog': {
      'fullscreen': true
    }
  },
  {
    'title': '工作台', 'imgico': const Icon(Icons.poll_outlined), 'type': 'icon',
    'component': const Dashboard(),
  },
  {
    'title': '组件',
    'imgico': const Icon(Icons.apps),
    'type': 'icon',
    'children': [
      {'title': 'Mail', 'imgico': 'assets/images/mac/mail.png'},
      {'title': 'Info', 'imgico': 'assets/images/mac/info.png'},
      {'title': 'Editor', 'imgico': 'assets/images/mac/scripteditor.png'},
      {'title': '下载', 'imgico': const Icon(Icons.download_outlined), 'type': 'icon'},
      {'title': 'Bug统计', 'imgico': const Icon(Icons.bug_report_outlined), 'type': 'icon'},
      {'title': '计算器', 'imgico': const Icon(Icons.calculate), 'type': 'icon'},
      {'title': '图表', 'imgico': const Icon(Icons.bar_chart), 'type': 'icon'},
      {'title': '打印', 'imgico': const Icon(Icons.print), 'type': 'icon'},
      {'title': '站内信', 'imgico': const Icon(Icons.campaign), 'type': 'icon'},
      {'title': '云存储', 'imgico': const Icon(Icons.cloud_outlined), 'type': 'icon'},
      {'title': '裁剪', 'imgico': const Icon(Icons.crop_outlined), 'type': 'icon'},
    ]
  },
  {
    'title': '私密空间', 'imgico': const Icon(Icons.camera_outlined), 'type': 'icon',
    'component': const Uzone(),
  },
  
  ...
  
  {
    'title': '公众号', 'imgico': const Icon(Icons.qr_code), 'type': 'icon',
    'dialog': {
      'title': const Text('QRcode', style: TextStyle(color: Colors.white60, fontSize: 14.0, fontFamily: 'arial')),
      'content': Padding(
        padding: const EdgeInsets.all(10.0),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Image.asset('assets/images/qrcode_white.png', height: 120.0, fit: BoxFit.contain,),
            const Spacer(),
            const Text('扫一扫,关注公众号', style: TextStyle(color: Colors.white60, fontSize: 12.0,),),
          ],
        ),
      ),
      'backgroundColor': const Color(0xff07c160),
      'actionColor': Colors.white54,
      'width': 300,
      'height': 220,
      'maximizable': false,
      'closable': true,
      'draggable': true,
    }
  },
];

在这里插入图片描述
自定义Fdialog弹窗支持如下参数配置

// 标题
final Widget? title;
// 弹窗内容
final Widget content;
// 标题内间距
final EdgeInsetsGeometry? titlePadding;
// 弹窗背景色
final Color? backgroundColor;
// 弹窗遮罩层颜色
final Color? barrierColor;
// 弹窗位置(坐标点)
final Offset? offset;
// 宽度
final num width;
// 高度
final num height;
// 圆角
final double? radius;
// 是否全屏
final bool fullscreen;
// 是否显示最大化按钮
final bool maximizable;
// 是否显示关闭按钮
final bool closable;
// 自定义关闭按钮
final Widget? customClose;
// 自定义关闭图标
final IconData? closeIcon;
// 右上角按钮组颜色
final Color? actionColor;
// 右上角按钮组大小
final double? actionSize;
// 是否可拖拽
final bool draggable;
// 鼠标滑出弹窗是否销毁关闭
final bool destroyOnExit;

Okay,以上就是flutter3+getx开发桌面端os系统的一些分享知识。

https://blog.csdn.net/yanxinyun1990/article/details/136410049

https://blog.csdn.net/yanxinyun1990/article/details/135329724

在这里插入图片描述

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-13 09:16:04       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-13 09:16:04       106 阅读
  3. 在Django里面运行非项目文件

    2024-04-13 09:16:04       87 阅读
  4. Python语言-面向对象

    2024-04-13 09:16:04       96 阅读

热门阅读

  1. 二维数组得学习

    2024-04-13 09:16:04       134 阅读
  2. Scala详解(2)

    2024-04-13 09:16:04       77 阅读
  3. 数据库DMP格式备份文件

    2024-04-13 09:16:04       122 阅读
  4. 3d max快捷键命令大全

    2024-04-13 09:16:04       42 阅读
  5. 用自动LOD简化3D网格【Babylon.js】

    2024-04-13 09:16:04       43 阅读
  6. Python如何使用不同的库来导出PDF和XLSX

    2024-04-13 09:16:04       43 阅读
  7. C语言--第二章之位运算符

    2024-04-13 09:16:04       36 阅读
  8. 熟练使用Nacos、GateWay、OpenFeign、Sentinel常用组件

    2024-04-13 09:16:04       46 阅读
  9. 微服务下使用sentinel进行服务熔断

    2024-04-13 09:16:04       42 阅读