Files
xianyan/docs/superpowers/plans/2026-05-22-performance-optimization.md
2026-05-22 02:04:46 +08:00

27 KiB
Raw Blame History

移动端性能优化 — 智能节流方案 实施计划

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 在不阉割任何视觉效果和动画的前提下,通过智能节流策略消除移动端发热和高资源占用问题

Architecture: 创建 PerformanceOrchestrator 性能调度中心单例,统一管理帧率节流、动画可见性感知、后台服务休眠、特效互斥调度。各组件主动注册到调度器,由调度器根据性能级别、电量、前后台状态决定运行策略。

Tech Stack: Flutter/Dart, Riverpod, battery_plus, sensors_plus, visibility_detector


文件结构

新建文件

文件 职责
lib/core/services/performance/performance_orchestrator.dart 性能调度中心单例 — 帧率/级别/电量/前后台统一管理
lib/core/services/performance/app_lifecycle_gate.dart 前后台统一管理门 — 暂停/恢复后台服务和动画
lib/core/services/performance/effect_mutex.dart 特效互斥调度器 — 同一时刻限制重量级特效数量

修改文件

文件 修改内容
lib/shared/widgets/shader_card_background.dart Shader 帧率节流 + 可见性暂停
lib/shared/widgets/appbar_character_sprite.dart 不可见时暂停 idle 动画 + setState→ValueNotifier
lib/shared/widgets/tab_icon_sprite.dart 未选中 Tab 停止 glow 动画
lib/shared/widgets/glass_container.dart RepaintBoundary 包裹 + 缓存策略
lib/shared/widgets/glass_bottom_nav_bar.dart RepaintBoundary 包裹
lib/core/layout/app_shell.dart 集成 AppLifecycleGate
lib/app/app.dart 集成 AppLifecycleGate + PerformanceOrchestrator 初始化
lib/main.dart 初始化 PerformanceOrchestrator
lib/core/services/device/shake_detector.dart 前后台暂停/恢复
lib/core/services/clipboard_monitor_service.dart 前后台暂停/恢复
lib/core/services/device/battery_info_service.dart 前后台暂停/恢复轮询
lib/core/services/device/battery_optimization_service.dart 联动 PerformanceOrchestrator
lib/core/utils/interaction_animations.dart CelebrationOverlay 按需激活
lib/features/home/presentation/home_refresh_indicator.dart setState→局部刷新
CHANGELOG.md 记录本次优化

Task 1: 创建 PerformanceOrchestrator 性能调度中心

Files:

  • Create: lib/core/services/performance/performance_orchestrator.dart

  • Step 1: 创建 PerformanceOrchestrator 单例

// ============================================================
// 闲言APP — 性能调度中心
// 创建时间: 2026-05-22
// 更新时间: 2026-05-22
// 作用: 统一管理帧率节流/性能级别/电量联动/前后台切换
// 上次更新: 初始创建
// ============================================================

import 'dart:async';

import 'package:flutter/scheduler.dart';
import 'package:xianyan/core/services/device/battery_info_service.dart';
import 'package:xianyan/core/storage/app_kv_store.dart';
import 'package:xianyan/core/utils/logger.dart';

/// 性能级别
enum PerformanceLevel {
  full('完整', 1, 1),
  balanced('平衡', 2, 30),
  saver('省电', 3, 20);

  const PerformanceLevel(this.label, this.sortOrder, this.targetFps);
  final String label;
  final int sortOrder;
  final int targetFps;
}

/// 帧率节流回调类型 — 返回 true 表示本帧需要更新
typedef FrameThrottleCallback = bool Function();

class PerformanceOrchestrator {
  PerformanceOrchestrator._();
  static final PerformanceOrchestrator instance = PerformanceOrchestrator._();

  static const _keyLevel = 'performance_level';

  PerformanceLevel _level = PerformanceLevel.full;
  bool _isAppForeground = true;
  StreamSubscription<BatteryInfo>? _batterySub;
  final List<VoidCallback> _onForegroundCallbacks = [];
  final List<VoidCallback> _onBackgroundCallbacks = [];

  PerformanceLevel get level => _level;
  bool get isAppForeground => _isAppForeground;
  bool get shouldThrottleShader => _level != PerformanceLevel.full;
  bool get shouldPauseAnimations => !_isAppForeground || _level == PerformanceLevel.saver;

  /// 初始化 — 在 main() 中调用
  Future<void> init() async {
    final stored = AppKVStore.getString(_keyLevel);
    _level = _resolveLevel(stored);
    Log.i('PerformanceOrchestrator 初始化: level=${_level.label}');

    _batterySub = BatteryInfoService.instance.onBatteryChanged.listen((info) {
      if (info.isCritical && _level != PerformanceLevel.saver) {
        setLevel(PerformanceLevel.saver);
        Log.i('PerformanceOrchestrator: 电量严重不足,自动切换省电模式');
      }
    });
  }

  /// 设置性能级别
  void setLevel(PerformanceLevel newLevel) {
    if (_level == newLevel) return;
    _level = newLevel;
    AppKVStore.setString(_keyLevel, newLevel.name);
    Log.i('PerformanceOrchestrator: 级别切换为 ${newLevel.label}');
  }

  /// App 进入前台
  void onAppResumed() {
    if (_isAppForeground) return;
    _isAppForeground = true;
    Log.i('PerformanceOrchestrator: App 回到前台');
    for (final cb in _onForegroundCallbacks) {
      cb();
    }
  }

  /// App 进入后台
  void onAppPaused() {
    if (!_isAppForeground) return;
    _isAppForeground = false;
    Log.i('PerformanceOrchestrator: App 进入后台');
    for (final cb in _onBackgroundCallbacks) {
      cb();
    }
  }

  /// 注册前台回调
  void onForeground(VoidCallback callback) {
    _onForegroundCallbacks.add(callback);
  }

  /// 注册后台回调
  void onBackground(VoidCallback callback) {
    _onBackgroundCallbacks.add(callback);
  }

  /// 移除回调
  void removeCallbacks(VoidCallback callback) {
    _onForegroundCallbacks.remove(callback);
    _onBackgroundCallbacks.remove(callback);
  }

  /// 创建节流帧计数器 — 返回每 N 帧触发一次的判断函数
  /// full: 每1帧, balanced: 每2帧, saver: 每3帧
  FrameThrottleCallback createFrameThrottle() {
    int frameCount = 0;
    return () {
      frameCount++;
      final skip = switch (_level) {
        PerformanceLevel.full => 1,
        PerformanceLevel.balanced => 2,
        PerformanceLevel.saver => 3,
      };
      if (frameCount % skip == 0) {
        return true;
      }
      return false;
    };
  }

  PerformanceLevel _resolveLevel(String? stored) {
    return switch (stored) {
      'full' => PerformanceLevel.full,
      'balanced' => PerformanceLevel.balanced,
      'saver' => PerformanceLevel.saver,
      _ => PerformanceLevel.full,
    };
  }

  void dispose() {
    _batterySub?.cancel();
    _onForegroundCallbacks.clear();
    _onBackgroundCallbacks.clear();
  }
}
  • Step 2: 验证文件创建

Run: dart analyze lib/core/services/performance/performance_orchestrator.dart Expected: 无错误


Task 2: 创建 AppLifecycleGate 前后台管理门

Files:

  • Create: lib/core/services/performance/app_lifecycle_gate.dart

  • Step 1: 创建 AppLifecycleGate

// ============================================================
// 闲言APP — 前后台统一管理门
// 创建时间: 2026-05-22
// 更新时间: 2026-05-22
// 作用: 统一管理前后台切换时暂停/恢复后台服务和动画
// 上次更新: 初始创建
// ============================================================

import 'package:flutter/widgets.dart';
import 'package:xianyan/core/services/performance/performance_orchestrator.dart';
import 'package:xianyan/core/services/device/shake_detector.dart';
import 'package:xianyan/core/services/clipboard_monitor_service.dart';
import 'package:xianyan/core/services/device/battery_info_service.dart';
import 'package:xianyan/core/utils/logger.dart';

/// App 生命周期统一管理门
///
/// 在 App 根组件中混入,统一管理前后台切换时:
/// - 暂停/恢复传感器(摇一摇)
/// - 暂停/恢复剪贴板监控
/// - 暂停/恢复电池轮询
/// - 通知 PerformanceOrchestrator 前后台状态
mixin AppLifecycleGate on WidgetsBindingObserver {
  bool _lifecycleGateInitialized = false;

  void initLifecycleGate() {
    if (_lifecycleGateInitialized) return;
    _lifecycleGateInitialized = true;
    WidgetsBinding.instance.addObserver(this);
  }

  void disposeLifecycleGate() {
    WidgetsBinding.instance.removeObserver(this);
    _lifecycleGateInitialized = false;
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    switch (state) {
      case AppLifecycleState.paused:
        _onEnterBackground();
      case AppLifecycleState.resumed:
        _onEnterForeground();
      default:
        break;
    }
  }

  void _onEnterBackground() {
    Log.i('AppLifecycleGate: 进入后台 — 暂停服务和动画');
    PerformanceOrchestrator.instance.onAppPaused();

    ShakeDetector.instance.stop();
    ClipboardMonitorService.instance.stopMonitor();
    BatteryInfoService.instance.pausePolling();
  }

  void _onEnterForeground() {
    Log.i('AppLifecycleGate: 回到前台 — 恢复服务和动画');
    PerformanceOrchestrator.instance.onAppResumed();

    if (ShakeDetector.instance.isEnabled) {
      ShakeDetector.instance.start();
    }
    if (ClipboardMonitorService.instance.isEnabled) {
      ClipboardMonitorService.instance.startMonitor();
    }
    BatteryInfoService.instance.resumePolling();
  }
}
  • Step 2: 为 BatteryInfoService 添加 pausePolling/resumePolling 方法

修改 lib/core/services/device/battery_info_service.dart,添加:

bool _pollingPaused = false;

void pausePolling() {
  _pollingPaused = true;
  _pollTimer?.cancel();
  _pollTimer = null;
  Log.i('BatteryInfoService: 轮询已暂停');
}

void resumePolling() {
  if (!_pollingPaused) return;
  _pollingPaused = false;
  _pollTimer?.cancel();
  _pollTimer = Timer.periodic(const Duration(minutes: 5), (_) async {
    try {
      _currentLevel = await _battery.batteryLevel;
      _notify();
    } catch (_) {}
  });
  Log.i('BatteryInfoService: 轮询已恢复');
}
  • Step 3: 验证

Run: dart analyze lib/core/services/performance/app_lifecycle_gate.dart lib/core/services/device/battery_info_service.dart Expected: 无错误


Task 3: 创建 EffectMutex 特效互斥调度器

Files:

  • Create: lib/core/services/performance/effect_mutex.dart

  • Step 1: 创建 EffectMutex

// ============================================================
// 闲言APP — 特效互斥调度器
// 创建时间: 2026-05-22
// 更新时间: 2026-05-22
// 作用: 限制同一时刻运行的重量级特效数量防止GPU过载
// 上次更新: 初始创建
// ============================================================

import 'dart:async';

import 'package:xianyan/core/utils/logger.dart';

/// 特效优先级
enum EffectPriority {
  interaction(0),
  entrance(1),
  decoration(2);

  const EffectPriority(this.value);
  final int value;
}

/// 特效令牌 — 持有者表示正在运行一个重量级特效
class EffectToken {
  EffectToken._(this._mutex, this.name);
  final EffectMutex _mutex;
  final String name;
  bool _released = false;

  bool get isActive => !_released;

  void release() {
    if (_released) return;
    _released = true;
    _mutex._release(this);
  }
}

/// 特效互斥调度器
///
/// 限制同一时刻运行的重量级特效数量。
/// 当已有 maxConcurrent 个特效运行时,低优先级特效会被延迟。
class EffectMutex {
  EffectMutex({this.maxConcurrent = 2});

  final int maxConcurrent;
  final List<EffectToken> _active = [];
  final List<_PendingEffect> _pending = [];

  /// 申请一个特效槽位
  ///
  /// 如果当前活跃特效数未达上限,立即返回 token
  /// 否则等待直到有槽位释放。
  Future<EffectToken> acquire(String name, {EffectPriority priority = EffectPriority.decoration}) async {
    if (_active.length < maxConcurrent) {
      final token = EffectToken._(this, name);
      _active.add(token);
      Log.i('EffectMutex: "$name" 获得槽位 (${_active.length}/$maxConcurrent)');
      return token;
    }

    final completer = Completer<EffectToken>();
    _pending.add(_PendingEffect(name, priority, completer));
    Log.i('EffectMutex: "$name" 排队等待 (优先级=${priority.name}, 队列=${_pending.length})');
    return completer.future;
  }

  void _release(EffectToken token) {
    _active.remove(token);
    Log.i('EffectMutex: "${token.name}" 释放槽位 (${_active.length}/$maxConcurrent)');

    if (_pending.isNotEmpty) {
      _pending.sort((a, b) => a.priority.value.compareTo(b.priority.value));
      final next = _pending.removeAt(0);
      final newToken = EffectToken._(this, next.name);
      _active.add(newToken);
      next.completer.complete(newToken);
      Log.i('EffectMutex: "${next.name}" 获得槽位 (${_active.length}/$maxConcurrent)');
    }
  }
}

class _PendingEffect {
  _PendingEffect(this.name, this.priority, this.completer);
  final String name;
  final EffectPriority priority;
  final Completer<EffectToken> completer;
}
  • Step 2: 验证

Run: dart analyze lib/core/services/performance/effect_mutex.dart Expected: 无错误


Task 4: Shader 帧率节流 — 修改 ShaderCardBackground

Files:

  • Modify: lib/shared/widgets/shader_card_background.dart

  • Step 1: 添加帧率节流 + 可见性感知

_ShaderCardBackgroundState 修改为:

class _ShaderCardBackgroundState extends State<ShaderCardBackground>
    with SingleTickerProviderStateMixin, WidgetsBindingObserver {
  late Ticker _ticker;
  final ValueNotifier<double> _timeNotifier = ValueNotifier(0);
  ui.FragmentProgram? _program;
  bool _loaded = false;
  bool _isVisible = true;
  late FrameThrottleCallback _shouldRenderFrame;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    _shouldRenderFrame = PerformanceOrchestrator.instance.createFrameThrottle();
    _ticker = createTicker(_onTick);
    _ticker.start();
    _loadShader();

    PerformanceOrchestrator.instance.onForeground(_resumeTicker);
    PerformanceOrchestrator.instance.onBackground(_pauseTicker);
  }

  void _pauseTicker() {
    if (_ticker.isActive) _ticker.stop();
  }

  void _resumeTicker() {
    if (!_ticker.isActive && _isVisible) _ticker.start();
  }

  void _onTick(Duration elapsed) {
    if (!_shouldRenderFrame()) return;
    _timeNotifier.value = elapsed.inMicroseconds / 1000000.0;
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    if (state == AppLifecycleState.paused) {
      _pauseTicker();
    } else if (state == AppLifecycleState.resumed && _isVisible) {
      _resumeTicker();
    }
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    PerformanceOrchestrator.instance.removeCallbacks(_resumeTicker);
    PerformanceOrchestrator.instance.removeCallbacks(_pauseTicker);
    _ticker.dispose();
    _timeNotifier.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (!_loaded || _program == null) {
      return Container(
        decoration: BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
            colors: [
              const Color(0xFF6699EE).withValues(alpha: 0.6),
              const Color(0xFFB366CC).withValues(alpha: 0.6),
            ],
          ),
        ),
      );
    }

    return RepaintBoundary(
      child: CustomPaint(
        painter: _ShaderPainter(
          program: _program!,
          timeNotifier: _timeNotifier,
          touch: widget.touchOffset ?? Offset.zero,
        ),
      ),
    );
  }
}

同时添加 import:

import 'package:xianyan/core/services/performance/performance_orchestrator.dart';
  • Step 2: 验证

Run: dart analyze lib/shared/widgets/shader_card_background.dart Expected: 无错误


Task 5: 动画可见性感知 — 修改 AppBarCharacterSprite

Files:

  • Modify: lib/shared/widgets/appbar_character_sprite.dart

  • Step 1: 添加前后台感知 + 不可见时暂停 idle 动画

AppBarCharacterSpriteState 中添加:

// 在类顶部添加
bool _isAppForeground = true;

// 在 initState 末尾添加
PerformanceOrchestrator.instance.onForeground(_onForeground);
PerformanceOrchestrator.instance.onBackground(_onBackground);

// 添加方法
void _onForeground() {
  _isAppForeground = true;
  if (widget.animationIntensity > 0.0 && !_idleController.isAnimating) {
    _idleController.repeat(reverse: true);
  }
}

void _onBackground() {
  _isAppForeground = false;
  _idleController.stop();
}

// 修改 _idleController.repeat 的位置 — 在 initState 中
// 原来: if (widget.animationIntensity > 0.0) _idleController.repeat(reverse: true);
// 改为: if (widget.animationIntensity > 0.0 && _isAppForeground) _idleController.repeat(reverse: true);

// 在 didUpdateWidget 中修改 idle 恢复逻辑
// 原来: } else if (!_idleController.isAnimating) {
// 改为: } else if (!_idleController.isAnimating && _isAppForeground) {

// 在 dispose 中添加
PerformanceOrchestrator.instance.removeCallbacks(_onForeground);
PerformanceOrchestrator.instance.removeCallbacks(_onBackground);

添加 import:

import 'package:xianyan/core/services/performance/performance_orchestrator.dart';
  • Step 2: 优化 onPointerMove 中的 setState

onPointerMove 中的 setState(() {}) 替换为局部刷新。由于 _eyeOffsetAnimatedBuilder 内部使用,且 AnimatedBuilder 已经在监听 6 个 Controller所以 _eyeOffset 的变化需要触发重绘。最佳方案是将 _eyeOffset 改为 ValueNotifier<Offset>

// 替换 Offset _eyeOffset = Offset.zero; 为:
final ValueNotifier<Offset> _eyeOffsetNotifier = ValueNotifier(Offset.zero);

// onPointerMove 中替换 setState(() {}); 为:
_eyeOffsetNotifier.value = Offset(
  (delta.dx / distance) * factor,
  (delta.dy / distance) * factor,
);

// lookAtTitle 中替换 _eyeOffset = ... 为:
_eyeOffsetNotifier.value = const Offset(3.0, 0.0);
// 和:
_eyeOffsetNotifier.value = Offset.zero;

// AnimatedBuilder 的 animation 改为:
animation: Listenable.merge([
  _bounceController,
  _earController,
  _noseController,
  _cheekController,
  _expressionController,
  _idleController,
  _eyeOffsetNotifier,
]),

// builder 内部替换 _eyeOffset 为 _eyeOffsetNotifier.value

// dispose 中添加:
_eyeOffsetNotifier.dispose();
  • Step 3: 验证

Run: dart analyze lib/shared/widgets/appbar_character_sprite.dart Expected: 无错误


Task 6: Tab 动画节流 — 修改 TabIconSprite

Files:

  • Modify: lib/shared/widgets/tab_icon_sprite.dart

  • Step 1: 未选中 Tab 停止 glow 呼吸动画

_TabIconSpriteStatedidUpdateWidget 中,当 Tab 从选中变为未选中时,_glowController.stop() 已经存在。需要确保未选中的 Tab 不运行 glow repeat

// 在 didUpdateWidget 中,!widget.isSelected && oldWidget.isSelected 分支
// 已有 _glowController.stop(); 确认无误

// 在 initState 中,只有 isSelected 时才 repeat glow
// 已有 if (widget.isSelected) { _glowController.repeat(reverse: true); }
// 确认无误
  • Step 2: 添加前后台感知
// 在 _TabIconSpriteState 中添加
bool _isAppForeground = true;

// initState 末尾添加
PerformanceOrchestrator.instance.onForeground(_onForeground);
PerformanceOrchestrator.instance.onBackground(_onBackground);

void _onForeground() {
  _isAppForeground = true;
  if (widget.isSelected) {
    _glowController.repeat(reverse: true);
  }
}

void _onBackground() {
  _isAppForeground = false;
  _glowController.stop();
}

// dispose 中添加
PerformanceOrchestrator.instance.removeCallbacks(_onForeground);
PerformanceOrchestrator.instance.removeCallbacks(_onBackground);

添加 import:

import 'package:xianyan/core/services/performance/performance_orchestrator.dart';
  • Step 3: 验证

Run: dart analyze lib/shared/widgets/tab_icon_sprite.dart Expected: 无错误


Task 7: BackdropFilter 缓存优化 — 修改 GlassContainer

Files:

  • Modify: lib/shared/widgets/glass_container.dart

  • Step 1: 添加 RepaintBoundary 包裹

build 方法中,将 childRepaintBoundary 包裹,减少 BackdropFilter 的重绘范围:

// 在所有 _maybeBackdropFilter 调用中,将 child 参数用 RepaintBoundary 包裹
// 例如:
child: _maybeBackdropFilter(
  sigma: cfg.blurSigma * multiplier,
  child: RepaintBoundary(child: Padding(padding: effectivePadding, child: child)),
),
  • Step 2: 验证

Run: dart analyze lib/shared/widgets/glass_container.dart Expected: 无错误


Task 8: GlassBottomNavBar 缓存优化

Files:

  • Modify: lib/shared/widgets/glass_bottom_nav_bar.dart

  • Step 1: 添加 RepaintBoundary

build 方法中,将整个 BackdropFilter 子树用 RepaintBoundary 包裹:

// 在 build 方法的 return Container(...) 外层包裹 RepaintBoundary
return RepaintBoundary(
  child: Container(
    margin: ...
    ...
  ),
);
  • Step 2: 验证

Run: dart analyze lib/shared/widgets/glass_bottom_nav_bar.dart Expected: 无错误


Task 9: 集成 AppLifecycleGate 到 App 根组件

Files:

  • Modify: lib/app/app.dart

  • Modify: lib/main.dart

  • Step 1: 在 _XianyanAppState 中混入 AppLifecycleGate

// 修改类声明
class _XianyanAppState extends ConsumerState<XianyanApp>
    with WidgetsBindingObserver, AppLifecycleGate {

// 修改 initState
@override
void initState() {
  super.initState();
  initLifecycleGate();
}

// 修改 dispose
@override
void dispose() {
  disposeLifecycleGate();
  super.dispose();
}

// 删除原有的 didChangeAppLifecycleState 方法AppLifecycleGate 已处理)
// 但保留 AppLockService 相关逻辑,在 AppLifecycleGate 的重写中调用
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
  super.didChangeAppLifecycleState(state);
  switch (state) {
    case AppLifecycleState.paused:
      try { AppLockService.onAppPaused(); } catch (e) { Log.e('应用锁暂停处理失败', e); }
      break;
    case AppLifecycleState.resumed:
      Future.microtask(() {
        try { AppLockService.onAppResumed(); } catch (e) { Log.e('应用锁恢复处理失败', e); }
      });
      break;
    default:
      break;
  }
}

添加 import:

import 'package:xianyan/core/services/performance/app_lifecycle_gate.dart';
  • Step 2: 在 main.dart 中初始化 PerformanceOrchestrator

main() 函数中,BatteryOptimizationService.init() 之后添加:

try {
  await PerformanceOrchestrator.instance.init();
  if (pu.isOhos) Log.i('🟢 [OHOS] 性能调度中心初始化完成');
} catch (e, st) {
  Log.e('性能调度中心初始化失败', e, st);
}

添加 import:

import 'core/services/performance/performance_orchestrator.dart';
  • Step 3: 验证

Run: dart analyze lib/app/app.dart lib/main.dart Expected: 无错误


Task 10: CelebrationOverlay 按需激活

Files:

  • Modify: lib/core/utils/interaction_animations.dart

  • Step 1: 修改 CelebrationOverlay 使用 EffectMutex

// 在 CelebrationOverlayState 中添加
EffectToken? _effectToken;

// 修改 celebrate 方法
void celebrate() async {
  final mutex = EffectMutex(maxConcurrent: 2);
  _effectToken = await mutex.acquire('celebration', priority: EffectPriority.decoration);
  _centerController.play();
}

// 在 ConfettiWidget 的 onComplete 或 _centerController 的状态监听中释放 token
// 修改 initState:
@override
void initState() {
  super.initState();
  _centerController = ConfettiController(
    duration: const Duration(milliseconds: 1500),
  );
  _centerController.addListener(() {
    if (_centerController.state == ConfettiControllerState.stopped && _effectToken != null) {
      _effectToken!.release();
      _effectToken = null;
    }
  });
}

添加 import:

import 'package:xianyan/core/services/performance/effect_mutex.dart';
  • Step 2: 验证

Run: dart analyze lib/core/utils/interaction_animations.dart Expected: 无错误


Task 11: HomeRefreshIndicator 局部刷新优化

Files:

  • Modify: lib/features/home/presentation/home_refresh_indicator.dart

  • Step 1: 将 _pullProgress 改为 ValueNotifier

// 替换 double _pullProgress = 0.0; 为:
final ValueNotifier<double> _pullProgressNotifier = ValueNotifier(0.0);

// 替换所有 setState(() => _pullProgress = newProgress); 为:
_pullProgressNotifier.value = newProgress;

// 替换所有 _pullProgress 读取为 _pullProgressNotifier.value

// 在 _displayProgress getter 中使用 _pullProgressNotifier.value

// 在 _onResetTick 中,将 setState(() {}); 改为直接通知 _pullProgressNotifier
// 由于 _resetController 的 listener 已经在更新,只需确保 AnimatedBuilder 监听 _pullProgressNotifier

// 在 build 方法中,用 AnimatedBuilder 包裹需要响应 _pullProgress 的部分:
// 将 progress 变量改为从 _pullProgressNotifier 获取

注意:由于此文件较复杂,涉及多个状态变量(_isRefreshing, _isComplete, _pullProgress),最安全的做法是将 _pullProgress 改为 ValueNotifier,其他保持 setState 不变(因为它们变化频率低)。

  • Step 2: 验证

Run: dart analyze lib/features/home/presentation/home_refresh_indicator.dart Expected: 无错误


Task 12: 全局编译验证 + CHANGELOG 更新

Files:

  • Modify: CHANGELOG.md

  • Step 1: 运行全局编译验证

Run: cd e:\project\flutter\f\xianyan && flutter analyze Expected: 无错误

  • Step 2: 更新 CHANGELOG.md

在文件顶部添加新版本记录:

## [v14.82.0] - 2026-05-22

### 移动端性能优化 — 智能节流方案

**问题**移动端发热严重GPU/CPU 资源占用高,后台持续消耗电量

**根因分析**
- Fragment Shader 每帧 60fps 无间断运行
- 21+ 个 AnimationController 全天候运行(角色精灵 + Tab 图标)
- 149 处 BackdropFilter 每帧重新模糊
- 8+ 个后台服务持续监听(传感器/剪贴板/电池)
- 多个重量级特效同时运行Lottie + Confetti + Shader + Tilt

**优化方案**(不阉割任何视觉效果):
- ✅ 新增 PerformanceOrchestrator 性能调度中心 — 统一管理帧率/级别/电量联动
- ✅ Shader 帧率节流 — full 60fps / balanced 30fps / saver 20fps
- ✅ 动画前后台感知 — App 后台时自动暂停所有 repeat 动画
- ✅ BackdropFilter 缓存 — RepaintBoundary 减少重绘范围
- ✅ AppLifecycleGate 前后台管理门 — 统一暂停/恢复传感器和服务
- ✅ EffectMutex 特效互斥调度器 — 限制同时运行的重量级特效数量
- ✅ HomeRefreshIndicator 局部刷新 — ValueNotifier 替代 setState
- ✅ AppBarCharacterSprite 局部刷新 — _eyeOffset 改为 ValueNotifier

**新增文件**
- `lib/core/services/performance/performance_orchestrator.dart`
- `lib/core/services/performance/app_lifecycle_gate.dart`
- `lib/core/services/performance/effect_mutex.dart`

**预期效果**
- GPU 平均帧耗时 ↓60%
- 空闲 CPU 占用 ↓80%
- 后台 CPU 占用 ↓95%
- 电池续航接近正常 APP
  • Step 3: 最终验证

Run: cd e:\project\flutter\f\xianyan && flutter analyze Expected: 无错误