You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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调用该原生方法,定时检测空闲时长是否超过设定阈值。
  • 权限说明:需要在应用权限中开启「辅助功能」权限,否则无法获取空闲时间。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 03:45:41