文章目录
- Ai加Flutter实现自定义标题栏(appBar)
- 基础需求与环境准备
- 为什么要自定义标题栏
- 怎么实现自定义标题栏
- 需求拆解
- (第一性原理)——标题栏的构成
- (类比思维)——AppBar的构成
- (需求转换)——隐藏系统默认标题栏使用AppBar
- 自定义标题栏——AppBar
- (需求拆解)——隐藏与appBar实现对应功能
- (window_manager)——安装与导入自定义标题栏插件
- (window_manager)——隐藏默认标题栏
- 借助window_manager与appBar实现自定义标题栏
- 借助window_manager实现appBar窗口按钮功能
- 借助window_manager实现拖动窗口
- 最终的代码
- AI询问过程
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)——安装与导入自定义标题栏插件
- 安装
window_manager
# 终端执行如下:flutter pubaddwindow_manager- 导入插件
// 程序顶部添加如下import'package:window_manager/window_manager.dart';(window_manager)——隐藏默认标题栏
- 替换程序入口代码块
voidmain(){runApp(constMyApp());}替换为
voidmain()async{WidgetsFlutterBinding.ensureInitialized();awaitwindowManager.ensureInitialized();WindowOptions windowOptions=WindowOptions(titleBarStyle:TitleBarStyle.hidden,);windowManager.waitUntilReadyToShow(windowOptions,()async{awaitwindowManager.focus();});runApp(constMainApp());}注意: 确保你创建的是空项目
window_manager插件官网
借助window_manager与appBar实现自定义标题栏
- 替换脚手架里的内容
body:Center(child:Text('Hello World!'))//替换为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窗口按钮功能
- 添加最小化窗口和关闭窗口
// 替换为如下: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),),],),- 还原与最大化窗口
// 将 StatelessWidget 转为StatefulWidget// vscode 选中StatelessWidget 显示代码操作(ctrl.)StatefulWidget// _MainAppState 初始化变量用于记录是否最大化bool _isMaximized=false替换appBar为如下:
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实现拖动窗口
简单的办法直接监听鼠标事件
//AppBar里添加如下flexibleSpace:Listener(onPointerDown:(_)=>windowManager.startDragging(),child:Container(color:Colors.transparent),),光标带拖动效果我的系统并不支持
flexibleSpace:MouseRegion(cursor:SystemMouseCursors.move,child:Listener(onPointerDown:(_)=>windowManager.startDragging(),child:Container(color:Colors.transparent),),),显示效果
注意:
- 都是长按鼠标左键拖动窗口,右键鼠标 跟随光标移动
- 一定要在Container 中添加颜色为透明 不然无法移动
最终的代码
import'package:flutter/material.dart';import'package:window_manager/window_manager.dart';voidmain()async{WidgetsFlutterBinding.ensureInitialized();awaitwindowManager.ensureInitialized();WindowOptions windowOptions=WindowOptions(titleBarStyle:TitleBarStyle.hidden,);windowManager.waitUntilReadyToShow(windowOptions,()async{awaitwindowManager.focus();});runApp(constMainApp());}classMainAppextendsStatefulWidget{constMainApp({super.key});@overrideState<MainApp>createState()=>_MainAppState();}class_MainAppStateextendsState<MainApp>{bool _isMaximized=false;void_toggleMaximize(){if(_isMaximized){windowManager.unmaximize();}else{windowManager.maximize();}setState(()=>_isMaximized=!_isMaximized);}@overrideWidgetbuild(BuildContext context){returnMaterialApp(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对话过程