Flutter Windows如何创建透明窗口实现自定义形状圆角界面
Flutter Windows 透明圆角自定义窗口实现方案
以下方案可直接实现全透明、自定义形状/圆角的无边框窗口,完全满足悬浮式仪表盘类UI的开发需求。
前置依赖准备
使用社区维护最稳定的window_manager插件做窗口控制,避免直接写大量Win32 API兼容代码。
在pubspec.yaml中添加依赖:
dependencies: flutter: sdk: flutter window_manager: ^0.3.9
第一步:原生层配置(解决黑边核心步骤)
Flutter默认的Windows窗口模板带原生边框、不透明背景,必须先修改原生配置,否则会出现启动黑边、透明失效问题。
打开windows/runner/main.cpp文件,做两处修改:
- 修改窗口类注册配置,将背景刷设为空
WNDCLASS window_class = {}; // 其他配置保持不变,仅修改hbrBackground字段 window_class.hbrBackground = nullptr;
- 修改窗口创建样式,替换原有的带边框窗口样式
HWND window = CreateWindow( window_class_name, title.c_str(), WS_POPUP, // 替换原有的WS_OVERLAPPEDWINDOW等默认样式,移除原生边框/标题栏 CW_USEDEFAULT, CW_USEDEFAULT, width, height, nullptr, nullptr, instance, this); // 开启分层窗口透明属性 SetLayeredWindowAttributes(window, RGB(0,0,0), 255, LWA_ALPHA);
第二步:Flutter层窗口初始化
在main.dart中初始化窗口配置,开启透明、无边框属性,等首帧渲染完成再显示窗口,避免启动闪烁:
import 'package:flutter/material.dart'; import 'package:window_manager/window_manager.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await windowManager.ensureInitialized(); const WindowOptions windowOptions = WindowOptions( size: Size(1280, 820), transparent: true, frameless: true, center: true, backgroundColor: Colors.transparent, skipTaskbar: false, titleBarStyle: TitleBarStyle.hidden, ); windowManager.waitUntilReadyToShow(windowOptions, () async { await windowManager.show(); await windowManager.focus(); }); runApp(const MyApp()); }
第三步:自定义窗口形状与交互
透明窗口开启后,Flutter渲染内容之外的区域会完全透过去,你可以自由绘制任意形状的窗口:
- 圆角矩形窗口:根容器设置圆角+裁剪,加外阴影模拟悬浮感
- 异形窗口:用
ClipPath包裹根组件,自定义绘制路径即可实现任意非矩形形状 - 交互补全:替换原生标题栏的拖拽、缩放、窗口操作按钮功能
最小可运行的圆角窗口示例代码:
class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( // *注意:从MaterialApp到根容器的所有父级组件,背景必须设为透明* backgroundColor: Colors.transparent, body: Container( decoration: BoxDecoration( // 窗口主背景色,可替换为渐变、图片等任意样式 color: const Color(0xFF1C1C28), // 自定义圆角大小 borderRadius: BorderRadius.circular(24), // 外阴影增强悬浮质感,和仪表盘类UI的视觉效果匹配 boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.35), blurRadius: 50, spreadRadius: 6, ) ], ), // 必须开启抗锯齿裁剪,否则圆角边缘会有硬锯齿 clipBehavior: Clip.antiAlias, child: Stack( children: [ Column( children: [ // 顶部可拖拽标题栏 DragToMoveArea( child: Container( height: 56, padding: const EdgeInsets.symmetric(horizontal: 24), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( "个人财务仪表盘", style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w500), ), // 窗口操作按钮 Row( children: [ IconButton( onPressed: () => windowManager.minimize(), icon: const Icon(Icons.remove, color: Colors.white70, size: 20), ), IconButton( onPressed: () => windowManager.maximize(), icon: const Icon(Icons.crop_din, color: Colors.white70, size: 18), ), IconButton( onPressed: () => windowManager.close(), icon: const Icon(Icons.close, color: Colors.white70, size: 20), ), ], ) ], ), ), ), // 下方写你的业务页面内容即可,毛玻璃、渐变等效果都可以正常渲染 const Expanded( child: Center( child: Text("页面内容区域", style: TextStyle(color: Colors.white70)), ), ) ], ), // 窗口边缘拖拽缩放区域,实现和原生窗口一致的边缘拉伸缩放效果 const ResizableBorderArea( enableResizeEdges: ResizeEdge.all, child: SizedBox.expand(), ) ], ), ), ), ); } }
常见问题排查
- 窗口出现方形不透明底色:检查从
MaterialApp、Scaffold到根容器之间的所有组件,是否设置了不透明的backgroundColor,全部改为透明即可 - 圆角边缘有白/黑边:确认根容器开启了
Clip.antiAlias裁剪,同时不要调用系统Dwm的圆角接口,避免系统绘制的圆角和Flutter绘制的圆角重叠 - 窗口点击穿透:检查原生窗口样式是否误加了
WS_EX_TRANSPARENT属性,或者误调用了windowManager.setIgnoreMouseEvents(true),移除即可 - 启动闪黑/白屏:必须使用
waitUntilReadyToShow方法,等Flutter首帧渲染完成后再显示窗口,不要提前调用show方法
内容的提问来源于stack exchange,提问作者RHS.Dev
相关产品推荐
相关产品推荐

