news 2026/4/16 16:02:16

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/4/16 14:04:44

降AI率实操指南:论文如何有效去除AI味

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

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

Pytest:优雅高效的Python测试框架

测试框架的进化需求 在持续集成与DevOps深度落地的2025年&#xff0c;Python作为主流开发语言亟需更强大的测试工具支撑。传统unittest框架的冗长断言、复杂配置已难以满足敏捷开发需求。Pytest应运而生&#xff0c;以其零配置起步、插件生态丰富和语法简洁优雅三大特性&#…

作者头像 李华
网站建设 2026/4/15 13:24:16

YOLOv8模型部署到Jetson Nano的实践经验

YOLOv8模型部署到Jetson Nano的实践经验 在智能摄像头、巡检机器人和边缘AI设备日益普及的今天&#xff0c;如何让深度学习模型真正在“端侧”跑起来&#xff0c;成了许多开发者面临的核心挑战。尤其是当项目从云端推理转向本地化、低延迟的实时检测时&#xff0c;资源受限的嵌…

作者头像 李华
网站建设 2026/4/5 10:43:56

YOLOv8推理时如何实现零拷贝内存传输?

YOLOv8推理时如何实现零拷贝内存传输&#xff1f; 在边缘计算和实时视觉系统中&#xff0c;每一毫秒都至关重要。当你部署一个基于YOLOv8的目标检测服务来处理多路高清视频流时&#xff0c;是否曾遇到这样的瓶颈&#xff1a;GPU利用率不到50%&#xff0c;但帧率却卡在瓶颈上&a…

作者头像 李华
网站建设 2026/4/16 14:32:46

YOLOv8官方示例代码实战演练:一步步教你跑通demo

YOLOv8实战指南&#xff1a;从零跑通官方Demo的完整路径 在智能摄像头遍布楼宇、无人机巡检成为常态的今天&#xff0c;如何快速构建一个稳定高效的目标检测系统&#xff0c;已成为开发者面临的核心挑战之一。传统方案往往卡在环境配置这一关——PyTorch版本不兼容、CUDA驱动错…

作者头像 李华
网站建设 2026/4/15 4:38:16

基于 Sora2 API 的视频生成实践:提示词写法与生成过程记录

一、背景说明在实际使用 Sora2 进行文生视频的过程中&#xff0c;除了接口是否调用成功外&#xff0c;更常见的问题是&#xff1a;视频内容与预期不一致画面元素缺失或混乱动作表现不连贯在排查这些问题时发现&#xff0c;在接口参数完全一致的情况下&#xff0c;Prompt 描述方…

作者头像 李华