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

如何检测Flutter Web应用运行状态以实现用户在线状态标记?

Flutter Web 用户在线状态标记实现方案

针对Flutter Web聊天应用的在线状态需求,核心是结合Web原生页面生命周期事件与Firebase Firestore实时写入,以下是落地性强的实现方案:

核心思路

通过Web原生API监听页面状态变化,配合Flutter应用初始化时机,实现:

  • 应用启动/页面可见时,将isOnline设为true
  • 页面关闭/隐藏时,将isOnline设为false

具体实现步骤

1. 导入依赖

确保已添加cloud_firestore依赖,并引入Web相关的dart:html库:

import 'dart:html' as html;
import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';

2. 封装在线状态管理类

创建独立管理类,统一处理事件监听与Firestore更新逻辑:

class OnlineStatusManager {
  final String userId;
  final FirebaseFirestore _firestore = FirebaseFirestore.instance;

  OnlineStatusManager(this.userId);

  void init() {
    // 应用启动时立即标记为在线
    _updateOnlineStatus(true);

    // 监听页面可见性变化(切换标签页/窗口最小化触发)
    html.document.onVisibilityChange.listen((_) {
      final isVisible = html.document.visibilityState == 'visible';
      _updateOnlineStatus(isVisible);
    });

    // 监听页面关闭/刷新事件
    html.window.onBeforeUnload.listen((_) {
      // 用sendBeacon确保请求在页面关闭前发送完成
      _sendOfflineStatus();
    });
  }

  // 常规更新在线状态
  Future<void> _updateOnlineStatus(bool isOnline) async {
    await _firestore.collection('users').doc(userId).update({
      'isOnline': isOnline,
      'lastSeen': FieldValue.serverTimestamp(),
    });
  }

  // 页面卸载时的可靠请求方案
  void _sendOfflineStatus() {
    // 替换为你的Firebase项目ID
    final projectId = 'your-firebase-project-id';
    final url = 'https://firestore.googleapis.com/v1/projects/$projectId/databases/(default)/documents/users/$userId?updateMask.fieldPaths=isOnline&updateMask.fieldPaths=lastSeen';

    final payload = {
      'fields': {
        'isOnline': {'booleanValue': false},
        'lastSeen': {'timestampValue': DateTime.now().toIso8601String()}
      }
    };

    // sendBeacon会被浏览器优先处理,避免异步操作被中断
    html.window.navigator.sendBeacon(
      url,
      html.Blob([Uri.encodeFull(jsonEncode(payload))], 'application/json'),
    );
  }
}

3. 在应用中集成

在应用初始化阶段启动状态管理,需确保已获取当前登录用户ID:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(const ChatApp());
}

class ChatApp extends StatefulWidget {
  const ChatApp({super.key});

  @override
  State<ChatApp> createState() => _ChatAppState();
}

class _ChatAppState extends State<ChatApp> {
  OnlineStatusManager? _statusManager;

  @override
  void initState() {
    super.initState();
    // 监听用户登录状态,获取用户ID
    FirebaseAuth.instance.authStateChanges().listen((user) {
      if (user != null) {
        _statusManager = OnlineStatusManager(user.uid);
        _statusManager?.init();
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Web Chat',
      home: StreamBuilder<User?>(
        stream: FirebaseAuth.instance.authStateChanges(),
        builder: (context, snapshot) {
          if (snapshot.hasData) return const ChatHomePage();
          return const LoginPage();
        },
      ),
    );
  }
}

关键注意事项

  • 页面卸载请求可靠性:直接在beforeunload中调用Firestore异步更新可能被浏览器中断,Navigator.sendBeacon是更稳妥的方案,浏览器会优先完成该请求再关闭页面。
  • 标签页切换逻辑调整:若不需要在用户切换标签页时标记为离线,可修改visibilityState监听逻辑,仅在页面卸载时设置isOnline为false,切换时仅更新lastSeen字段。
  • Firestore权限控制:需配置Firestore安全规则,允许用户仅更新自己文档的isOnline和lastSeen字段,避免权限错误。

内容的提问来源于stack exchange,提问作者F.SO7

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 13:35:13