You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Flutter Windows桌面应用实现类似微软时钟专注会话悬浮窗口的技术咨询

Flutter Windows桌面应用实现类似微软时钟专注会话悬浮窗口的技术咨询

我之前在做Flutter Windows桌面项目时,也踩过window_manager的不少坑——要么样式和预期差太远,要么偶尔出现莫名其妙的bug,太懂你的感受了!下面针对你的问题逐一给出解决方案和实践建议:

一、先搞定window_manager的无边框+悬浮(如果还想尝试的话)

可能你之前的配置没到位,试试下面的标准流程,亲测能解决大部分基础问题:

  • 首先在pubspec.yaml里确保依赖最新版的window_manager
  • 初始化时必须配置权限(Windows上需要),然后设置核心属性:
import 'package:window_manager/window_manager.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // 必须先初始化窗口管理权限
  await windowManager.ensureInitialized();
  
  WindowOptions windowOptions = const WindowOptions(
    size: Size(300, 200),
    center: false,
    alignment: Alignment.topRight, // 悬浮在右上角
    title: "悬浮计时器",
    backgroundColor: Colors.transparent, // 如果需要半透明背景
    skipTaskbar: true, // 不在任务栏显示
    titleBarStyle: TitleBarStyle.hidden, // 隐藏标题栏(实现无边框)
  );
  
  windowManager.waitUntilReadyToShow(windowOptions, () async {
    await windowManager.show();
    await windowManager.focus();
    await windowManager.setAlwaysOnTop(true); // 置顶悬浮
    await windowManager.setMovable(true); // 允许拖动窗口
  });
  
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: Scaffold(body: Center(child: Text("主窗口"))));
  }
}

如果还是出现bug,大概率是window_manager在某些Windows 11版本上的兼容性问题,直接换下面的替代包更省心。

二、多窗口实现方案

不管是window_manager还是替代包,都支持多窗口:

  • window_manager的话,每个新窗口需要单独创建WindowController,重复上面的配置流程即可
  • 更推荐用下文的bitsdojo_window,多窗口管理更直观,示例代码:
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:flutter/material.dart';

// 主窗口初始化
void main() {
  runApp(const MyApp());
  doWhenWindowReady(() {
    final mainWindow = appWindow;
    mainWindow.size = const Size(800, 600);
    mainWindow.center();
    mainWindow.title = "主应用";
    mainWindow.show();
  });
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: ElevatedButton(
            onPressed: openFloatingTimer,
            child: const Text("打开悬浮计时器"),
          ),
        ),
      ),
    );
  }
}

// 创建第二个悬浮窗口
void openFloatingTimer() {
  final timerWindow = WindowController();
  timerWindow.create();
  timerWindow.size = const Size(300, 200);
  timerWindow.borderless = true;
  timerWindow.alwaysOnTop = true;
  timerWindow.alignment = Alignment.topRight;
  timerWindow.skipTaskbar = true;
  // 绑定悬浮窗口的UI
  timerWindow.show(() => const FloatingTimerWidget());
}

class FloatingTimerWidget extends StatelessWidget {
  const FloatingTimerWidget({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.black.withOpacity(0.8),
        body: const Center(
          child: Text(
            "00:00:00",
            style: TextStyle(color: Colors.white, fontSize: 24),
          ),
        ),
      ),
    );
  }
}

三、更适合悬浮/ overlay窗口的替代包

1. bitsdojo_window(首推)

这是我目前用过最稳定的Flutter Windows窗口管理包,专门针对桌面端优化,API简洁,无边框、悬浮、多窗口这些需求都能轻松实现,几乎没有奇怪的bug,上手快。

2. win32包(高级需求首选)

如果你需要更直接的Win32 API通信,比如做透明窗口、自定义窗口阴影、精细控制窗口层级,直接用官方维护的win32包就行——它把Windows原生API封装成了Dart方法,完全不需要写C++代码。

四、直接调用Win32 API实现高级悬浮窗口

如果要做类似微软时钟的精细悬浮效果,比如半透明、无任务栏图标、始终置顶,用win32包直接调用原生API是最靠谱的:

import 'package:win32/win32.dart';
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
  doWhenWindowReady(() {
    final hwnd = appWindow.handle;
    setupFloatingWindow(hwnd);
    appWindow.show();
  });
}

// 配置悬浮窗口属性
void setupFloatingWindow(int hwnd) {
  // 设置扩展样式:工具窗口(不显示在任务栏)+ 置顶 + 透明背景
  SetWindowLongPtr(hwnd, GWL_EXSTYLE,
      GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_LAYERED);
  
  // 移除窗口边框和标题栏
  SetWindowLongPtr(hwnd, GWL_STYLE,
      GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_CAPTION & ~WS_THICKFRAME & ~WS_SYSMENU);
  
  // 设置窗口透明度(比如80%不透明度)
  SetLayeredWindowAttributes(hwnd, 0, 204, LWA_ALPHA);
  
  // 调整窗口位置到右上角,大小300x200
  SetWindowPos(hwnd, HWND_TOPMOST, GetSystemMetrics(SM_CXSCREEN)-350, 50, 300, 200, SWP_SHOWWINDOW);
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.transparent,
        body: Center(child: Text("悬浮计时器", style: TextStyle(color: Colors.white))),
      ),
    );
  }
}

五、类似微软时钟悬浮计时器的完整实现思路

  1. 创建独立悬浮窗口:用bitsdojo_window创建一个小尺寸、无边框、置顶、跳过任务栏的窗口
  2. UI设计:做一个简洁的计时器UI,支持开始/暂停/重置,背景可以设为半透明
  3. 窗口交互:允许用户拖动窗口(bitsdojo_window自带拖动功能,只要给Widget添加MouseRegion绑定拖动事件)
  4. 状态同步:主窗口和悬浮窗口之间用全局状态管理(比如Provider、GetX)共享计时器状态,确保两边显示一致
  5. 后台运行:如果需要最小化主窗口后悬浮窗口仍能运行,确保窗口的生命周期配置正确,不要随主窗口关闭而销毁

内容来源于stack exchange

火山引擎 最新活动