ai + fluent_ui 实现自定义winUI风格窗口

这是一个使用 Fluent UIwindow_manager 插件实现的跨平台统一 Windows UI 风格的示例程序。本文档将对这些技术进行详细介绍,帮助初学者理解项目的核心概念。

完整代码与效果预览

light
light一个示例面板
示例面板

效果预览

本项目实现了以下效果:

效果 说明
自定义 WinUI 风格标题栏 隐藏系统原生标题栏,使用 Fluent UI 组件自定义标题栏
窗口控制按钮 包含最小化、最大化/还原、关闭按钮
Windows 11 风格 UI 使用 Fluent UI 组件构建的现代化界面
导航侧边栏 支持折叠/展开的侧边栏导航
流畅动画 窗口最大/最小化动画、导航切换动画

项目完整代码

1️⃣ 主入口文件 (lib/winui_title_bar.dart)

这是项目的核心入口文件,实现了自定义窗口标题栏和 Fluent UI 导航布局:

import 'package:fluent_ui/fluent_ui.dart';
import 'package:window_manager/window_manager.dart';

void main() async {
  // 1. 确保 Flutter 绑定已初始化
  WidgetsFlutterBinding.ensureInitialized();

  // 2. 初始化 windowManager
  await windowManager.ensureInitialized();

  // 3. 配置窗口选项:隐藏系统标题栏
  WindowOptions windowOptions = WindowOptions(
    titleBarStyle: TitleBarStyle.hidden,
  );

  // 4. 等待窗口准备好后显示
  windowManager.waitUntilReadyToShow(windowOptions, () async {
    await windowManager.focus();
  });

  // 5. 启动应用
  runApp(const MyApp());
}

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

  
  Widget build(BuildContext context) {
    return FluentApp(
      debugShowCheckedModeBanner: false,
      title: 'Fluent UI Demo',
      // 浅色主题
      theme: FluentThemeData(
        brightness: Brightness.light,
        accentColor: Colors.blue,
      ),
      // 深色主题
      darkTheme: FluentThemeData(
        brightness: Brightness.dark,
        accentColor: Colors.blue,
      ),
      home: const NavigationViewDemo(),
    );
  }
}

class NavigationViewDemo extends StatefulWidget {
  const NavigationViewDemo({super.key});

  
  State<NavigationViewDemo> createState() => _NavigationViewDemoState();
}

class _NavigationViewDemoState extends State<NavigationViewDemo> {
  int _currentIndex = 0;
  bool _isMaximized = false;

  // 切换最大化/还原状态
  void _toggleMaximize() {
    if (_isMaximized) {
      windowManager.unmaximize();
    } else {
      windowManager.maximize();
    }
    setState(() => _isMaximized = !_isMaximized);
  }

  
  Widget build(BuildContext context) {
    return NavigationView(
      // 自定义应用栏(标题栏)
      appBar: NavigationAppBar(
        // 标题区域:使用 DragToMoveArea 允许拖动窗口
        title: DragToMoveArea(
          child: Container(
            alignment: Alignment.centerLeft,
            child: const Text('Fluent UI 导航演示'),
          ),
        ),
        // 右侧窗口控制按钮
        actions: Center(
          child: Row(
            spacing: 20.0,
            verticalDirection: VerticalDirection.down,
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              // 最小化按钮
              IconButton(
                icon: WindowsIcon(FluentIcons.chrome_minimize),
                onPressed: windowManager.minimize,
              ),
              // 最大化/还原按钮
              IconButton(
                icon: WindowsIcon(
                  _isMaximized
                      ? FluentIcons.chrome_restore
                      : FluentIcons.grid_view_large,
                ),
                onPressed: _toggleMaximize,
              ),
              // 关闭按钮
              IconButton(
                icon: WindowsIcon(FluentIcons.chrome_close),
                onPressed: windowManager.close,
              ),
              SizedBox(), // 占位符
            ],
          ),
        ),
      ),
      // 导航面板
      pane: NavigationPane(
        selected: _currentIndex,
        onChanged: (index) => setState(() => _currentIndex = index),
        displayMode: PaneDisplayMode.auto, // 自动适应宽度
        items: [
          // 首页导航项
          PaneItem(
            icon: const Icon(FluentIcons.home),
            title: const Text('首页'),
            body: const Center(child: Text('欢迎来到首页')),
          ),
          // 设置导航项
          PaneItem(
            icon: const Icon(FluentIcons.settings),
            title: const Text('设置'),
            body: const Center(child: Text('这里是设置页面')),
          ),
        ],
      ),
    );
  }
}
2️⃣ 组件演示文件 (lib/main.dart)

这个文件展示了 Fluent UI 的各种基础组件和高级组件的用法:

import 'package:fluent_ui/fluent_ui.dart';

void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  // 状态变量
  bool checked = false;
  bool checkboxValue = false;
  double sliderValue = 50.0;
  bool disabled = false;
  AccentColor splitButtonColor = Colors.blue;

  // 全局 Key 用于访问组件状态
  final key = GlobalKey<MenuBarState>();
  final splitButtonKey = GlobalKey<SplitButtonState>();
  final expanderKey = GlobalKey<ExpanderState>(debugLabel: 'Expander key');

  
  Widget build(BuildContext context) {
    return FluentApp(
      title: 'Windows UI Layout',
      theme: FluentThemeData(
        brightness: Brightness.light,
        accentColor: Colors.blue,
      ),
      home: ScaffoldPage(
        header: const PageHeader(title: Text('分层布局示例')),
        content: SingleChildScrollView(
          padding: const EdgeInsets.all(20.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              // 菜单栏
              MenuBar(
                key: key,
                items: [
                  MenuBarItem(
                    title: 'File',
                    items: [
                      MenuFlyoutItem(text: const Text('New'), onPressed: () {}),
                      MenuFlyoutItem(text: const Text('Open'), onPressed: () {}),
                      MenuFlyoutItem(text: const Text('Save'), onPressed: () {}),
                      MenuFlyoutItem(text: const Text('Exit'), onPressed: () {}),
                    ],
                  ),
                  MenuBarItem(
                    title: 'Edit',
                    items: [
                      MenuFlyoutItem(text: const Text('Cut'), onPressed: () {}),
                      MenuFlyoutItem(text: const Text('Copy'), onPressed: () {}),
                      MenuFlyoutItem(text: const Text('Paste'), onPressed: () {}),
                    ],
                  ),
                  MenuBarItem(
                    title: 'Help',
                    items: [
                      MenuFlyoutItem(
                        text: const Text('About'),
                        onPressed: () {},
                      ),
                    ],
                  ),
                ],
              ),

              const SizedBox(height: 10),

              // ===== 第一部分:基础控制组件 =====
              const Text('基础控制', style: TextStyle(fontWeight: FontWeight.bold)),
              const SizedBox(height: 10),

              Wrap(
                spacing: 15.0,
                runSpacing: 15.0,
                crossAxisAlignment: WrapCrossAlignment.center,
                children: [
                  // 标准按钮
                  Button(child: const Text('标准按钮'), onPressed: () {}),
                  // 填充按钮
                  FilledButton(child: const Text('填充按钮'), onPressed: () {}),
                  // 切换开关
                  ToggleButton(
                    checked: checked,
                    onChanged: (v) => setState(() => checked = v),
                    child: const Text('切换开关'),
                  ),
                  // 复选框
                  Checkbox(
                    checked: checkboxValue,
                    onChanged: (v) => setState(() => checkboxValue = v ?? false),
                    content: const Text('复选框'),
                  ),
                  // 图标按钮
                  IconButton(
                    icon: const Icon(FluentIcons.disable_updates, size: 16.0),
                    onPressed: () => debugPrint('pressed'),
                  ),
                  // 下拉菜单按钮
                  DropDownButton(
                    title: const Text('编程语言'),
                    items: [
                      MenuFlyoutItem(text: const Text('Python'), onPressed: () {}),
                      const MenuFlyoutSeparator(),
                      MenuFlyoutItem(text: const Text('C++'), onPressed: null),
                      MenuFlyoutItem(text: const Text('C#'), onPressed: () {}),
                    ],
                  ),
                  // 拆分按钮
                  SplitButton(
                    key: splitButtonKey,
                    enabled: !disabled,
                    child: Container(
                      height: 32,
                      width: 36,
                      decoration: BoxDecoration(
                        color: splitButtonColor,
                        borderRadius: const BorderRadiusDirectional.horizontal(
                          start: Radius.circular(4.0),
                        ),
                      ),
                    ),
                    flyout: FlyoutContent(
                      constraints: const BoxConstraints(maxWidth: 200.0),
                      child: Wrap(
                        runSpacing: 10.0,
                        spacing: 8.0,
                        children: Colors.accentColors.map((color) {
                          return Button(
                            style: const ButtonStyle(
                              padding: WidgetStatePropertyAll(EdgeInsets.all(4.0)),
                            ),
                            onPressed: () {
                              setState(() => splitButtonColor = color);
                              Navigator.of(context).pop();
                            },
                            child: Container(
                              height: 32,
                              width: 32,
                              color: color,
                            ),
                          );
                        }).toList(),
                      ),
                    ),
                  ),
                ],
              ),

              const SizedBox(height: 30),
              const Divider(),
              const SizedBox(height: 30),

              // ===== 第二部分:高级组件 =====
              const Text('高级组件', style: TextStyle(fontWeight: FontWeight.bold)),
              const SizedBox(height: 10),

              // 滑块
              Slider(
                label: '${sliderValue.toInt()}%',
                value: sliderValue,
                onChanged: (v) => setState(() => sliderValue = v),
              ),

              const SizedBox(height: 20),

              // 信息提示条
              const InfoBar(
                title: Text('系统提示'),
                content: Text('这是强制换行后放置在底部的长内容区域。'),
                severity: InfoBarSeverity.info,
              ),

              const SizedBox(height: 20),

              // 开关
              ToggleSwitch(
                checked: checked,
                onChanged: disabled ? null : (v) => setState(() => checked = v),
              ),

              // 可展开面板
              Expander(
                leading: RadioButton(
                  checked: checked,
                  onChanged: (v) => setState(() => checked = v),
                ),
                header: Text('This text is in header'),
                content: Text('This text is in content'),
              ),

              Expander(
                header: Text('Open to see the scrollable text'),
                content: SizedBox(
                  height: 300,
                  child: SingleChildScrollView(
                    child: Text('A LONG TEXT HERE' * 20),
                  ),
                ),
              ),

              // 日历视图
              CalendarView(
                selectionMode: CalendarViewSelectionMode.single,
                onSelectionChanged: (value) {
                  debugPrint('${value.selectedDates}');
                },
                isOutOfScopeEnabled: false,
                isGroupLabelVisible: false,
                locale: const Locale('zh'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
3️⃣ 项目依赖配置 (pubspec.yaml)
name: flent_ui_app
description: "A new Flutter project."
publish_to: 'none'
version: 0.1.0

environment:
  sdk: ^3.10.4

dependencies:
  flutter:
    sdk: flutter
  fluent_ui: ^4.13.0      # Fluent UI 组件库
  window_manager: ^0.5.1   # 窗口管理插件

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^6.0.0

flutter:
  uses-material-design: true

如何运行项目

# 1. 进入项目目录
cd flent_ui_app

# 2. 获取依赖
flutter pub get

# 3. 运行项目(Windows 平台)
flutter run -d windows

目录

  1. 什么是 Fluent UI?
  2. 什么是 window_manager?
  3. 什么是 WinUI?
  4. 跨平台支持
  5. 项目代码结构解析

1. Fluent UI 是什么?

1.1 基本概念

Fluent UI 是微软官方设计系统的 Flutter 实现版本。它提供了一套遵循微软 Fluent Design System 设计规范的 UI 组件库,让 Flutter 应用能够拥有与 Windows 11 原生应用一致的视觉效果和交互体验。

简单来说:如果你想让你的 Flutter 应用看起来像 Windows 原生应用,使用 Fluent UI 就对了!

1.2 官方资源

资源类型 链接
Pub.dev 包地址 https://pub.dev/packages/fluent_ui
GitHub 仓库 https://github.com/bdlukaa/fluent_ui
官方文档 https://docs.flutterflow.io/resources/ui-and-interactions/widgets/widget-catalog/fluent-ui-widgets

1.3 核心特性

Fluent UI 提供了丰富的 Windows 风格组件,包括但不限于:

组件类别 示例组件 说明
按钮类 ButtonFilledButtonToggleButton 标准按钮、填充按钮、开关按钮
输入类 CheckboxTextBoxRadioButton 复选框、文本框、单选按钮
导航类 NavigationViewPaneTabView 导航视图、侧边栏、标签页
信息展示 InfoBarTooltipProgressBar 信息提示条、进度条
布局类 ExpanderSplitButtonFlyout 可展开面板、下拉菜单
日期时间 CalendarViewTimePicker 日历视图、时间选择器

1.4 基本使用方式

安装依赖:

dependencies:
  fluent_ui: ^4.13.0

基本使用示例:

import 'package:fluent_ui/fluent_ui.dart';

// 使用 FluentApp 作为根组件
FluentApp(
  title: '我的应用',
  theme: FluentThemeData(
    brightness: Brightness.light,    // 浅色主题
    accentColor: Colors.blue,        // 主题色
  ),
  home: HomePage(),
)

使用 Fluent UI 组件:

// 按钮组件
Button(
  child: Text('点击我'),
  onPressed: () => print('点击事件'),
)

// 复选框
Checkbox(
  checked: isChecked,
  onChanged: (value) => setState(() => isChecked = value),
)

// 导航视图
NavigationView(
  pane: NavigationPane(
    items: [
      PaneItem(icon: Icon(FluentIcons.home), title: Text('首页')),
      PaneItem(icon: Icon(FluentIcons.settings), title: Text('设置')),
    ],
  ),
)

2. window_manager 是什么?

2.1 基本概念

window_manager 是一个 Flutter 插件,专门用于控制桌面应用程序窗口的行为和外观。它允许开发者自定义窗口标题栏、控制窗口大小、位置、最大/最小化等操作,实现更灵活的桌面应用体验。

简单来说:如果你想让你的 Flutter 桌面应用有自定义的标题栏、可以自由拖动、缩放窗口,window_manager 就是你需要的工具!

2.2 官方资源

资源类型 链接
Pub.dev 包地址 https://pub.dev/packages/window_manager
GitHub 仓库 https://github.com/leanflutter/window_manager

2.3 核心功能

功能 说明 代码示例
隐藏标题栏 使用自定义标题栏替换系统默认 TitleBarStyle.hidden
窗口控制 最小化、最大化、关闭窗口 windowManager.minimize()
窗口状态 判断是否最大化 windowManager.isMaximized()
窗口大小 获取或设置窗口尺寸 windowManager.setSize(Size(800, 600))
窗口位置 获取或设置窗口位置 windowManager.setPosition(Offset(100, 100))
置顶显示 保持窗口在其他应用之上 windowManager.setAlwaysOnTop(true)
窗口事件 监听窗口状态变化 windowManager.addListener(...)

2.4 基本使用方式

安装依赖:

dependencies:
  window_manager: ^0.5.1

初始化配置:

import 'package:window_manager/window_manager.dart';

void main() async {
  // 确保 Flutter 绑定已初始化
  WidgetsFlutterBinding.ensureInitialized();

  // 初始化 windowManager
  await windowManager.ensureInitialized();

  // 配置窗口选项
  WindowOptions windowOptions = WindowOptions(
    titleBarStyle: TitleBarStyle.hidden,  // 隐藏系统标题栏
    backgroundColor: Colors.transparent,  // 背景透明
  );

  // 等待窗口准备好后显示
  windowManager.waitUntilReadyToShow(windowOptions, () async {
    await windowManager.focus();
  });

  runApp(const MyApp());
}

在应用中使用窗口控制:

// 创建自定义标题栏
NavigationAppBar(
  title: DragToMoveArea(  // 允许拖动窗口
    child: Container(
      alignment: Alignment.centerLeft,
      child: Text('自定义标题栏'),
    ),
  ),
  actions: Row(
    children: [
      // 最小化按钮
      IconButton(
        icon: Icon(FluentIcons.chrome_minimize),
        onPressed: windowManager.minimize,
      ),
      // 最大化/还原按钮
      IconButton(
        icon: Icon(FluentIcons.grid_view_large),
        onPressed: () async {
          if (await windowManager.isMaximized()) {
            windowManager.unmaximize();
          } else {
            windowManager.maximize();
          }
        },
      ),
      // 关闭按钮
      IconButton(
        icon: Icon(FluentIcons.chrome_close),
        onPressed: windowManager.close,
      ),
    ],
  ),
)

3. WinUI 是什么?

3.1 基本概念

WinUIWindows UI Library 的缩写,是微软官方的原生 Windows 应用界面库。它是 Fluent Design System 的核心实现,用于构建现代化、美观的 Windows 桌面应用。

3.2 WinUI 的特点

特性 说明
原生体验 拥有最正宗的 Windows 11 视觉风格和交互效果
高性能 直接使用 Windows 原生 API,无需额外抽象层
现代设计 支持圆角、亚克力/云母材质效果、毛玻璃动画
持续更新 跟随 Windows 系统更新,不断添加新特性
官方支持 微软官方维护,有完善的技术支持

3.3 Fluent UI 与 WinUI 的关系

┌─────────────────────────────────────────────────────────┐
│                      你的 Flutter 应用                    │
├─────────────────────────────────────────────────────────┤
│                    Fluent UI (Flutter)                   │
│         提供与 WinUI 一致的组件和行为表现                  │
├─────────────────────────────────────────────────────────┤
│              window_manager (Flutter 插件)               │
│         控制窗口行为:拖动、最大/最小化等                   │
├─────────────────────────────────────────────────────────┤
│              Windows 原生平台 (WinUI)                    │
│              Fluent Design System 官方实现                │
└─────────────────────────────────────────────────────────┘

3.4 为什么选择 Fluent UI?

优势 说明
跨平台 一套代码可在 Windows、macOS、Linux、Web 上运行
Flutter 生态 可使用 Flutter 丰富的插件和工具
原生外观 视觉效果与原生 WinUI 应用非常接近
开发效率 热重载、声明式 UI 等 Flutter 优势

4. 跨平台支持

4.1 Fluent UI 的跨平台支持

Fluent UI 主要是 Windows 风格 的 UI 库,但它在设计上考虑了多平台支持:

平台 支持程度 说明
Windows ✅ 完全支持 官方主要支持平台,效果最佳
macOS ✅ 支持 有一定的适配,部分组件可用
Linux ✅ 支持 有一定的适配,部分组件可用
Web ⚠️ 部分支持 实验性支持,功能受限
iOS/Android ❌ 不推荐 风格不匹配,不建议使用

4.2 window_manager 的跨平台支持

平台 支持程度 说明
Windows ✅ 完全支持 所有功能可用
macOS ✅ 完全支持 所有功能可用
Linux ✅ 完全支持 所有功能可用
Web ❌ 不支持 浏览器环境限制
iOS/Android ❌ 不支持 移动端不适用

4.3 本项目的跨平台特性

本项目通过以下组合实现跨平台开发:

┌──────────────────────────────────────────────────────────┐
│                    共享业务逻辑层                          │
│           (Dart 语言,一套代码多端运行)                     │
├──────────────────┬─────────────────┬─────────────────────┤
│   Windows 平台   │   macOS 平台    │     Linux 平台      │
│  fluent_ui +     │  fluent_ui +    │   fluent_ui +       │
│  window_manager  │  window_manager │   window_manager    │
├──────────────────┴─────────────────┴─────────────────────┤
│              统一的 Windows 11 风格界面                    │
└──────────────────────────────────────────────────────────┘

4.4 平台特定配置

Windows 平台配置 (本项目示例):

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // Windows 特有:隐藏标题栏使用自定义标题栏
  await windowManager.ensureInitialized();
  WindowOptions windowOptions = WindowOptions(
    titleBarStyle: TitleBarStyle.hidden,
  );
  windowManager.waitUntilReadyToShow(windowOptions, () async {
    await windowManager.focus();
  });
  
  runApp(const MyApp());
}

多平台条件编译示例:

import 'package:flutter/foundation.dart' show kIsWeb;

// 根据不同平台显示不同 UI
Widget buildAdaptiveLayout() {
  if (kIsWeb) {
    // Web 平台特殊处理
    return WebLayout();
  } else {
    // 桌面平台使用 Fluent UI
    return NavigationView(
      pane: NavigationPane(items: [...]),
    );
  }
}

项目代码结构解析

5.1 项目文件说明

文件路径 功能说明
lib/main.dart 应用入口,包含基础 Fluent UI 组件演示
lib/winui_title_bar.dart 自定义 WinUI 风格标题栏实现
pubspec.yaml 项目依赖配置

5.2 核心代码解析

入口文件 (winui_title_bar.dart):

void main() async {
  // 1. 初始化 Flutter 绑定
  WidgetsFlutterBinding.ensureInitialized();

  // 2. 初始化窗口管理器
  await windowManager.ensureInitialized();

  // 3. 配置窗口选项(隐藏系统标题栏)
  WindowOptions windowOptions = WindowOptions(
    titleBarStyle: TitleBarStyle.hidden,
  );

  // 4. 等待窗口准备好后显示
  windowManager.waitUntilReadyToShow(windowOptions, () async {
    await windowManager.focus();
  });

  // 5. 启动应用
  runApp(const MyApp());
}

自定义标题栏实现:

NavigationView(
  appBar: NavigationAppBar(
    // 使用 DragToMoveArea 允许拖动窗口
    title: DragToMoveArea(
      child: Container(
        alignment: Alignment.centerLeft,
        child: const Text('Fluent UI 导航演示'),
      ),
    ),
    // 自定义窗口控制按钮
    actions: Row(
      children: [
        IconButton(
          icon: Icon(FluentIcons.chrome_minimize),
          onPressed: windowManager.minimize,
        ),
        IconButton(
          icon: Icon(FluentIcons.grid_view_large),
          onPressed: _toggleMaximize,
        ),
        IconButton(
          icon: Icon(FluentIcons.chrome_close),
          onPressed: windowManager.close,
        ),
      ],
    ),
  ),
  // 导航面板
  pane: NavigationPane(
    items: [
      PaneItem(icon: Icon(FluentIcons.home), title: Text('首页')),
      PaneItem(icon: Icon(FluentIcons.settings), title: Text('设置')),
    ],
  ),
)

总结

本项目展示了如何使用 Fluent UIwindow_manager 构建具有 Windows 11 风格的跨平台桌面应用:

  1. Fluent UI 提供了丰富的 Windows 风格组件,让应用拥有原生外观
  2. window_manager 提供了窗口控制能力,支持自定义标题栏
  3. 结合两者,可以在 Windows、macOS、Linux 上实现统一的 UI 体验

这种开发方式既保留了 Flutter 的跨平台优势,又实现了原生 Windows 应用的视觉效果,是一个非常实用的技术组合!

参考链接汇总

资源 链接
Fluent UI Pub.dev https://pub.dev/packages/fluent_ui
Fluent UI GitHub https://github.com/bdlukaa/fluent_ui
window_manager Pub.dev https://pub.dev/packages/window_manager
window_manager GitHub https://github.com/leanflutter/window_manager
Fluent Design System https://fluent.microsoft.com/
Flutter 官网 https://flutter.dev/
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐