You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter:将Riverpod Provider代码重构为控制器与服务架构

Flutter Riverpod代码重构:分离Controller与Service

原有可运行代码

@freezed
class LessonDOState with _$LessonDOState {
  const factory LessonDOState(
      {@Default([]) List<LessonDO> lessons,
      @Default({}) Map<int, LessonDO> lessonMap,
      @Default(true) isLoading}) = _LessonDOState;

  const LessonDOState._();
}

final lessonsProvider = StateNotifierProvider<LessonDONotifier, LessonDOState>(
    (ref) => LessonDONotifier());

class LessonDONotifier extends StateNotifier<LessonDOState> {
  LessonDONotifier() : super(const LessonDOState()) {
    loadLessonDOs();
  }

  loadLessonDOs() async {
    final lessonsData = await rootBundle.rootBundle.loadString('assets/json/lessons.json');
    // 原代码遗漏JSON解析步骤,重构时已补充
    final lessonsList = jsonDecode(lessonsData) as List;
    final lessons = lessonsList.map((e) => LessonDO.fromJson(e)).toList();
    final lessonMap = listToMap(lessons);
    final isLoading = lessons.isEmpty;
    state = state.copyWith(
        lessons: lessons, lessonMap: lessonMap, isLoading: isLoading);
    
  }

  Map<int, LessonDO> listToMap(List<LessonDO> lessons) {
    Map<int, LessonDO> map = {};
    for (LessonDO lesson in lessons) {
      map[lesson.number!] = lesson;
    }
    return map;
  }
}

重构后代码

按照Controller(Provider)与Service分离的思路,将数据获取逻辑抽离到Service层,Controller仅负责状态管理。

1. lesson_service.dart(Service层)

专注处理数据加载、解析与转换,封装数据获取细节:

final lessonService = Provider((ref) => LessonService());

class LessonService {
  Future<List<LessonDO>> loadLessons() async {
    try {
      final lessonsData = await rootBundle.loadString('assets/json/lessons.json');
      final lessonsList = jsonDecode(lessonsData) as List;
      return lessonsList.map((e) => LessonDO.fromJson(e)).toList();
    } catch (e) {
      rethrow;
    }
  }

  Map<int, LessonDO> convertLessonsToMap(List<LessonDO> lessons) {
    final Map<int, LessonDO> map = {};
    for (final lesson in lessons) {
      if (lesson.number != null) {
        map[lesson.number!] = lesson;
      }
    }
    return map;
  }
}

2. lesson_provider.dart(Controller层)

负责状态管理,依赖Service层获取数据,使用AsyncValue统一处理加载、成功、失败状态:

final lessonsProvider = StateNotifierProvider.autoDispose<LessonNotifier, AsyncValue<LessonDOState>>(
  (ref) => LessonNotifier(ref),
);

class LessonNotifier extends StateNotifier<AsyncValue<LessonDOState>> {
  final AutoDisposeStateNotifierProviderRef _ref;
  late final LessonService _lessonService;

  LessonNotifier(this._ref) : super(const AsyncValue.data(LessonDOState())) {
    _lessonService = _ref.watch(lessonService);
    _loadLessons();
  }

  Future<void> _loadLessons() async {
    state = const AsyncValue.loading();
    final result = await AsyncValue.guard(() async {
      final lessons = await _lessonService.loadLessons();
      final lessonMap = _lessonService.convertLessonsToMap(lessons);
      return LessonDOState(
        lessons: lessons,
        lessonMap: lessonMap,
        isLoading: false,
      );
    });
    state = result;
  }

  // 暴露外部重新加载数据的方法
  Future<void> reloadLessons() => _loadLessons();
}

重构核心要点

  • 职责分离:Service层专注数据操作,Controller层专注状态管理,降低耦合度,便于单元测试和代码复用。
  • 状态统一管理:使用AsyncValue封装加载、成功、失败三种状态,简化UI层的状态判断逻辑。
  • 资源自动清理:使用autoDispose修饰Provider,在不再被监听时自动释放资源,避免内存泄漏。
  • 补全遗漏逻辑:原代码缺少JSON解析步骤,重构时已补充完整。

内容的提问来源于stack exchange,提问作者Yuriy N.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 06:37:22