主要变更: 1. 重构多处Provider初始化逻辑,使用Future.microtask避免build阶段修改state 2. 重命名"灵感"模块为"工作流","天气诗词"改为"情景诗词" 3. 新增插件系统页面与路由,添加speech_to_text_windows插件支持 4. 优化玻璃容器性能,添加性能节流与模糊值适配 5. 新增离线横幅、阅读体验控制器、手势控制器等组件 6. 完善头像审核状态展示与缓存管理 7. 修复键盘弹出与页面路由问题 8. 更新依赖与项目配置,优化widget默认数据
200 lines
6.1 KiB
Dart
200 lines
6.1 KiB
Dart
// ============================================================
|
||
// 闲言APP — 性能优化工具类
|
||
// 创建时间: 2026-05-25
|
||
// 更新时间: 2026-05-25
|
||
// 作用: 全局帧率控制、动画节流、指针事件防抖、GPU负载降级
|
||
// 上次更新: 修复currentFrameTimeStamp断言崩溃—使用Stopwatch替代
|
||
// ============================================================
|
||
|
||
import 'dart:async';
|
||
|
||
import 'package:flutter/widgets.dart';
|
||
|
||
import 'package:xianyan/core/services/performance/performance_orchestrator.dart';
|
||
import 'package:xianyan/core/utils/logger.dart';
|
||
|
||
/// 动画帧率类别
|
||
enum AnimationFpsCategory {
|
||
/// 装饰性动画 — full:30fps balanced:24fps saver:15fps
|
||
decorative,
|
||
|
||
/// 空闲动画 — full:20fps balanced:15fps saver:10fps
|
||
idle,
|
||
|
||
/// 交互动画 — full:60fps balanced:40fps saver:30fps
|
||
interaction,
|
||
}
|
||
|
||
/// 帧率节流器 — 基于时间戳控制动画更新频率
|
||
///
|
||
/// AnimationController每帧(60/120fps)触发listener,
|
||
/// 但widget rebuild才是CPU/GPU开销的大头。
|
||
/// FrameRateThrottler在listener中判断是否应该触发rebuild,
|
||
/// 将rebuild频率从60fps降低到目标帧率。
|
||
///
|
||
/// 使用Stopwatch计时,避免在非帧回调中访问currentFrameTimeStamp
|
||
/// 导致_AssertionError('scheduler/binding.dart': _currentFrameTimeStamp != null)
|
||
class FrameRateThrottler {
|
||
FrameRateThrottler(this._category)
|
||
: _stopwatch = Stopwatch()..start(),
|
||
_lastUpdateTime = Duration.zero;
|
||
|
||
final AnimationFpsCategory _category;
|
||
final Stopwatch _stopwatch;
|
||
Duration _lastUpdateTime;
|
||
|
||
/// 根据性能级别和动画类别获取目标帧率
|
||
int get targetFps {
|
||
final level = PerformanceOrchestrator.instance.level;
|
||
return switch (_category) {
|
||
AnimationFpsCategory.decorative => switch (level) {
|
||
PerformanceLevel.full => 30,
|
||
PerformanceLevel.balanced => 24,
|
||
PerformanceLevel.saver => 15,
|
||
},
|
||
AnimationFpsCategory.idle => switch (level) {
|
||
PerformanceLevel.full => 20,
|
||
PerformanceLevel.balanced => 15,
|
||
PerformanceLevel.saver => 10,
|
||
},
|
||
AnimationFpsCategory.interaction => switch (level) {
|
||
PerformanceLevel.full => 60,
|
||
PerformanceLevel.balanced => 40,
|
||
PerformanceLevel.saver => 30,
|
||
},
|
||
};
|
||
}
|
||
|
||
Duration get _targetInterval => Duration(microseconds: 1000000 ~/ targetFps);
|
||
|
||
/// 判断本帧是否应该触发UI更新
|
||
///
|
||
/// 使用Stopwatch.elapsed替代currentFrameTimeStamp,
|
||
/// 避免在非帧回调期间访问currentFrameTimeStamp触发断言错误。
|
||
bool shouldUpdate() {
|
||
final now = _stopwatch.elapsed;
|
||
if (_lastUpdateTime == Duration.zero ||
|
||
now - _lastUpdateTime >= _targetInterval) {
|
||
_lastUpdateTime = now;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void reset() {
|
||
_stopwatch.reset();
|
||
_stopwatch.start();
|
||
_lastUpdateTime = Duration.zero;
|
||
}
|
||
}
|
||
|
||
/// 防抖值通知器 — 限制值变更通知频率
|
||
///
|
||
/// 适用于指针移动等高频事件,避免每次pointer move都触发rebuild。
|
||
/// 在指定时间窗口内只发送最后一次值变更通知。
|
||
class DebouncedValueNotifier<T> extends ValueNotifier<T> {
|
||
DebouncedValueNotifier(
|
||
T initialValue, {
|
||
this.debounceDuration = const Duration(milliseconds: 16),
|
||
}) : _pendingValue = initialValue,
|
||
super(initialValue);
|
||
|
||
final Duration debounceDuration;
|
||
Timer? _debounceTimer;
|
||
T _pendingValue;
|
||
|
||
@override
|
||
set value(T newValue) {
|
||
if (newValue == _pendingValue) return;
|
||
_pendingValue = newValue;
|
||
_debounceTimer?.cancel();
|
||
_debounceTimer = Timer(debounceDuration, () {
|
||
if (_pendingValue != super.value) {
|
||
super.value = _pendingValue;
|
||
}
|
||
});
|
||
}
|
||
|
||
/// 立即更新值(跳过防抖),用于交互结束时确保最终值正确
|
||
void updateImmediate(T newValue) {
|
||
_debounceTimer?.cancel();
|
||
_pendingValue = newValue;
|
||
if (newValue != super.value) {
|
||
super.value = newValue;
|
||
}
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
_debounceTimer?.cancel();
|
||
super.dispose();
|
||
}
|
||
}
|
||
|
||
/// 全局性能优化器 — 提供GPU负载降级建议
|
||
///
|
||
/// 根据PerformanceOrchestrator的当前性能级别,
|
||
/// 为各组件提供模糊值、粒子数等参数的降级建议。
|
||
class PerformanceOptimizer {
|
||
PerformanceOptimizer._();
|
||
static final PerformanceOptimizer instance = PerformanceOptimizer._();
|
||
|
||
/// 获取BackdropFilter模糊建议值
|
||
double suggestedBlurSigma(double originalSigma) {
|
||
final level = PerformanceOrchestrator.instance.level;
|
||
return switch (level) {
|
||
PerformanceLevel.full => originalSigma,
|
||
PerformanceLevel.balanced => originalSigma * 0.7,
|
||
PerformanceLevel.saver => originalSigma * 0.5,
|
||
};
|
||
}
|
||
|
||
/// 获取粒子数量建议值
|
||
int suggestedParticleCount(int originalCount) {
|
||
final level = PerformanceOrchestrator.instance.level;
|
||
return switch (level) {
|
||
PerformanceLevel.full => originalCount,
|
||
PerformanceLevel.balanced => (originalCount * 0.6).round().clamp(
|
||
1,
|
||
originalCount,
|
||
),
|
||
PerformanceLevel.saver => (originalCount * 0.3).round().clamp(
|
||
1,
|
||
originalCount,
|
||
),
|
||
};
|
||
}
|
||
|
||
/// 获取色块数量建议值
|
||
int suggestedBlobCount(int originalCount) {
|
||
final level = PerformanceOrchestrator.instance.level;
|
||
return switch (level) {
|
||
PerformanceLevel.full => originalCount,
|
||
PerformanceLevel.balanced => (originalCount * 0.8).round().clamp(
|
||
2,
|
||
originalCount,
|
||
),
|
||
PerformanceLevel.saver => (originalCount * 0.6).round().clamp(
|
||
2,
|
||
originalCount,
|
||
),
|
||
};
|
||
}
|
||
|
||
/// saver模式下禁用BackdropFilter
|
||
bool get shouldEnableBackdropFilter =>
|
||
PerformanceOrchestrator.instance.level != PerformanceLevel.saver;
|
||
|
||
/// saver模式下禁用粒子效果
|
||
bool get shouldEnableParticles =>
|
||
PerformanceOrchestrator.instance.level != PerformanceLevel.saver;
|
||
|
||
void logDiagnostics() {
|
||
final level = PerformanceOrchestrator.instance.level;
|
||
final isForeground = PerformanceOrchestrator.instance.isAppForeground;
|
||
Log.i(
|
||
'PerformanceOptimizer: level=${level.label}, foreground=$isForeground',
|
||
);
|
||
}
|
||
}
|