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

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

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

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

基础需求与环境准备

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

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

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

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

笔者的环境:

  • Linux : 64fedora 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模型)

为什么要自定义标题栏

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

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

怎么实现自定义标题栏

需求拆解

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

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

注意 : gnomemacOS不支持窗口图标!

(类比思维)------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对话过程

相关推荐
布列瑟农的星空2 小时前
SSE与流式传输(Streamable HTTP)
前端·后端
GISer_Jing2 小时前
跨境营销前端AI应用业务领域
前端·人工智能·aigc
oak隔壁找我2 小时前
Node.js的package.json
前端·javascript
talenteddriver2 小时前
web: http请求(自用总结)
前端·网络协议·http
全栈派森2 小时前
Flutter 实战:基于 GetX + Obx 的企业级架构设计指南
前端·flutter
Awu12272 小时前
Vue3自定义渲染器:原理剖析与实践指南
前端·vue.js·three.js
支撑前端荣耀2 小时前
从零实现前端监控告警系统:SMTP + Node.js + 个人邮箱 完整免费方案
前端·javascript·面试
进击的野人2 小时前
Vue.js 插槽机制深度解析:从基础使用到高级应用
前端·vue.js·前端框架
重铸码农荣光2 小时前
🎯 从零搭建一个 React Todo 应用:父子通信、状态管理与本地持久化全解析!
前端·react.js·架构