news 2026/5/13 10:05:32

mixin 写一个 Flutter 的“埋点 + 日志 + 性能监控”完整框架示例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
mixin 写一个 Flutter 的“埋点 + 日志 + 性能监控”完整框架示例

1)推荐目录结构

lib/ core/ observability/ observability.dart // 统一门面:O.log / O.track / O.perf logger.dart // Logger接口 + 默认实现 tracker.dart // Tracker接口 + 默认实现 perf.dart // Perf接口 + Trace route_analytics.dart // RouteObserver + 页面曝光 mixins/ log_mixin.dart track_mixin.dart perf_mixin.dart page_lifecycle_mixin.dart auto_dispose_mixin.dart widgets/ track_tap.dart // 点击埋点组件(低侵入) app.dart main.dart pages/ demo_page.dart

2)核心:Observability 统一门面(O)

lib/core/observability/observability.dart

import 'logger.dart'; import 'tracker.dart'; import 'perf.dart'; /// 统一观测门面:后续接 SDK 只换实现,不动业务代码 class O { O._(); static Logger logger = ConsoleLogger(); static Tracker tracker = DebugTracker(); static Perf perf = DebugPerf(); static void log(String msg, {Map<String, Object?>? fields}) => logger.log(msg, fields: fields); static void event(String name, {Map<String, Object?>? props}) => tracker.event(name, props: props); static Trace trace(String name, {Map<String, Object?>? tags}) => perf.trace(name, tags: tags); }

3)Logger / Tracker / Perf 接口与默认实现

logger.dart

import 'package:flutter/foundation.dart'; abstract class Logger { void log(String msg, {Map<String, Object?>? fields}); void warn(String msg, {Map<String, Object?>? fields}); void error(String msg, {Object? err, StackTrace? st, Map<String, Object?>? fields}); } class ConsoleLogger implements Logger { @override void log(String msg, {Map<String, Object?>? fields}) { debugPrint('[LOG] $msg ${fields ?? {}}'); } @override void warn(String msg, {Map<String, Object?>? fields}) { debugPrint('[WARN] $msg ${fields ?? {}}'); } @override void error(String msg, {Object? err, StackTrace? st, Map<String, Object?>? fields}) { debugPrint('[ERROR] $msg err=$err fields=${fields ?? {}}'); if (st != null) debugPrint(st.toString()); } }

tracker.dart

import 'package:flutter/foundation.dart'; abstract class Tracker { void event(String name, {Map<String, Object?>? props}); void pageView(String routeName, {Map<String, Object?>? props}); } class DebugTracker implements Tracker { @override void event(String name, {Map<String, Object?>? props}) { debugPrint('[TRACK] event=$name props=${props ?? {}}'); } @override void pageView(String routeName, {Map<String, Object?>? props}) { debugPrint('[TRACK] page=$routeName props=${props ?? {}}'); } }

perf.dart

import 'dart:async'; import 'package:flutter/foundation.dart'; abstract class Perf { Trace trace(String name, {Map<String, Object?>? tags}); } abstract class Trace { void tag(String key, Object? value); void end({Object? error}); } /// Debug 版 Trace:用 Stopwatch 统计耗时 class DebugPerf implements Perf { @override Trace trace(String name, {Map<String, Object?>? tags}) { return _DebugTrace(name, tags: tags); } } class _DebugTrace implements Trace { _DebugTrace(this.name, {Map<String, Object?>? tags}) : _tags = {...?tags}, _sw = Stopwatch()..start(); final String name; final Stopwatch _sw; final Map<String, Object?> _tags; @override void tag(String key, Object? value) => _tags[key] = value; @override void end({Object? error}) { _sw.stop(); debugPrint('[PERF] $name cost=${_sw.elapsedMilliseconds}ms tags=$_tags error=$error'); } } /// 便捷:包一层 async trace Future<T> traceAsync<T>( String name, Future<T> Function(Trace t) body, { Map<String, Object?>? tags, }) async { final t = DebugPerf().trace(name, tags: tags); try { final r = await body(t); t.end(); return r; } catch (e) { t.end(error: e); rethrow; } }

4)mixin 切面层(静态 AOP)

4.1 LogMixin

mixins/log_mixin.dart

import '../observability.dart'; mixin LogMixin { void log(String msg, {Map<String, Object?>? fields}) => O.log(msg, fields: fields); }

4.2 TrackMixin(事件埋点)

mixins/track_mixin.dart

import '../observability.dart'; mixin TrackMixin { void track(String event, {Map<String, Object?>? props}) => O.event(event, props: props); }

4.3 PerfMixin(性能 trace)

mixins/perf_mixin.dart

import '../observability.dart'; import '../perf.dart'; mixin PerfMixin { Trace trace(String name, {Map<String, Object?>? tags}) => O.trace(name, tags: tags); Future<T> traceFuture<T>( String name, Future<T> Function(Trace t) body, { Map<String, Object?>? tags, }) async { final t = trace(name, tags: tags); try { final r = await body(t); t.end(); return r; } catch (e) { t.end(error: e); rethrow; } } }

4.4 AutoDisposeMixin(资源释放切面)

mixins/auto_dispose_mixin.dart

import 'package:flutter/widgets.dart'; mixin AutoDisposeMixin<T extends StatefulWidget> on State<T> { final _disposers = <VoidCallback>[]; void addDisposer(VoidCallback disposer) => _disposers.add(disposer); @override void dispose() { for (final d in _disposers.reversed) { d(); } _disposers.clear(); super.dispose(); } }

4.5 PageLifecycleMixin(页面生命周期切面)

mixins/page_lifecycle_mixin.dart

import 'package:flutter/widgets.dart'; import '../observability.dart'; /// 你可以把它当成 “Android onCreate/onResume/onPause/onDestroy 的替代” mixin PageLifecycleMixin<T extends StatefulWidget> on State<T> { /// 推荐提供一个稳定的页面名(routeName 或业务名) String get pageName => widget.runtimeType.toString(); @mustCallSuper void onPageCreate() {} @mustCallSuper void onPageDispose() {} @override void initState() { super.initState(); O.log('page_create', fields: {'page': pageName}); onPageCreate(); } @override void dispose() { O.log('page_dispose', fields: {'page': pageName}); onPageDispose(); super.dispose(); } }

5)RouteObserver:自动页面曝光 PV(最像 Android 的“自动埋点”)

route_analytics.dart

import 'package:flutter/widgets.dart'; import 'observability.dart'; class AnalyticsRouteObserver extends RouteObserver<ModalRoute<dynamic>> { void _pageView(Route<dynamic>? route) { final name = route?.settings.name; if (name == null || name.isEmpty) return; O.tracker.pageView(name); O.log('page_view', fields: {'route': name}); } @override void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) { super.didPush(route, previousRoute); _pageView(route); } @override void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) { super.didPop(route, previousRoute); _pageView(previousRoute); } @override void didReplace({Route<dynamic>? newRoute, Route<dynamic>? oldRoute}) { super.didReplace(newRoute: newRoute, oldRoute: oldRoute); _pageView(newRoute); } }

6)低侵入点击埋点组件 TrackTap(替代到处手写 track)

widgets/track_tap.dart

import 'package:flutter/material.dart'; import '../observability.dart'; class TrackTap extends StatelessWidget { const TrackTap({ super.key, required this.event, this.props, required this.child, this.onTap, }); final String event; final Map<String, Object?>? props; final Widget child; final VoidCallback? onTap; @override Widget build(BuildContext context) { return GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { O.event(event, props: props); onTap?.call(); }, child: child, ); } }

7)在 App 里接入(MaterialApp)

app.dart

import 'package:flutter/material.dart'; import 'core/observability/route_analytics.dart'; import 'pages/demo_page.dart'; final routeObserver = AnalyticsRouteObserver(); class App extends StatelessWidget { const App({super.key}); @override Widget build(BuildContext context) { return MaterialApp( navigatorObservers: [routeObserver], routes: { '/': (_) => const DemoPage(), }, initialRoute: '/', ); } }

main.dart

import 'package:flutter/material.dart'; import 'app.dart'; void main() { runApp(const App()); }

8)页面使用示例(把 mixin 当 AOP 切面拼装)

pages/demo_page.dart

import 'package:flutter/material.dart'; import '../core/observability/mixins/log_mixin.dart'; import '../core/observability/mixins/track_mixin.dart'; import '../core/observability/mixins/perf_mixin.dart'; import '../core/observability/mixins/page_lifecycle_mixin.dart'; import '../core/observability/mixins/auto_dispose_mixin.dart'; import '../core/observability/widgets/track_tap.dart'; class DemoPage extends StatefulWidget { const DemoPage({super.key}); @override State<DemoPage> createState() => _DemoPageState(); } class _DemoPageState extends State<DemoPage> with LogMixin, TrackMixin, PerfMixin, PageLifecycleMixin<DemoPage>, AutoDisposeMixin<DemoPage> { @override String get pageName => 'DemoPage'; @override void onPageCreate() { log('DemoPage init'); } Future<void> _loadData() async { await traceFuture('api.load_demo', (t) async { t.tag('endpoint', '/demo'); await Future<void>.delayed(const Duration(milliseconds: 300)); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Observability Demo')), body: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ TrackTap( event: 'click_reload', props: {'page': pageName}, onTap: () async { track('reload_start', props: {'page': pageName}); await _loadData(); track('reload_done', props: {'page': pageName}); }, child: Container( padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 12), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all(), ), child: const Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Reload (TrackTap + PerfTrace)'), Icon(Icons.refresh), ], ), ), ), const SizedBox(height: 16), ElevatedButton( onPressed: () { // 也可以直接手写埋点(更灵活) track('click_native_button', props: {'page': pageName}); log('button clicked', fields: {'btn': 'native'}); }, child: const Text('Native Button'), ), ], ), ), ); } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/12 8:03:15

论文降AI率全流程详解:从30%降到20%以下怎么做

一、为什么手动降重总翻车&#xff1f;学术党必知的3大痛点“明明查重率达标了&#xff0c;导师却说论文有AI味要求重写&#xff01;”——这是不是你的真实写照&#xff1f;很多同学误以为同义词替换调整句式就能蒙混过关&#xff0c;结果陷入三大困局&#xff1a;❌ 痛点1&am…

作者头像 李华
网站建设 2026/5/6 11:27:13

YOLOv8如何实现文字区域检测任务?

YOLOv8如何实现文字区域检测任务&#xff1f; 在智能文档处理日益普及的今天&#xff0c;如何从一张扫描发票、合同或书籍页面中快速而准确地“找出哪些地方有字”&#xff0c;已成为OCR系统成败的关键。传统方法依赖边缘检测和连通域分析&#xff0c;面对复杂背景、倾斜排版或…

作者头像 李华
网站建设 2026/4/28 9:49:15

YOLOv8训练参数详解:epochs、imgsz、data配置说明

YOLOv8训练参数详解&#xff1a;epochs、imgsz、data配置说明 在目标检测的实际项目中&#xff0c;我们常常面临这样的困境&#xff1a;模型训练了上百轮却依然漏检严重&#xff0c;或者推理速度慢得无法部署到边缘设备。问题的根源往往不在于算法本身&#xff0c;而在于那些看…

作者头像 李华
网站建设 2026/5/10 10:58:44

YOLOv8 SPPF模块作用机制详解

YOLOv8中的SPPF模块&#xff1a;轻量高效的空间上下文增强机制 在目标检测领域&#xff0c;速度与精度的平衡始终是工程实践的核心挑战。尤其是在无人机航拍、智能监控和移动端视觉应用中&#xff0c;模型不仅要准确识别尺度差异极大的物体——从远处的小行人到近处的大车辆——…

作者头像 李华
网站建设 2026/5/1 10:23:50

YOLOv8如何替换主干网络?自定义Backbone教程

YOLOv8如何替换主干网络&#xff1f;自定义Backbone教程 在目标检测的实际落地过程中&#xff0c;我们常常会遇到这样的问题&#xff1a;标准模型虽然在COCO等通用数据集上表现优异&#xff0c;但在特定场景下却“水土不服”——比如在边缘设备上跑得太慢&#xff0c;或者对小…

作者头像 李华
网站建设 2026/5/13 9:34:40

基于SpringBoot技术的港口物流数据分析及可视化的设计与实现

目录具体实现截图项目介绍论文大纲核心代码部分展示可定制开发之亮点部门介绍结论源码获取详细视频演示 &#xff1a;文章底部获取博主联系方式&#xff01;同行可合作具体实现截图 本系统&#xff08;程序源码数据库调试部署讲解&#xff09;同时还支持Python(flask,django)、…

作者头像 李华