基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章

马肤

温馨提示:这篇文章已超过444天没有更新,请注意相关的内容是否还可用!

摘要:基于Flutter 3.x框架,开发跨平台的移动应用,同时支持Windows操作系统。利用Flutter的高效开发能力和Windows平台的广泛用户基础,实现应用的高性能、高效率和良好的用户体验。通过Flutter 3.x的更新,优化了性能并提升了开发效率,使得在Windows平台上构建现代化、高性能的移动应用成为可能。

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

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

基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第1张

支持macOS和windows11两种风格。

基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第2张

使用技术

  • 编辑器: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

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第3张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第4张

    特性

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

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第5张

    项目结构

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第6张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第7张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第8张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第9张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第10张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第11张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第12张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第13张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第14张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第15张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第16张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第17张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第18张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第19张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第20张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第21张

    flutter-os布局模板

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第22张

    如上图:整体布局分为顶部导航条+桌面菜单+底部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(),
      ),
    );
    

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第23张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第24张

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

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第25张

    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'},
    ];
    

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第26张

    Dock二级菜单采用showDialog和Positioned组件实现定位弹窗。

    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实现桌面菜单

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第27张

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第28张

    桌面菜单使用垂向排列展示。

    @override
    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,
        }
      },
    ];
    

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第29张

    自定义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

    基于flutter3.x+window,基于 Flutter 3.x 的跨平台应用开发实践,构建 Windows 应用体验新篇章 第30张


0
收藏0
文章版权声明:除非注明,否则均为VPS857原创文章,转载或复制请以超链接形式并注明出处。

相关阅读

  • 【研发日记】Matlab/Simulink自动生成代码(二)——五种选择结构实现方法,Matlab/Simulink自动生成代码的五种选择结构实现方法(二),Matlab/Simulink自动生成代码的五种选择结构实现方法详解(二)
  • 超级好用的C++实用库之跨平台实用方法,跨平台实用方法的C++实用库超好用指南,C++跨平台实用库使用指南,超好用实用方法集合,C++跨平台实用库超好用指南,方法与技巧集合
  • 【动态规划】斐波那契数列模型(C++),斐波那契数列模型(C++实现与动态规划解析),斐波那契数列模型解析与C++实现(动态规划)
  • 【C++】,string类底层的模拟实现,C++中string类的模拟底层实现探究
  • uniapp 小程序实现微信授权登录(前端和后端),Uniapp小程序实现微信授权登录全流程(前端后端全攻略),Uniapp小程序微信授权登录全流程攻略,前端后端全指南
  • Vue脚手架的安装(保姆级教程),Vue脚手架保姆级安装教程,Vue脚手架保姆级安装指南,Vue脚手架保姆级安装指南,从零开始教你如何安装Vue脚手架
  • 如何在树莓派 Raspberry Pi中本地部署一个web站点并实现无公网IP远程访问,树莓派上本地部署Web站点及无公网IP远程访问指南,树莓派部署Web站点及无公网IP远程访问指南,本地部署与远程访问实践,树莓派部署Web站点及无公网IP远程访问实践指南,树莓派部署Web站点及无公网IP远程访问实践指南,本地部署与远程访问详解,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南,树莓派部署Web站点及无公网IP远程访问实践详解,本地部署与远程访问指南。
  • vue2技术栈实现AI问答机器人功能(流式与非流式两种接口方法),Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法探究,Vue2技术栈实现AI问答机器人功能,流式与非流式接口方法详解
  • 发表评论

    快捷回复:表情:
    评论列表 (暂无评论,0人围观)

    还没有评论,来说两句吧...

    目录[+]

    取消
    微信二维码
    微信二维码
    支付宝二维码