Flutter Repository Pattern

很久不写 Flutter 了,最近在写一个项目,用到了 Repository Pattern,然后也用到了 riverpod,还记得当时学 flutter 时候用的 provider,然后其他项目又用到了 bloc,现在又用到了 riverpod,感觉 flutter 的生态真的是太丰富了。

全局使用 riverpod,太方便了,想到就用,直接 ref 一把嗦,爽歪歪。然后本地状态用 flutter_hooks,配合起来用效果更好。

3.7

用 claude 3.7 解析,然后生成流程图。
是的,你说得对!WaterDio 是实际处理 HTTP 请求的核心组件,让我更新完整的流程图:

Dio Layer

API Layer

Repository Pattern

UI Layer

1.ref.read

2.实例化

3._apiCall

4.请求

5.HTTP请求

继承

配置

6.HTTP响应

7.响应处理

8.数据转换

9.返回数据

Response Flow

login_user.dart
登录页面

data_providers.dart
adminApiRepo

admin_api_implements.dart
AdminApiImplement

xxx_project_api_source.dart
xxxProjectApiSource

UserDio

Backend Server

_BaseDio

Dio实例
拦截器/超时/headers等

完整的文件结构:

lib/
├── app/
│   └── pages/
│       └── auth/
│           └── login_user.dart                # UI 层
├── data/
│   ├── data_providers.dart                    # Provider 定义
│   ├── repositories/
│   │   ├── admin_api_repository.dart         # 接口定义
│   │   └── implements/
│   │       └── admin_api_implements.dart     # 接口实现
│   ├── sources/
│   │   └── xxx_project_api_source.dart     # API 调用封装
│   ├── dio/
│   │   ├── xxx_dio.dart                    # Dio 实现
│   │   └── base_dio.dart                     # 基础 Dio 配置
│   └── models/
│       └── auth/
│           ├── login_request.dart            # 请求模型
│           └── login_response.dart           # 响应模型

_BaseDio 通常包含的功能:

class _BaseDio {
  late final Dio _dio;

  _BaseDio() {
    _dio = Dio()
      ..options = BaseOptions(
        baseUrl: 'API_BASE_URL',
        connectTimeout: Duration(seconds: 30),
        receiveTimeout: Duration(seconds: 30),
      )
      ..interceptors.addAll([
        // 日志拦截器
        LogInterceptor(),
        // 错误处理拦截器
        ErrorInterceptor(),
        // Token 拦截器
        TokenInterceptor(),
        // 重试拦截器
        RetryInterceptor(),
      ]);
  }
}

UserDio 的特定实现:

class UserDio extends _BaseDio {
  // 用户相关的特定配置
  UserDio() {
    _dio.interceptors.add(
      InterceptorsWrapper(
        onRequest: (options, handler) {
          // 添加用户相关的 headers
          options.headers['Authorization'] = 'Bearer $userToken';
          return handler.next(options);
        },
      ),
    );
  }

  // 封装具体的请求方法
  Future<Response<T>> get<T>(...) => _dio.get(...);
  Future<Response<T>> post<T>(...) => _dio.post(...);
}

完整的调用链:

  1. UI 触发请求
  2. Repository 处理业务逻辑
  3. ApiSource 封装 API 调用
  4. UserDio 处理 HTTP 请求
    • 请求拦截(headers、token 等)
    • 错误处理
    • 重试机制
    • 日志记录
  5. 服务器响应
  6. 响应处理(数据转换、错误处理)
  7. 返回业务数据

这种架构的优势:

  1. 统一的网络层配置
  2. 灵活的拦截器机制
  3. 统一的错误处理
  4. 可配置的重试策略
  5. 完整的日志系统

o1 基础介绍

简单扼要的介绍下 flutter 的 resposity pattern 网络请求模式,用 retrofit。业务层用 freezed,riverpod annotation,riverpod hooks,flutter hook

下面给出一个简要示例,演示如何在 Flutter 中结合 retrofit(网络请求)、freezed(数据类)、riverpod_annotation(Riverpod 代码生成)、riverpod_hooks / flutter_hooks(Hooks)构建一套「Repository + Provider」的数据请求层级。代码示例仅作思路参考,实际业务可根据需要扩充。

1. 添加依赖

pubspec.yaml 中(示例):

dependencies:
  flutter:
    sdk: flutter
  dio: ^5.1.0
  retrofit: ^4.0.1
  json_annotation: ^4.8.0
  freezed_annotation: ^2.2.0
  hooks_riverpod: ^2.3.6
  flutter_hooks: ^0.18.6

dev_dependencies:
  build_runner: ^2.3.3
  retrofit_generator: ^7.0.1
  json_serializable: ^6.6.0
  freezed: ^2.2.0
  riverpod_annotation: ^2.0.2

2. 定义数据模型(freezed

以「用户信息」为例,结合 freezed + json_serializable 来简化序列化。

// user.dart
import 'package:freezed_annotation/freezed_annotation.dart';

part 'user.freezed.dart';
part 'user.g.dart';


class User with _$User {
  const factory User({
    required String id,
    required String name,
  }) = _User;

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

执行 flutter pub run build_runner build 后,会生成 user.freezed.dartuser.g.dart

3. 定义 Retrofit 接口

使用 retrofit + dio

// api_service.dart
import 'package:dio/dio.dart';
import 'package:retrofit/retrofit.dart';
import 'user.dart';

part 'api_service.g.dart';

(baseUrl: "https://example.com/api")
abstract class ApiService {
  factory ApiService(Dio dio, {String baseUrl}) = _ApiService;

  ("/users/{id}")
  Future<User> getUser(("id") String id);

  ("/users")
  Future<List<User>> getUsers();
}

同样执行 flutter pub run build_runner build 来生成 api_service.g.dart

4. 定义 Repository 接口和实现

4.1. Repository 接口

// user_repository.dart
import 'user.dart';

abstract class UserRepository {
  Future<User> fetchUserById(String id);
  Future<List<User>> fetchAllUsers();
}

4.2. 具体实现

// user_repository_impl.dart
import 'package:dio/dio.dart';
import 'api_service.dart';
import 'user.dart';
import 'user_repository.dart';

class UserRepositoryImpl implements UserRepository {
  final ApiService _apiService;

  UserRepositoryImpl(Dio dio) : _apiService = ApiService(dio);

  
  Future<User> fetchUserById(String id) => _apiService.getUser(id);

  
  Future<List<User>> fetchAllUsers() => _apiService.getUsers();
}

5. 在 Riverpod 中声明 Provider

利用 riverpod_annotation(自动生成 Provider),示例中声明了一个 UserRepository 的 Provider,以及一个获取用户列表的异步 Provider。

// user_providers.dart
import 'package:dio/dio.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'user.dart';
import 'user_repository.dart';
import 'user_repository_impl.dart';

part 'user_providers.g.dart';

// 1) 声明一个 Repository Provider

UserRepository userRepository(UserRepositoryRef ref) {
  final dio = Dio(); // 如果有全局 dio 也可从别的 provider 里获取
  return UserRepositoryImpl(dio);
}

// 2) 声明一个异步 Provider,用于获取用户列表

Future<List<User>> userList(UserListRef ref) {
  return ref.watch(userRepositoryProvider).fetchAllUsers();
}

// 你也可以声明获取单个用户的 Provider,或更多业务逻辑

执行 flutter pub run build_runner build 后,会生成 user_providers.g.dart

这样就有了:

  • userRepositoryProvider:返回 UserRepositoryImpl 实例
  • userListProvider:返回一个异步 Future<List<User>>,依赖 userRepositoryProvider

6. 在界面层(Widget)中使用 Hooks + Riverpod

在 Flutter 中,可以使用 HookConsumerWidget 来消费 Riverpod Provider,并结合 Hooks 管理状态、生命周期等。示例:

// user_list_page.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'user_providers.dart';

class UserListPage extends HookConsumerWidget {
  const UserListPage({Key? key}) : super(key: key);

  
  Widget build(BuildContext context, WidgetRef ref) {
    final asyncUserList = ref.watch(userListProvider);

    return Scaffold(
      appBar: AppBar(title: const Text("User List")),
      body: asyncUserList.when(
        data: (users) => ListView.builder(
          itemCount: users.length,
          itemBuilder: (_, index) {
            final user = users[index];
            return ListTile(
              title: Text(user.name),
              subtitle: Text('ID: ${user.id}'),
            );
          },
        ),
        loading: () => const Center(child: CircularProgressIndicator()),
        error: (err, st) => Center(child: Text("Error: $err")),
      ),
    );
  }
}
  • ref.watch(userListProvider) 会触发异步加载并返回 AsyncValue<List<User>>
  • asyncUserList.when(...) 用于根据 loading / data / error 不同状态显示不同 UI。

如果需要主动刷新,可以调用 ref.refresh(userListProvider)ref.invalidate(userListProvider) 等方法。

7. 总结

  1. 数据层(Repository + Retrofit):使用 retrofit + dio 封装所有网络请求;
  2. 模型层(freezed + json_serializable:自动生成不可变数据类,简化序列化/反序列化;
  3. 业务注入(riverpod_annotation:通过 @riverpod 自动生成各种 Provider(如 Repository Provider、数据 Provider),上层只需 ref.watch(...) 即可拿到数据;
  4. UI 层(HookConsumerWidget):使用 Hooks + Riverpod 的方式来管理状态、消费 Provider,轻松地监听数据更新并刷新界面。

通过以上流程,可以把「数据获取逻辑」与「UI/业务」进行良好解耦,并利用 Riverpod 的 Provider 机制,实现可测试、可维护的整体结构。