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'),
            ),
          ],
        ),
      ),
    );
  }
}
相关推荐
火柴就是我20 分钟前
让我们实现一个更好看的内部阴影按钮
android·flutter
王晓枫1 小时前
flutter接入三方库运行报错:Error running pod install
前端·flutter
砖厂小工7 小时前
用 GLM + OpenClaw 打造你的 AI PR Review Agent — 让龙虾帮你审代码
android·github
张拭心8 小时前
春节后,有些公司明确要求 AI 经验了
android·前端·人工智能
张拭心8 小时前
Android 17 来了!新特性介绍与适配建议
android·前端
shankss9 小时前
Flutter 下拉刷新库 pull_to_refresh_plus 设计与实现分析
flutter
Kapaseker10 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴10 小时前
Android17 为什么重写 MessageQueue
android
忆江南1 天前
iOS 深度解析
flutter·ios
明君879971 天前
Flutter 实现 AI 聊天页面 —— 记一次 Markdown 数学公式显示的踩坑之旅
前端·flutter