Flutter Riverpod 终极指南:从基础到高级状态管理实战

Flutter 简单封装http网络框架
Flutter 实现下拉刷新和自动加载更多
Flutter Banner封装
Flutter使用官方CustomScrollView实现复杂页面下拉刷新和加载更多
Flutter之Router和Navigator实现页面跳转
Flutter的基本应用

引言

本文将全面介绍 Riverpod 的各种 Provider 用法、状态定义策略以及模块化组织的最佳实践,助你构建可维护的大型 Flutter 应用。

一、Riverpod 核心概念与基础

为什么选择 Riverpod?

  • 编译时安全 - 提前发现类型错误
  • 独立于 Widget 树 - 业务逻辑层直接访问状态
  • 精准状态共享 - 避免全局污染
  • 强大的依赖管理 - 自动处理依赖关系
  • 灵活的响应式编程 - 原生支持异步操作

环境配置

dependencies:
  flutter_riverpod: ^2.5.1
  dio: ^5.4.0
  freezed: ^2.4.5 # 状态不可变工具

dev_dependencies:
  build_runner: ^2.4.8
  freezed_annotation: ^2.4.5

二、Riverpod 中的各种 Provider 详解

1. Provider (基础提供者)

使用场景:提供不变的配置或服务

final configProvider = Provider<AppConfig>((ref) {
  return AppConfig(
    apiUrl: "https://api.example.com",
    isDebug: true
  );
});

// 使用
final config = ref.watch(configProvider);

2. StateProvider (简单状态管理)

使用场景:管理简单的可变状态

final counterProvider = StateProvider<int>((ref) => 0);

// 修改状态
ref.read(counterProvider.notifier).state++;

3. FutureProvider (异步数据加载)

使用场景:处理网络请求等异步操作

final userProvider = FutureProvider<User>((ref) async {
  final response = await Dio().get('/user');
  return User.fromJson(response.data);
});

// 使用
ref.watch(userProvider).when(
  data: (user) => Text(user.name),
  loading: () => CircularProgressIndicator(),
  error: (e, _) => Text('Error: $e'),
);

4. StreamProvider (实时数据流)

使用场景:处理实时数据更新

final messagesProvider = StreamProvider<List<Message>>((ref) {
  return FirebaseFirestore.instance
      .collection('messages')
      .snapshots()
      .map((snapshot) => snapshot.docs.map(Message.fromDoc).toList());
});

5. NotifierProvider (复杂状态管理)

使用场景:管理包含业务逻辑的复杂状态

class CounterNotifier extends Notifier<int> {
  @override
  int build() => 0; // 初始状态

  void increment() => state++;
  void decrement() => state--;
  void reset() => state = 0;
}

final counterProvider = NotifierProvider<CounterNotifier, int>(
  CounterNotifier.new
);

6. AsyncNotifierProvider (异步复杂状态)

使用场景:需要异步初始化的复杂状态

class UserProfileNotifier extends AsyncNotifier<UserProfile> {
  @override
  Future<UserProfile> build() async {
    return await _fetchUserProfile();
  }

  Future<void> refresh() async {
    state = const AsyncValue.loading();
    state = await AsyncValue.guard(_fetchUserProfile);
  }
  
  Future<UserProfile> _fetchUserProfile() async {
    // 获取用户资料
  }
}

7. StateNotifierProvider (旧版,将被废弃)

使用场景:兼容旧项目

class Counter extends StateNotifier<int> {
  Counter() : super(0);

  void increment() => state++;
}

final counterProvider = StateNotifierProvider<Counter, int>((ref) => Counter());

三、如何定义和管理状态

1. 状态定义原则

  • 单一职责:每个状态只负责一个功能
  • 不可变性:使用不可变状态确保可预测性
  • 归一化:避免重复数据

2. 使用 Freezed 创建不可变状态

import 'package:freezed_annotation/freezed_annotation.dart';

part 'user_state.freezed.dart';

@freezed
class UserState with _$UserState {
  const factory UserState.initial() = _Initial;
  const factory UserState.loading() = _Loading;
  const factory UserState.success(User user) = _Success;
  const factory UserState.failure(String error) = _Failure;
}

3. 状态管理最佳实践

class UserNotifier extends StateNotifier<UserState> {
  UserNotifier(this.ref) : super(const UserState.initial());

  final Ref ref;

  Future<void> fetchUser() async {
    state = const UserState.loading();
    
    try {
      final user = await ref.read(userRepositoryProvider).fetchUser();
      state = UserState.success(user);
    } catch (e) {
      state = UserState.failure(e.toString());
    }
  }
}

final userProvider = StateNotifierProvider<UserNotifier, UserState>((ref) {
  return UserNotifier(ref);
});

四、模块化项目结构

lib/
├── features/
│   ├── auth/
│   │   ├── data/         # 数据源
│   │   ├── domain/       # 业务逻辑
│   │   └── presentation/ # UI组件
│   ├── product/
│   └── cart/
├── core/
│   ├── api/              # 网络请求
│   ├── providers/        # 全局Provider
│   ├── models/           # 数据模型
│   ├── utils/            # 工具类
│   └── constants/        # 常量
└── app.dart              # 应用入口

五、网络请求模块化实现

1. API 客户端 (core/api/api_client.dart)

class ApiClient {
  final Dio _dio = Dio();
  
  ApiClient() {
    _dio.options.baseUrl = 'https://api.example.com';
    _dio.interceptors.add(LogInterceptor());
    _dio.interceptors.add(AuthInterceptor(ref));
  }
  
  Future<Response> get(String path) => _dio.get(path);
  Future<Response> post(String path, {dynamic data}) => _dio.post(path, data: data);
}

final apiClientProvider = Provider<ApiClient>((ref) => ApiClient());

2. 认证拦截器 (core/api/auth_interceptor.dart)

class AuthInterceptor extends Interceptor {
  final Ref ref;
  
  AuthInterceptor(this.ref);

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    final token = ref.read(authProvider).token;
    if (token != null) {
      options.headers['Authorization'] = 'Bearer $token';
    }
    super.onRequest(options, handler);
  }
}

3. 仓库模式 (features/auth/data/auth_repository.dart)

class AuthRepository {
  final ApiClient apiClient;
  
  AuthRepository(this.apiClient);

  Future<User> login(String email, String password) async {
    final response = await apiClient.post('/login', data: {
      'email': email,
      'password': password
    });
    return User.fromJson(response.data);
  }
}

final authRepositoryProvider = Provider<AuthRepository>((ref) {
  return AuthRepository(ref.read(apiClientProvider));
});

六、状态与 UI 的绑定策略

1. 基本绑定

class ProfileScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final userState = ref.watch(userProvider);
    
    return userState.when(
      initial: () => Placeholder(),
      loading: () => CircularProgressIndicator(),
      success: (user) => UserProfile(user: user),
      failure: (error) => ErrorView(error: error),
    );
  }
}

2. 精细控制刷新

class UserProfile extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 只监听用户名变化
    final name = ref.watch(
      userProvider.select((state) => state.maybeMap(
        success: (s) => s.user.name,
        orElse: () => '',
      ))
    );
    
    return Text(name);
  }
}

3. 状态监听与副作用

class AuthListener extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    ref.listen<AuthState>(authProvider, (previous, next) {
      next.maybeMap(
        unauthenticated: (_) => Navigator.pushNamed(context, '/login'),
        orElse: () {},
      );
    });
    
    return SizedBox.shrink();
  }
}

七、Provider 组合与依赖管理

1. 提供者依赖

final userProfileProvider = FutureProvider<UserProfile>((ref) async {
  // 获取用户ID
  final userId = ref.watch(authProvider).userId;
  // 获取API客户端
  final apiClient = ref.read(apiClientProvider);
  
  return apiClient.get('/users/$userId');
});

2. 提供者组合

final recommendedProductsProvider = FutureProvider<List<Product>>((ref) async {
  final user = await ref.watch(userProvider.future);
  final products = await ref.read(productRepositoryProvider).getRecommended(user.id);
  return products;
});

3. 自动销毁管理

@riverpod
Future<Product> productDetails(ProductDetailsRef ref, String productId) async {
  // 当productId变化或Provider不再使用时自动销毁
  final product = await ref.watch(productRepositoryProvider).getDetails(productId);
  
  // 设置缓存时间(5分钟)
  ref.cacheFor(const Duration(minutes: 5));
  
  return product;
}

八、最佳实践总结

  1. 状态设计原则

    • 使用不可变状态
    • 单一职责原则
    • 状态归一化
  2. Provider 选择指南

    river_pod_select.png

  1. 项目组织建议

    • 按功能模块划分
    • 数据层/领域层/表现层分离
    • 全局Provider放在core目录
  2. 性能优化技巧

    • 使用 select 精细控制重建
    • 使用 autoDispose 自动释放资源
    • 合理设置缓存时间

九、完整示例:购物车模块

1. 状态定义 (features/cart/domain/cart_state.dart)

@freezed
class CartState with _$CartState {
  const factory CartState({
    required List<CartItem> items,
    @Default(false) bool isCheckout,
  }) = _CartState;
}

2. 状态管理 (features/cart/domain/cart_notifier.dart)

class CartNotifier extends Notifier<CartState> {
  @override
  CartState build() => const CartState(items: []);

  void addItem(Product product) {
    state = state.copyWith(
      items: [...state.items, CartItem(product: product, quantity: 1)]
    );
  }
  
  void removeItem(String productId) {
    state = state.copyWith(
      items: state.items.where((item) => item.product.id != productId).toList()
    );
  }
  
  void checkout() async {
    state = state.copyWith(isCheckout: true);
    await _processPayment();
    state = state.copyWith(isCheckout: false, items: []);
  }
}

3. UI 绑定 (features/cart/presentation/cart_screen.dart)

class CartScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final cart = ref.watch(cartProvider);
    
    return Scaffold(
      appBar: AppBar(title: const Text('购物车')),
      body: ListView.builder(
        itemCount: cart.items.length,
        itemBuilder: (context, index) => CartItemTile(item: cart.items[index]),
      ),
      bottomNavigationBar: CheckoutBar(total: _calculateTotal(cart.items)),
    );
  }
}

十、常见问题解答

Q1: 如何选择 StateNotifierProvider 和 NotifierProvider?
A: 优先使用 NotifierProvider,它是 Riverpod 2.0 推荐的新 API,设计更现代

Q2: 什么时候使用 .autoDispose?
A: 当状态不需要持久化时使用,如页面级状态、临时数据

Q3: 如何避免不必要的重建?
A: 使用 select 只监听需要的部分状态

Q4: 如何处理全局状态?
A: 在根目录的 providers 文件夹中定义,不使用 autoDispose

Q5: 如何测试状态管理逻辑?
A: 使用 ProviderContainer 独立测试状态管理逻辑

结语

Riverpod 提供了一个强大而灵活的状态管理解决方案。通过本文的学习,你应该掌握了:

  1. 各种 Provider 的使用场景和区别
  2. 状态设计的最佳实践
  3. 模块化项目的组织方式
  4. 网络请求的封装策略
  5. 状态与 UI 的绑定技巧
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容