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

1)推荐目录结构

bash 复制代码
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

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

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

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

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

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

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

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

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

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

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

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

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

Dart 复制代码
import 'package:flutter/material.dart';
import 'app.dart';

void main() {
  runApp(const App());
}

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

pages/demo_page.dart

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'),
            ),
          ],
        ),
      ),
    );
  }
}
相关推荐
we1less2 小时前
[audio] AudioTrack (五) 共享内存创建分析
android·java·开发语言
hslinux2 小时前
NDK 通过configure 编译C++源码通用脚本
android·c++·ndk·configure
消失的旧时光-19432 小时前
Flutter 工程中 mixin 的正确打开方式:5 种高质量设计范式 + mixin vs 继承 vs 组合 + 为什么它比 BasePage 更优雅
前端·flutter·架构
2501_915106322 小时前
iOS 抓包工具有哪些?不同类型的抓包工具可以做什么
android·ios·小程序·https·uni-app·iphone·webview
消失的旧时光-19433 小时前
Flutter 中 mixin 的完整认知体系——从原理、范式、架构选择到反模式(工程实战版)
flutter·架构
2501_946244783 小时前
Flutter & OpenHarmony OA系统下拉刷新组件开发指南
开发语言·javascript·flutter
kirk_wang3 小时前
Flutter 三方库 simple_circular_progress_bar 在 OHOS 平台的适配实践
flutter·移动开发·跨平台·arkts·鸿蒙
芦半山4 小时前
2025:生活是个缓慢受锤的过程
android·年终总结
Kapaseker12 小时前
你不看会后悔的2025年终总结
android·kotlin