【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 优化让我学会了性能分析和优化的方法。

最重要的几点:

  1. Selector 是关键

    • 能用 Selector 的地方就用 Selector
    • 不要用 Consumer
  2. const 能用就用

    • const 可以减少 Widget 创建
    • 提高渲染性能
  3. 不要过度优化

    • 先保证功能正确
    • 再针对性优化
  4. 测试很重要

    • 使用 DevTools 监测性能
    • 量化优化效果

作者:IntMainJhy
创作时间:2026年5月

在这里插入图片描述

更多推荐