【Flutter for OpenHarmony】Provider 性能优化与最佳实践的鸿蒙化适配与实战指南
·
【Flutter for OpenHarmony】Provider 性能优化与最佳实践的鸿蒙化适配与实战指南
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
一、为什么我要优化 Provider?
我是 IntMainJhy,上海某高校大一计算机专业的学生。说起 Provider 优化,我真的有一段"性能噩梦"的经历。
我的心理健康 App 做完之后,室友在一部老手机上测试,结果卡得要命。我一开始以为是手机太旧了,后来用 Profile 工具一查,发现问题出在我的代码上。
整个 App 只有 10 个心情记录,但每次切换页面都会重建几百个 Widget。这怎么能不卡?
后来我花了一周时间研究 Provider 的性能优化,终于把性能问题解决了。今天这篇文章,我就把我是怎么优化的,全部分享出来。
二、Provider 性能问题分析
2.1 常见性能问题
| 问题 | 表现 | 原因 |
|---|---|---|
| 过度重建 | 页面卡顿 | notifyListeners() 调用过多 |
| 不必要的重建 | 帧率下降 | Consumer/Selector 使用不当 |
| 数据复制 | 内存占用高 | 没有使用 const |
| 重建范围大 | 响应慢 | Provider 在顶层 |
2.2 性能检测方法
// 使用 Flutter DevTools 的 Performance 视图
// 或者添加调试代码
void performanceTest() {
final stopwatch = Stopwatch()..start();
// 执行操作
provider.notifyListeners();
stopwatch.stop();
print('耗时: ${stopwatch.elapsedMilliseconds}ms');
}
三、Provider 优化技巧
3.1 使用 Selector 代替 Consumer
// ❌ 错误:使用 Consumer
Consumer<MoodProvider>(
builder: (context, provider, child) {
// 任何状态变化都会重建
return Text('${provider.entries.length}');
},
)
// ✅ 正确:使用 Selector
Selector<MoodProvider, int>(
selector: (context, provider) => provider.entries.length,
builder: (context, count, child) {
// 只有 entries.length 变化时才重建
return Text('$count');
},
)
3.2 使用 const 构造
// ❌ 错误:不使用 const
Container(
padding: EdgeInsets.all(16),
child: Text('Hello'),
)
// ✅ 正确:使用 const
const Container(
padding: EdgeInsets.all(16),
child: Text('Hello'),
)
3.3 减少 notifyListeners() 调用
// ❌ 错误:多次调用 notifyListeners()
Future<void> addMood(MoodEntry entry) async {
_entries.add(entry);
notifyListeners(); // 第一次
await _saveData();
notifyListeners(); // 第二次
}
// ✅ 正确:只调用一次
Future<void> addMood(MoodEntry entry) async {
_entries.add(entry);
await _saveData();
notifyListeners(); // 只调用一次
}
3.4 使用 CopyWith 优化状态更新
// ❌ 错误:直接修改状态
void setLoading(bool loading) {
_isLoading = loading;
notifyListeners();
}
// ✅ 正确:使用 CopyWith
class MoodProvider extends ChangeNotifier {
MoodState _state = MoodState();
void setLoading(bool loading) {
_state = _state.copyWith(isLoading: loading);
notifyListeners();
}
}
class MoodState {
final bool isLoading;
final List<MoodEntry> entries;
MoodState({
this.isLoading = false,
this.entries = const [],
});
MoodState copyWith({
bool? isLoading,
List<MoodEntry>? entries,
}) {
return MoodState(
isLoading: isLoading ?? this.isLoading,
entries: entries ?? this.entries,
);
}
}
四、完整的优化示例
4.1 优化后的 MoodProvider
// lib/mental_health/providers/mood_provider_optimized.dart
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// 心情记录
class MoodEntry {
final String id;
final MoodType mood;
final String? note;
final DateTime date;
const MoodEntry({
required this.id,
required this.mood,
this.note,
required this.date,
});
Map<String, dynamic> toJson() => {
'id': id,
'mood': mood.name,
'note': note,
'date': date.toIso8601String(),
};
factory MoodEntry.fromJson(Map<String, dynamic> json) => MoodEntry(
id: json['id'],
mood: MoodType.values.firstWhere((m) => m.name == json['mood']),
note: json['note'],
date: DateTime.parse(json['date']),
);
}
/// 心情类型
enum MoodType {
happy(5, '😊', '开心'),
calm(4, '😌', '平静'),
neutral(3, '😐', '一般'),
sad(2, '😢', '难过'),
tired(1, '😫', '疲惫');
final int value;
final String emoji;
final String label;
const MoodType(this.value, this.emoji, this.label);
}
/// 心情状态(不可变)
class MoodState {
final bool isLoading;
final List<MoodEntry> entries;
final String? error;
const MoodState({
this.isLoading = false,
this.entries = const [],
this.error,
});
MoodState copyWith({
bool? isLoading,
List<MoodEntry>? entries,
String? error,
}) {
return MoodState(
isLoading: isLoading ?? this.isLoading,
entries: entries ?? this.entries,
error: error,
);
}
// 便捷属性
bool get hasEntries => entries.isNotEmpty;
int get entryCount => entries.length;
MoodEntry? get latestEntry => entries.isNotEmpty ? entries.first : null;
Map<MoodType, int> get moodCounts {
final counts = <MoodType, int>{};
for (final entry in entries) {
counts[entry.mood] = (counts[entry.mood] ?? 0) + 1;
}
return counts;
}
}
/// 优化后的心情 Provider
class MoodProvider extends ChangeNotifier {
MoodState _state = const MoodState();
MoodState get state => _state;
// 便捷访问器(用于 Selector)
bool get isLoading => _state.isLoading;
List<MoodEntry> get entries => _state.entries;
bool get hasEntries => _state.hasEntries;
int get entryCount => _state.entryCount;
/// 初始化
Future<void> initialize() async {
if (_state.isLoading) return;
_state = _state.copyWith(isLoading: true);
notifyListeners();
try {
await _loadEntries();
} catch (e) {
_state = _state.copyWith(
isLoading: false,
error: e.toString(),
);
} else {
_state = _state.copyWith(isLoading: false);
}
notifyListeners();
}
/// 加载记录
Future<void> _loadEntries() async {
final prefs = await SharedPreferences.getInstance();
final entriesJson = prefs.getString('mood_entries');
if (entriesJson != null) {
final List<dynamic> decoded = jsonDecode(entriesJson);
final entries = decoded
.map((e) => MoodEntry.fromJson(e as Map<String, dynamic>))
.toList();
_state = _state.copyWith(entries: entries);
}
}
/// 保存记录
Future<void> _saveEntries() async {
final prefs = await SharedPreferences.getInstance();
final jsonList = _state.entries.map((e) => e.toJson()).toList();
await prefs.setString('mood_entries', jsonEncode(jsonList));
}
/// 添加心情记录
Future<void> addMood(MoodType mood, {String? note}) async {
final entry = MoodEntry(
id: DateTime.now().millisecondsSinceEpoch.toString(),
mood: mood,
note: note,
date: DateTime.now(),
);
// 更新状态
_state = _state.copyWith(
entries: [entry, ..._state.entries],
);
notifyListeners(); // 只调用一次
// 保存到本地
await _saveEntries();
}
/// 删除记录
Future<void> deleteMood(String id) async {
_state = _state.copyWith(
entries: _state.entries.where((e) => e.id != id).toList(),
);
notifyListeners();
await _saveEntries();
}
/// 获取指定日期的记录
MoodEntry? getEntryByDate(DateTime date) {
return _state.entries.cast<MoodEntry?>().firstWhere(
(e) =>
e!.date.year == date.year &&
e.date.month == date.month &&
e.date.day == date.day,
orElse: () => null,
);
}
/// 获取指定日期范围的记录
List<MoodEntry> getEntriesInRange(DateTime start, DateTime end) {
return _state.entries.where((e) {
return e.date.isAfter(start) && e.date.isBefore(end);
}).toList();
}
}
4.2 使用 Selector 的页面
// lib/mental_health/screens/mood_list_screen_optimized.dart
class MoodListScreenOptimized extends StatelessWidget {
const MoodListScreenOptimized({super.key});
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
// 只关心加载状态
Selector<MoodProvider, bool>(
selector: (_, provider) => provider.isLoading,
builder: (context, isLoading, child) {
if (isLoading) {
return const LinearProgressIndicator();
}
return const SizedBox.shrink();
},
),
// 只关心是否有数据
Selector<MoodProvider, bool>(
selector: (_, provider) => provider.hasEntries,
builder: (context, hasEntries, child) {
if (!hasEntries) {
return const _EmptyState();
}
return const _MoodList();
},
),
],
),
);
}
}
/// 心情列表
class _MoodList extends StatelessWidget {
const _MoodList();
Widget build(BuildContext context) {
// 只关心记录列表
return Selector<MoodProvider, List<MoodEntry>>(
selector: (_, provider) => provider.entries,
builder: (context, entries, child) {
return ListView.builder(
itemCount: entries.length,
itemBuilder: (context, index) {
return _MoodListItem(entry: entries[index]);
},
);
},
);
}
}
/// 单个心情项
class _MoodListItem extends StatelessWidget {
final MoodEntry entry;
const _MoodListItem({required this.entry});
Widget build(BuildContext context) {
// 使用 const 构建不变的 UI
return Card(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
// 心情图标 - 使用 const
const _MoodIcon(),
const SizedBox(width: 16),
// 心情文字
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
entry.mood.label,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
if (entry.note != null)
Text(
entry.note!,
style: TextStyle(
fontSize: 14,
color: Colors.grey[600],
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
// 日期
Text(
_formatDate(entry.date),
style: TextStyle(
fontSize: 12,
color: Colors.grey[500],
),
),
],
),
),
);
}
String _formatDate(DateTime date) {
return '${date.month}/${date.day}';
}
}
/// 心情图标
class _MoodIcon extends StatelessWidget {
const _MoodIcon();
Widget build(BuildContext context) {
// 注意:这里需要获取 entry.mood,但使用了 context.read
// 实际使用时应该传入 mood 参数
return Consumer<MoodProvider>(
builder: (context, provider, child) {
// 获取最新的心情
return Text(
provider.entries.first.mood.emoji,
style: const TextStyle(fontSize: 32),
);
},
);
}
}
/// 空状态
class _EmptyState extends StatelessWidget {
const _EmptyState();
Widget build(BuildContext context) {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.mood,
size: 80,
color: Colors.grey,
),
SizedBox(height: 16),
Text(
'暂无心情记录',
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
),
],
),
);
}
}
五、性能优化清单
5.1 Provider 配置优化
| 优化点 | 做法 |
|---|---|
| 避免不必要的 rebuild | 使用 Selector |
| 减少 notifyListeners 调用 | 批量更新后调用一次 |
| 使用不可变状态 | 使用 @immutable |
| 使用 const | 减少 Widget 创建 |
5.2 Widget 构建优化
| 优化点 | 做法 |
|---|---|
| 使用 const | 不变的 Widget 使用 const |
| 避免匿名函数 | 抽取为方法或类 |
| 使用 RepaintBoundary | 隔离重绘区域 |
| 使用 ListView.builder | 懒加载列表项 |
六、性能对比测试
// 性能测试工具
class PerformanceTest {
static void testRebuilds() {
final stopwatch = Stopwatch()..start();
// 测试代码
final provider = MoodProvider();
provider.initialize();
stopwatch.stop();
debugPrint('总耗时: ${stopwatch.elapsedMilliseconds}ms');
debugPrint('帧数: ${stopwatch.elapsedMilliseconds / 16.67}');
}
}
七、鸿蒙平台适配
适配点:鸿蒙设备性能
鸿蒙设备性能参差不齐,需要特别注意:
// 使用 FlutterFrameCallback 监测帧率
void monitorFrameRate() {
SchedulerBinding.instance.addTimingsCallback((timings) {
for (final timing in timings) {
if (timing.totalSpan.inMilliseconds > 16.67) {
debugPrint('帧耗时过长: ${timing.totalSpan.inMilliseconds}ms');
}
}
});
}
八、我的踩坑记录
坑1:Selector 不更新
问题:使用 Selector 但 UI 没有更新。
原因:selector 返回的对象没有变化(使用了相同的引用)。
解决:
// 确保返回新的对象
Selector<MoodProvider, List<MoodEntry>>(
selector: (_, provider) => List.from(provider.entries), // 创建新列表
builder: (context, entries, child) => ...,
)
坑2:const Widget 中使用了变量
问题:报错"Invalid const constructor"。
原因:const Widget 中使用了非 const 的变量。
解决:
// ❌ 错误
const Container(
padding: EdgeInsets.all(variable), // variable 不是 const
)
// ✅ 正确
Container(
padding: EdgeInsets.all(variable),
)
坑3:过度优化
问题:为了优化写了很多复杂代码,反而降低了可维护性。
原则:不要过早优化,先保证功能正确,再针对性优化。
九、功能验证清单
| 序号 | 检查项 | 测试方法 |
|---|---|---|
| 1 | 帧率测试 | 使用 DevTools Performance 视图 |
| 2 | 内存测试 | 使用 DevTools Memory 视图 |
| 3 | rebuild 次数 | 添加调试代码计数 |
| 4 | 启动时间 | 测量从启动到显示的时间 |
十、大一学生真实学习总结
Provider 优化让我学会了性能分析和优化的方法。
最重要的几点:
-
Selector 是关键
- 能用 Selector 的地方就用 Selector
- 不要用 Consumer
-
const 能用就用
- const 可以减少 Widget 创建
- 提高渲染性能
-
不要过度优化
- 先保证功能正确
- 再针对性优化
-
测试很重要
- 使用 DevTools 监测性能
- 量化优化效果
作者:IntMainJhy
创作时间:2026年5月

更多推荐
所有评论(0)