Flutter中如何跟踪用户应用使用时长并通过Firebase上报给管理员
Flutter 应用使用时长跟踪与Firebase上报实现
准备工作
首先在pubspec.yaml中添加必要依赖:
dependencies: flutter: sdk: flutter firebase_core: ^2.20.0 cloud_firestore: ^4.12.0 shared_preferences: ^2.2.2 # 缓存未上报时长,防止应用被杀丢失数据
核心跟踪逻辑实现
创建单例类AppUsageTracker处理时长统计与生命周期监听:
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; class AppUsageTracker with WidgetsBindingObserver { static final AppUsageTracker _instance = AppUsageTracker._internal(); factory AppUsageTracker() => _instance; AppUsageTracker._internal() { WidgetsBinding.instance.addObserver(this); } DateTime? _startTime; int _currentSessionDuration = 0; // 单位:秒 final FirebaseFirestore _firestore = FirebaseFirestore.instance; final FirebaseAuth _auth = FirebaseAuth.instance; // 初始化加载缓存的未上报时长 Future<void> init() async { final prefs = await SharedPreferences.getInstance(); _currentSessionDuration = prefs.getInt('unreported_duration') ?? 0; } @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); switch (state) { case AppLifecycleState.resumed: // 应用进入前台,开始计时 _startTime = DateTime.now(); break; case AppLifecycleState.paused: // 应用进入后台,计算本次会话时长并累加 if (_startTime != null) { final duration = DateTime.now().difference(_startTime!).inSeconds; _currentSessionDuration += duration; _startTime = null; _cacheUnreportedDuration(); _reportUsage(); } break; case AppLifecycleState.detached: // 应用被销毁,最后一次上报 if (_startTime != null) { final duration = DateTime.now().difference(_startTime!).inSeconds; _currentSessionDuration += duration; _startTime = null; _cacheUnreportedDuration(); _reportUsage(); } break; default: break; } } // 缓存未上报时长 Future<void> _cacheUnreportedDuration() async { final prefs = await SharedPreferences.getInstance(); await prefs.setInt('unreported_duration', _currentSessionDuration); } // 上报时长到Firebase Future<void> _reportUsage() async { final user = _auth.currentUser; if (user == null) return; // 未登录用户不上报 try { final today = DateTime.now().toIso8601String().split('T')[0]; // 格式:YYYY-MM-DD final userUsageDoc = _firestore.collection('user_app_usage').doc(user.uid); await _firestore.runTransaction((transaction) async { final snapshot = await transaction.get(userUsageDoc); if (snapshot.exists) { // 更新今日时长 final currentDailyUsage = snapshot.data()?['daily_usage'] as Map<String, dynamic>? ?? {}; final todayUsage = (currentDailyUsage[today] as int? ?? 0) + _currentSessionDuration; currentDailyUsage[today] = todayUsage; transaction.update(userUsageDoc, { 'daily_usage': currentDailyUsage, 'last_updated': FieldValue.serverTimestamp(), 'user_id': user.uid, 'email': user.email }); } else { // 创建新文档 transaction.set(userUsageDoc, { 'daily_usage': {today: _currentSessionDuration}, 'last_updated': FieldValue.serverTimestamp(), 'user_id': user.uid, 'email': user.email }); } }); // 上报成功,清空缓存 _currentSessionDuration = 0; await _cacheUnreportedDuration(); } catch (e) { print('上报失败: $e'); // 上报失败,保留缓存,下次启动自动重试 } } // 主动触发上报(比如用户退出登录时) Future<void> forceReport() async { if (_startTime != null) { final duration = DateTime.now().difference(_startTime!).inSeconds; _currentSessionDuration += duration; _startTime = null; } await _reportUsage(); } // 销毁时移除监听 void dispose() { WidgetsBinding.instance.removeObserver(this); } }
集成到应用中
在main.dart中初始化Firebase和跟踪器:
import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); // 初始化应用时长跟踪器 await AppUsageTracker().init(); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'App Usage Tracker Demo', home: const HomeScreen(), ); } } class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State<HomeScreen> createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { @override void dispose() { // 页面销毁时主动上报(可选) AppUsageTracker().forceReport(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('首页')), body: const Center(child: Text('应用使用时长跟踪中...')), ); } }
管理员查看数据
管理员可通过Firebase控制台的Firestore查看user_app_usage集合:
- 每个文档对应一位用户,包含
user_id、email、last_updated字段 daily_usage字段为键值对,键是日期(YYYY-MM-DD),值是当日累计使用时长(秒)
如需可视化报表,可将数据导出至Excel,或结合Firebase Analytics自定义事件补充统计维度。
关键注意事项
- 登录校验:仅上报已登录用户数据,避免匿名数据混乱
- 异常处理:通过本地缓存确保应用被杀时未上报数据不丢失
- 数据一致性:使用Firestore事务防止多端同时上报时的数据冲突
- 隐私合规:需根据地区法规(如GDPR)告知用户数据收集行为,并提供关闭选项
内容的提问来源于stack exchange,提问作者SM Huzaifa
相关产品推荐
相关产品推荐

