Ai加Flutter实现自定义标题栏(appBar)

Ai加Flutter实现自定义标题栏(appBar)

在这篇博客中,笔者会讲诉为什么要实现自定义标题栏 ,标题栏都有些什么功能 ,然后结合AI简单实现自定义标题栏!

话不多说,我们直接开始吧!

基础需求与环境准备

这篇文档默认你的设备已经配置了flutter环境且自身具备了如下:

  • 一定的Dart语法基础
  • 了解甚至熟悉其他桌面应用开发框架(比如:Qt以及其他)
  • 一个flutter空项目-> flutter create --empty your_project_name
  • 想要学会flutter的心

补充说明 : 安装flutter环境来这里 dart语法基础来这里 新手入门项目来这里

注意: 学习程度因人而异,如果满足的点越多,理论上学习效率越高!

笔者的环境:

  • Linux : 64位 fedora 43 gnome,桌面系统Wayland,内核版本Linux 6.17.9-300.fc43.x86_64
  • Flutter : Flutter 3.38.5
  • Dart : Dart 3.10.4
  • DevTools : 2.51.1
  • 大模型 : 通义千问网页版(Qwen3-Coder模型)

为什么要自定义标题栏

我们可以参考常见的应用vscode、QQ、微信等这些在(Windows、Linux和macOS)都有较强的统一风格,这就是自定义标题栏的目的------统一风格

插一嘴 :其实自定义标题栏可以避免大量因标题栏样式不统一而导致的问题(比如:gnome49的标题栏不支持暗色主题等)

怎么实现自定义标题栏

需求拆解

(第一性原理)------标题栏的构成

我们需要把这个看似复杂的问题简单化,实现自定义标题栏那我们就要知道标题栏 的构成:窗口图标 、窗口标题 、窗口按钮 (最小化、最大与还原、关闭)!见下图:

注意 : gnome 和 macOS不支持窗口图标!

(类比思维)------AppBar的构成

事实窗口标题栏和flutter中的appBar是吻合的,见下图

(需求转换)------隐藏系统默认标题栏使用AppBar

既然我们已经知道AppBar的功能可以平替窗口标题栏, 那么我们直接隐藏默认的使用AppBar自定义一个不就好了?

自定义标题栏------AppBar

(需求拆解)------隐藏与appBar实现对应功能

关闭系统默认的标题栏 标题栏与AppBar以及功能映射关系

  • 窗口图标 -> leading-> 显示窗口图标
  • 窗口标题 ->title-> 显示窗口标题
  • 窗口图标们 -> actions -> 显示对应图标以及窗口的隐藏,放大复原,关闭
(window_manager)------安装与导入自定义标题栏插件
  1. 安装window_manager
csharp 复制代码
# 终端执行如下:
flutter pub add window_manager
  1. 导入插件
arduino 复制代码
// 程序顶部添加如下
import 'package:window_manager/window_manager.dart';
(window_manager)------隐藏默认标题栏
  1. 替换程序入口代码块
csharp 复制代码
void main() {
  runApp(const MyApp());
}

替换为

csharp 复制代码
void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await windowManager.ensureInitialized();
  WindowOptions windowOptions = WindowOptions(
    titleBarStyle: TitleBarStyle.hidden,
  );
  windowManager.waitUntilReadyToShow(windowOptions, () async {
    await windowManager.focus();
  });

  runApp(const MainApp());
}

注意 : 确保你创建的是空项目 window_manager插件官网

显示效果

借助window_manager与appBar实现自定义标题栏
  1. 替换脚手架里的内容
css 复制代码
body: Center(child: Text('Hello World!'))
less 复制代码
//替换为
 appBar: AppBar(
          leading: Icon(Icons.favorite),
          title: Text("自定义标题栏"),
          actions: [
            IconButton(onPressed: () {}, icon: Icon(Icons.horizontal_rule)),
            IconButton(onPressed: () {}, icon: Icon(Icons.crop_square)),
            IconButton(onPressed: () {}, icon: Icon(Icons.close)),
          ],
        ),

显示效果

借助window_manager实现appBar窗口按钮功能
  1. 添加最小化窗口和关闭窗口
less 复制代码
// 替换为如下:
appBar: AppBar(
          leading: Icon(Icons.favorite),
          title: Text("自定义标题栏"),
          actions: [
            IconButton(
              onPressed: () {
                windowManager.minimize();
              },
              icon: Icon(Icons.horizontal_rule),
            ),
            IconButton(onPressed: () {}, icon: Icon(Icons.crop_square)),
            IconButton(
              onPressed: () {
                windowManager.close();
              },
              icon: Icon(Icons.close),
            ),
          ],
        ),
  1. 还原与最大化窗口
arduino 复制代码
// 将 StatelessWidget 转为StatefulWidget
// vscode 选中StatelessWidget 显示代码操作(ctrl.)StatefulWidget

// _MainAppState 初始化变量用于记录是否最大化
  bool _isMaximized = false

替换appBar为如下:

less 复制代码
         appBar: AppBar(
          leading: Icon(Icons.favorite),
          title: Text("自定义标题栏"),
          actions: [
            IconButton(
              onPressed: () {
                windowManager.minimize();
              },
              icon: Icon(Icons.horizontal_rule),
            ),
            IconButton(
              onPressed: () {
                if (_isMaximized) {
                  windowManager.unmaximize();
                  setState(() {
                    _isMaximized = !_isMaximized;
                  });
                } else {
                  windowManager.maximize();
                  setState(() {
                    _isMaximized = !_isMaximized;
                  });
                }
              },
              icon: _isMaximized
                  ? Icon(Icons.crop_free)
                  : Icon(Icons.crop_square),
            ),
            IconButton(
              onPressed: () {
                windowManager.close();
              },
              icon: Icon(Icons.close),
            ),
          ],
        ),

显示效果

借助window_manager实现拖动窗口

简单的办法 直接监听鼠标事件

scss 复制代码
//AppBar里添加如下
          flexibleSpace: Listener(
            onPointerDown: (_) => windowManager.startDragging(),
            child: Container(color: Colors.transparent),
          ),

光标带拖动效果 我的系统并不支持

less 复制代码
          flexibleSpace: MouseRegion(
            cursor: SystemMouseCursors.move,
            child: Listener(
              onPointerDown: (_) => windowManager.startDragging(),
              child: Container(color: Colors.transparent),
            ),
          ),

显示效果

注意:

  • 都是长按鼠标左键拖动窗口,右键鼠标 跟随光标移动
  • 一定要在Container 中添加颜色为透明 不然无法移动

最终的代码

less 复制代码
import 'package:flutter/material.dart';
import 'package:window_manager/window_manager.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await windowManager.ensureInitialized();
  WindowOptions windowOptions = WindowOptions(
    titleBarStyle: TitleBarStyle.hidden,
  );
  windowManager.waitUntilReadyToShow(windowOptions, () async {
    await windowManager.focus();
  });

  runApp(const MainApp());
}

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

  @override
  State<MainApp> createState() => _MainAppState();
}

class _MainAppState extends State<MainApp> {
  bool _isMaximized = false;

  void _toggleMaximize() {
    if (_isMaximized) {
      windowManager.unmaximize();
    } else {
      windowManager.maximize();
    }
    setState(() => _isMaximized = !_isMaximized);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          leading: Icon(Icons.favorite),
          title: Text('自定义标题栏'),
          flexibleSpace: Listener(
            onPointerDown: (event) => windowManager.startDragging(),
            child: Container(color: Colors.transparent),
          ),
          actions: [
            IconButton(
              icon: Icon(Icons.horizontal_rule),
              onPressed: windowManager.minimize,
            ),
            IconButton(
              icon: Icon(_isMaximized ? Icons.crop_free : Icons.crop_square),
              onPressed: _toggleMaximize,
            ),
            IconButton(icon: Icon(Icons.close), onPressed: windowManager.close),
          ],
        ),
        body: Center(child: Text('Hello, World!')),
      ),
    );
  }
}

AI询问过程

通义AI对话过程

相关推荐
穆梓兰煊28 分钟前
Flutter端云协同推理架构解析
flutter·github
lovingsoft29 分钟前
Chrome Lighthouse 详解
前端·chrome
Qlly39 分钟前
全栈工程师怎么接需求?从一句「加个功能」聊到能开发、能验收
前端·后端
马可家的菠萝43 分钟前
你的微信“文件传输助手”,是不是已经成了第二个网盘?
前端·后端·架构
略略略啦43 分钟前
Vibe Coding 基于Spec DD和TDD的实践指南
前端
shmily麻瓜小菜鸡1 小时前
前端实现图片与PDF文字识别敏感信息打码方案
前端·pdf·状态模式
打工仔折腾 AI1 小时前
LangChain 工具调用实战:从 Tool 定义到 Agent 执行链路拆解
java·前端·人工智能·后端·python·langchain·ai agent 实战
汉堡大王95271 小时前
单次输出拉到100万Token:拆完 Gemini 4 Argon 的续写协议,才看懂谷歌这7个月在憋什么
前端·人工智能·后端
看浪的路人1 小时前
第9讲:大模型应用监控与可观测性——OpenTelemetry + Prometheus + Grafana 全链路追踪
java·前端·spring
内存过载1 小时前
用表格模拟甘特图:项目排期的土办法
前端·低代码