0%

项目工程化总结

记录项目工程化步骤

项目基本架构

![image-20251229183737763](/Users/yyu/Library/Application Support/typora-user-images/image-20251229183737763.png)

基本结构:

  • domain下写model,然后state包住model用于管理其状态
  • presentation下面写展示内容,分为controller和主页面,controller里面操作数据,页面仅展示数据

基本流程

  1. 先请求一遍接口,根据接口返回值,用jsonToDart或者jsonToFreezed,或者是直接用AI生成freezed文件,prompts把这段 JSON 转成 Dart 的 Freezed 类,要包含 Default 值,并且处理好嵌套关系 ,然后terminal跑这一段flutter pub run build_runner build --delete-conflicting-outputs生成freezed和g文件。

  2. state包住model

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @freezed
    class UserProfileState with _$UserProfileState {
    const factory UserProfileState({
    // 核心数据
    UserProfileModel? userProfile,

    // UI 状态
    @Default(true) bool isLoading,
    @Default(false) bool permission,
    }) = _UserProfileState;
    }

    类似于这样的结构

  3. 写controller,对于网络数据这种异步获取的数据,Riverpod 2.0 更推荐把 build 变成异步的 (Future),这样就不用手动管理 isLoading,也不用手动触发 loadData,一切由 Riverpod 自动管理。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @override
    Future<UserProfileState> build(int userId) async {
    final data = await RequestUtils.ins().askFor(
    RequestUtils.urls['gc_mine']['getUserInfo'],
    method: "GET",
    data: {"visitedUserId": userId});

    final model = UserProfileModel.fromJson(data);

    return UserProfileState(
    isLoading: false,
    userProfile: model,
    );
    }

    Future<void> refresh() async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(() => build(userId));
    }

    类似于这样的写法

  4. 在页面中获取数据:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    Widget build(BuildContext context) {
    final asyncState = ref.watch(profileControllerProvider(widget.userId));
    return asyncState.when(
    data: (state) {
    // 这里拿到了 UserProfileState
    return Scaffold(
    backgroundColor: GC.backGroundColor,
    body: // 这里拿数据办事
    },
    error: (err, stack) => Text('Error: $err'),
    loading: () => Scaffold(
    backgroundColor: GC.backGroundColor,
    body: Center(
    child: SizedBox(
    width: 32.w,
    height: 32.w,
    child: const CircularProgressIndicator(
    strokeWidth: 2.5,
    ),
    ),
    ),
    ),
    );
    }

    写法也比较简单。

工程化写法有助于理解,这样不会发生过个一年半载回来看代码发现自己也不理解的情况。