news 2026/3/8 5:11:04

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/3/7 6:47:26

数据科学家不愿公开的秘密:R语言随机森林分类优化技巧,速看!

第一章&#xff1a;数据科学家不愿公开的秘密&#xff1a;R语言随机森林分类优化技巧概览在实际建模过程中&#xff0c;许多数据科学家发现随机森林虽强大&#xff0c;但默认参数往往无法发挥其全部潜力。通过精细调参与特征工程配合&#xff0c;模型准确率可显著提升。以下是一…

作者头像 李华
网站建设 2026/3/7 1:56:24

JUnit 5 新特性详解与最佳实践

JUnit 5&#xff0c;作为Java生态系统中最主流的单元测试框架&#xff0c;自2017年发布以来&#xff0c;彻底改变了测试开发范式。相比JUnit 4&#xff0c;它引入了模块化架构、Lambda支持等创新&#xff0c;显著提升了测试的灵活性和可维护性。对于软件测试从业者而言&#xf…

作者头像 李华
网站建设 2026/2/20 23:48:51

还在用AI生搬硬套?这6款免费工具,知网维普查重无痕过!

别再让AI写作坑了你&#xff01;这3个错误正在毁掉你的论文 还在用ChatGPT生搬硬套写论文&#xff1f; 还在为AI生成的内容重复率超标失眠&#xff1f; 还在被导师指着屏幕骂“这就是你所谓的原创&#xff1f;”&#xff1f; 如果你对以上任何一个问题点头&#xff0c;那么这…

作者头像 李华
网站建设 2026/3/4 2:24:55

企业级Axios封装实战指南

以下是一套企业级 Axios 封装方案&#xff0c;包含请求 / 响应拦截、统一错误处理、请求取消、环境区分、请求重试等核心功能&#xff0c;可直接集成到 Vue/React/ 纯前端项目中&#xff1a;一、封装目录结构src/ ├── utils/ │ ├── request.js # Axios 核心封装…

作者头像 李华
网站建设 2026/3/7 4:41:37

YOLOv8镜像提供完整的备份恢复机制

YOLOv8镜像提供完整的备份恢复机制 在AI研发一线摸爬滚打过的人都知道&#xff0c;一个“在我机器上能跑”的项目到底有多让人崩溃。环境依赖错乱、训练中断后无法复现、团队成员配置不一致……这些问题消耗的不仅是时间&#xff0c;更是耐心。而当目标检测任务遇上YOLOv8这个高…

作者头像 李华
网站建设 2026/3/8 4:41:54

Linux系统下YOLOv8的SSH远程调用与运行方法详解

Linux系统下YOLOv8的SSH远程调用与运行方法详解 在智能视觉应用日益普及的今天&#xff0c;越来越多的AI项目不再局限于本地开发环境。无论是高校实验室共享GPU服务器&#xff0c;还是企业部署边缘推理设备&#xff0c;一个共通的需求浮现出来&#xff1a;如何在资源受限或地理…

作者头像 李华