Flutter桌面应用:如何检测后台状态下的用户活跃度?
实现Flutter桌面应用后台用户活跃度检测
Flutter本身没有跨平台的后台活跃度检测API,需要针对不同桌面平台调用原生能力,以下是各平台的具体实现方案:
Windows平台
- 调用
user32.dll原生API:通过GetLastInputInfo函数获取用户最后一次输入的时间戳,与当前时间对比判断活跃度。- 借助
ffi包在Flutter中调用原生函数,示例代码片段:import 'dart:ffi'; import 'package:ffi/ffi.dart'; typedef GetLastInputInfoC = Bool Function(Pointer<LASTINPUTINFO> plii); typedef GetLastInputInfoDart = bool Function(Pointer<LASTINPUTINFO> plii); typedef GetTickCountC = Uint32 Function(); typedef GetTickCountDart = int Function(); class LASTINPUTINFO extends Struct { @Uint32() int cbSize; @Uint32() int dwTime; } void checkUserActivity() { final user32 = DynamicLibrary.open('user32.dll'); final getLastInputInfo = user32.lookupFunction<GetLastInputInfoC, GetLastInputInfoDart>('GetLastInputInfo'); final getTickCount = user32.lookupFunction<GetTickCountC, GetTickCountDart>('GetTickCount'); final info = calloc<LASTINPUTINFO>(); info.ref.cbSize = sizeOf<LASTINPUTINFO>(); if (getLastInputInfo(info)) { final lastInputTime = info.ref.dwTime; final currentTime = getTickCount(); final idleTime = currentTime - lastInputTime; // 比如idleTime超过30000ms(30秒)判定为用户不活跃 } calloc.free(info); }
- 借助
- 后台运行保障:将检测逻辑放在独立Isolate中定时执行,避免被UI线程阻塞,确保应用后台时检测不中断。
macOS平台
- 利用Cocoa框架的
NSWorkspace:直接获取系统空闲时长,无需监听具体事件。- 封装原生OC代码为Flutter插件,示例原生代码:
#import <Cocoa/Cocoa.h> double getUserIdleTime() { return [NSWorkspace.sharedWorkspace idleTime]; } - 在Flutter侧通过
MethodChannel调用该原生方法,定时检测空闲时长是否超过设定阈值。
- 封装原生OC代码为Flutter插件,示例原生代码:
- 权限说明:需要在应用权限中开启「辅助功能」权限,否则无法获取空闲时间。
Linux平台
- 使用X11的
XScreenSaverQueryInfoAPI:获取屏幕保护相关信息,其中包含用户空闲时长。- 通过
ffi调用X11库,示例思路代码:import 'dart:ffi'; import 'package:ffi/ffi.dart'; typedef XOpenDisplayC = Pointer<Void> Function(Pointer<Utf8> displayName); typedef XOpenDisplayDart = Pointer<Void> Function(Pointer<Utf8> displayName); typedef DefaultRootWindowC = IntPtr Function(Pointer<Void> display); typedef DefaultRootWindowDart = int Function(Pointer<Void> display); typedef XScreenSaverQueryInfoC = Int32 Function(Pointer<Void> display, IntPtr window, Pointer<Void> info); typedef XScreenSaverQueryInfoDart = int Function(Pointer<Void> display, int window, Pointer<Void> info); typedef XCloseDisplayC = Void Function(Pointer<Void> display); typedef XCloseDisplayDart = void Function(Pointer<Void> display); void checkLinuxUserActivity() { final xlib = DynamicLibrary.open('libX11.so'); final xss = DynamicLibrary.open('libXss.so'); final openDisplay = xlib.lookupFunction<XOpenDisplayC, XOpenDisplayDart>('XOpenDisplay'); final defaultRootWindow = xlib.lookupFunction<DefaultRootWindowC, DefaultRootWindowDart>('DefaultRootWindow'); final queryInfo = xss.lookupFunction<XScreenSaverQueryInfoC, XScreenSaverQueryInfoDart>('XScreenSaverQueryInfo'); final closeDisplay = xlib.lookupFunction<XCloseDisplayC, XCloseDisplayDart>('XCloseDisplay'); final display = openDisplay(nullptr); final rootWindow = defaultRootWindow(display); final info = calloc.allocate(1024); // 根据XScreenSaverInfo结构体大小分配内存 if (queryInfo(display, rootWindow, info) != 0) { final idleTime = info.cast<Uint32>().elementAt(4).value; // 对应XScreenSaverInfo的idle字段偏移 // 处理空闲时间判断逻辑 } closeDisplay(display); calloc.free(info); }
- 通过
通用注意事项
- 后台稳定性:在应用进入后台时,确保Isolate的检测任务不被系统终止,部分平台可能需要配置后台运行权限。
- 阈值设置:根据业务需求设定合理的空闲阈值(比如5分钟无输入判定为不活跃)。
内容的提问来源于stack exchange,提问作者Cee Jay
相关产品推荐
相关产品推荐

