关注

Flutter Riverpod 状态管理:构建可扩展的应用架构

Flutter Riverpod 状态管理:构建可扩展的应用架构

掌握 Riverpod 的核心概念和最佳实践,构建更加可维护的 Flutter 应用。

一、Riverpod 简介

作为一名追求像素级还原的 UI 匠人,我对 Flutter 的状态管理方案有着深入的研究。Riverpod 是由 Flutter 社区知名开发者 Remi Rousselet 开发的状态管理库,它是 Provider 的继任者,提供了更强大、更灵活的状态管理解决方案。从简单的局部状态到复杂的全局状态,Riverpod 为我们提供了一套完整的状态管理工具。

二、核心概念

1. Provider

import 'package:flutter_riverpod/flutter_riverpod.dart';

// 创建一个简单的 provider
final counterProvider = StateProvider((ref) => 0);

// 在组件中使用
class CounterWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    
    return Scaffold(
      appBar: AppBar(title: Text('计数器')),
      body: Center(
        child: Text('计数: $count'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          ref.read(counterProvider.notifier).state++;
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

2. StateNotifierProvider

// 定义状态类
class Todo {
  final String id;
  final String title;
  final bool completed;
  
  Todo({required this.id, required this.title, this.completed = false});
  
  Todo copyWith({String? id, String? title, bool? completed}) {
    return Todo(
      id: id ?? this.id,
      title: title ?? this.title,
      completed: completed ?? this.completed,
    );
  }
}

// 定义状态管理器
class TodoNotifier extends StateNotifier<List<Todo>> {
  TodoNotifier() : super([]);
  
  void addTodo(String title) {
    final newTodo = Todo(
      id: DateTime.now().toString(),
      title: title,
    );
    state = [...state, newTodo];
  }
  
  void toggleTodo(String id) {
    state = state.map((todo) {
      if (todo.id == id) {
        return todo.copyWith(completed: !todo.completed);
      }
      return todo;
    }).toList();
  }
  
  void removeTodo(String id) {
    state = state.where((todo) => todo.id != id).toList();
  }
}

// 创建 provider
final todoProvider = StateNotifierProvider<TodoNotifier, List<Todo>>((ref) {
  return TodoNotifier();
});

// 在组件中使用
class TodoListWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final todos = ref.watch(todoProvider);
    
    return Scaffold(
      appBar: AppBar(title: Text('待办事项')),
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, index) {
          final todo = todos[index];
          return ListTile(
            title: Text(
              todo.title,
              style: TextStyle(
                decoration: todo.completed ? TextDecoration.lineThrough : null,
              ),
            ),
            trailing: Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                Checkbox(
                  value: todo.completed,
                  onChanged: (_) {
                    ref.read(todoProvider.notifier).toggleTodo(todo.id);
                  },
                ),
                IconButton(
                  icon: Icon(Icons.delete),
                  onPressed: () {
                    ref.read(todoProvider.notifier).removeTodo(todo.id);
                  },
                ),
              ],
            ),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 显示添加待办事项的对话框
          showDialog(
            context: context,
            builder: (context) {
              final controller = TextEditingController();
              return AlertDialog(
                title: Text('添加待办事项'),
                content: TextField(
                  controller: controller,
                  decoration: InputDecoration(hintText: '输入待办事项'),
                ),
                actions: [
                  TextButton(
                    onPressed: () => Navigator.pop(context),
                    child: Text('取消'),
                  ),
                  TextButton(
                    onPressed: () {
                      if (controller.text.isNotEmpty) {
                        ref.read(todoProvider.notifier).addTodo(controller.text);
                        Navigator.pop(context);
                      }
                    },
                    child: Text('添加'),
                  ),
                ],
              );
            },
          );
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

3. FutureProvider

// 创建 FutureProvider
final userProvider = FutureProvider<User>((ref) async {
  final apiService = ref.watch(apiServiceProvider);
  return await apiService.getUser();
});

// 定义数据模型
class User {
  final String id;
  final String name;
  final String email;
  
  User({required this.id, required this.name, required this.email});
  
  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'],
      name: json['name'],
      email: json['email'],
    );
  }
}

// 定义 API 服务
class ApiService {
  Future<User> getUser() async {
    // 模拟网络请求
    await Future.delayed(Duration(seconds: 2));
    return User(
      id: '1',
      name: '张三',
      email: '[email protected]',
    );
  }
}

final apiServiceProvider = Provider((ref) => ApiService());

// 在组件中使用
class UserWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final userAsyncValue = ref.watch(userProvider);
    
    return userAsyncValue.when(
      data: (user) => Column(
        children: [
          Text('ID: ${user.id}'),
          Text('姓名: ${user.name}'),
          Text('邮箱: ${user.email}'),
        ],
      ),
      loading: () => CircularProgressIndicator(),
      error: (error, stackTrace) => Text('错误: $error'),
    );
  }
}

4. StreamProvider

// 创建 StreamProvider
final timerProvider = StreamProvider<int>((ref) {
  return Stream.periodic(Duration(seconds: 1), (count) => count);
});

// 在组件中使用
class TimerWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final timerAsyncValue = ref.watch(timerProvider);
    
    return timerAsyncValue.when(
      data: (count) => Text('秒数: $count'),
      loading: () => CircularProgressIndicator(),
      error: (error, stackTrace) => Text('错误: $error'),
    );
  }
}

三、高级用法

1. 依赖注入

// 定义服务
class AuthService {
  Future<bool> login(String email, String password) async {
    // 模拟登录
    await Future.delayed(Duration(seconds: 1));
    return email == '[email protected]' && password == 'password';
  }
  
  void logout() {
    // 执行登出操作
  }
}

class ApiService {
  final AuthService authService;
  
  ApiService(this.authService);
  
  Future<Map<String, dynamic>> fetchData() async {
    // 模拟 API 请求
    await Future.delayed(Duration(seconds: 1));
    return {'data': 'Hello World'};
  }
}

// 创建 providers
final authServiceProvider = Provider((ref) => AuthService());

final apiServiceProvider = Provider((ref) {
  final authService = ref.watch(authServiceProvider);
  return ApiService(authService);
});

// 在组件中使用
class HomeWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final apiService = ref.watch(apiServiceProvider);
    
    return Scaffold(
      appBar: AppBar(title: Text('首页')),
      body: Center(
        child: ElevatedButton(
          onPressed: () async {
            final data = await apiService.fetchData();
            print(data);
          },
          child: Text('获取数据'),
        ),
      ),
    );
  }
}

2. 状态组合

// 定义 providers
final counterProvider = StateProvider((ref) => 0);

final doubledCounterProvider = Provider((ref) {
  final count = ref.watch(counterProvider);
  return count * 2;
});

// 在组件中使用
class CounterWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    final doubledCount = ref.watch(doubledCounterProvider);
    
    return Scaffold(
      appBar: AppBar(title: Text('计数器')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('计数: $count'),
            Text('双倍计数: $doubledCount'),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          ref.read(counterProvider.notifier).state++;
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

3. 作用域管理

// 创建一个作用域 provider
final userIdProvider = Provider<String>((ref) => throw UnimplementedError());

final userProvider = FutureProvider<User>((ref) async {
  final userId = ref.watch(userIdProvider);
  final apiService = ref.watch(apiServiceProvider);
  return await apiService.getUserById(userId);
});

// 在组件中使用
class UserProfileWidget extends ConsumerWidget {
  final String userId;
  
  UserProfileWidget(this.userId);
  
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return ProviderScope(
      overrides: [
        userIdProvider.overrideWithValue(userId),
      ],
      child: UserProfileContent(),
    );
  }
}

class UserProfileContent extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final userAsyncValue = ref.watch(userProvider);
    
    return userAsyncValue.when(
      data: (user) => Column(
        children: [
          Text('姓名: ${user.name}'),
          Text('邮箱: ${user.email}'),
        ],
      ),
      loading: () => CircularProgressIndicator(),
      error: (error, stackTrace) => Text('错误: $error'),
    );
  }
}

四、实战案例

1. 完整的状态管理系统

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

// 定义数据模型
class Product {
  final String id;
  final String name;
  final double price;
  final String imageUrl;
  
  Product({
    required this.id,
    required this.name,
    required this.price,
    required this.imageUrl,
  });
}

// 定义购物车项
class CartItem {
  final Product product;
  final int quantity;
  
  CartItem(this.product, this.quantity);
  
  double get totalPrice => product.price * quantity;
}

// 定义状态管理器
class CartNotifier extends StateNotifier<List<CartItem>> {
  CartNotifier() : super([]);
  
  void addProduct(Product product) {
    final existingIndex = state.indexWhere((item) => item.product.id == product.id);
    
    if (existingIndex >= 0) {
      // 产品已存在,增加数量
      final updatedItems = [...state];
      updatedItems[existingIndex] = CartItem(
        product,
        updatedItems[existingIndex].quantity + 1,
      );
      state = updatedItems;
    } else {
      // 新产品,添加到购物车
      state = [...state, CartItem(product, 1)];
    }
  }
  
  void removeProduct(String productId) {
    state = state.where((item) => item.product.id != productId).toList();
  }
  
  void updateQuantity(String productId, int quantity) {
    if (quantity <= 0) {
      removeProduct(productId);
      return;
    }
    
    state = state.map((item) {
      if (item.product.id == productId) {
        return CartItem(item.product, quantity);
      }
      return item;
    }).toList();
  }
  
  double get totalAmount {
    return state.fold(0, (sum, item) => sum + item.totalPrice);
  }
}

// 创建 providers
final cartProvider = StateNotifierProvider<CartNotifier, List<CartItem>>((ref) {
  return CartNotifier();
});

final totalAmountProvider = Provider<double>((ref) {
  final cart = ref.watch(cartProvider);
  return cart.fold(0, (sum, item) => sum + item.totalPrice);
});

// 模拟产品数据
final products = [
  Product(
    id: '1',
    name: 'iPhone 13',
    price: 5999,
    imageUrl: 'https://example.com/iphone13.jpg',
  ),
  Product(
    id: '2',
    name: 'MacBook Pro',
    price: 12999,
    imageUrl: 'https://example.com/macbookpro.jpg',
  ),
  Product(
    id: '3',
    name: 'iPad Pro',
    price: 6999,
    imageUrl: 'https://example.com/ipadpro.jpg',
  ),
];

// 产品列表组件
class ProductListWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(
        title: Text('产品列表'),
        actions: [
          Consumer(
            builder: (context, ref, child) {
              final cartItems = ref.watch(cartProvider);
              return Stack(
                children: [
                  IconButton(
                    icon: Icon(Icons.shopping_cart),
                    onPressed: () {
                      Navigator.push(
                        context,
                        MaterialPageRoute(builder: (context) => CartWidget()),
                      );
                    },
                  ),
                  if (cartItems.isNotEmpty)
                    Positioned(
                      top: 0,
                      right: 0,
                      child: Container(
                        padding: EdgeInsets.all(2),
                        decoration: BoxDecoration(
                          color: Colors.red,
                          borderRadius: BorderRadius.circular(10),
                        ),
                        constraints: BoxConstraints(
                          minWidth: 16,
                          minHeight: 16,
                        ),
                        child: Text(
                          cartItems.length.toString(),
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 10,
                          ),
                          textAlign: TextAlign.center,
                        ),
                      ),
                    ),
                ],
              );
            },
          ),
        ],
      ),
      body: ListView.builder(
        itemCount: products.length,
        itemBuilder: (context, index) {
          final product = products[index];
          return ListTile(
            leading: Image.network(product.imageUrl, width: 50, height: 50),
            title: Text(product.name),
            subtitle: Text('¥${product.price}'),
            trailing: ElevatedButton(
              onPressed: () {
                ref.read(cartProvider.notifier).addProduct(product);
              },
              child: Text('加入购物车'),
            ),
          );
        },
      ),
    );
  }
}

// 购物车组件
class CartWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final cartItems = ref.watch(cartProvider);
    final totalAmount = ref.watch(totalAmountProvider);
    
    if (cartItems.isEmpty) {
      return Scaffold(
        appBar: AppBar(title: Text('购物车')),
        body: Center(child: Text('购物车为空')),
      );
    }
    
    return Scaffold(
      appBar: AppBar(title: Text('购物车')),
      body: Column(
        children: [
          Expanded(
            child: ListView.builder(
              itemCount: cartItems.length,
              itemBuilder: (context, index) {
                final item = cartItems[index];
                return ListTile(
                  leading: Image.network(item.product.imageUrl, width: 50, height: 50),
                  title: Text(item.product.name),
                  subtitle: Text('¥${item.product.price} x ${item.quantity} = ¥${item.totalPrice}'),
                  trailing: Row(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      IconButton(
                        icon: Icon(Icons.remove),
                        onPressed: () {
                          ref.read(cartProvider.notifier).updateQuantity(
                            item.product.id,
                            item.quantity - 1,
                          );
                        },
                      ),
                      Text(item.quantity.toString()),
                      IconButton(
                        icon: Icon(Icons.add),
                        onPressed: () {
                          ref.read(cartProvider.notifier).updateQuantity(
                            item.product.id,
                            item.quantity + 1,
                          );
                        },
                      ),
                      IconButton(
                        icon: Icon(Icons.delete),
                        onPressed: () {
                          ref.read(cartProvider.notifier).removeProduct(item.product.id);
                        },
                      ),
                    ],
                  ),
                );
              },
            ),
          ),
          Container(
            padding: EdgeInsets.all(16),
            decoration: BoxDecoration(
              border: Border(top: BorderSide(color: Colors.grey)),
            ),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text(
                  '总计:',
                  style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                ),
                Text(
                  '¥$totalAmount',
                  style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.red),
                ),
              ],
            ),
          ),
          ElevatedButton(
            onPressed: () {
              // 执行结算操作
              print('结算金额: ¥$totalAmount');
            },
            child: Text('结算'),
            style: ElevatedButton.styleFrom(
              minimumSize: Size(double.infinity, 50),
            ),
          ),
        ],
      ),
    );
  }
}

// 应用入口
void main() {
  runApp(ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Riverpod 购物车示例',
      home: ProductListWidget(),
    );
  }
}

五、最佳实践

  1. 模块化:将不同功能的状态管理分离到不同的 provider 中
  2. 单一职责:每个 provider 只负责管理一种类型的状态
  3. 依赖注入:使用 provider 进行依赖注入,提高代码的可测试性
  4. 状态组合:通过组合多个 provider 来构建复杂的状态逻辑
  5. 性能优化:使用 const 构造器和选择性监听来优化性能
  6. 错误处理:正确处理 FutureProvider 和 StreamProvider 中的错误
  7. 测试:为状态管理逻辑编写单元测试

六、常见问题

1. 状态更新不触发重建

// 确保使用 Consumer 或 ConsumerWidget
Consumer(
  builder: (context, ref, child) {
    final count = ref.watch(counterProvider);
    return Text('计数: $count');
  },
);

2. 性能问题

// 使用 const 构造器
class MyWidget extends ConsumerWidget {
  const MyWidget({Key? key}) : super(key: key);
  
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 只监听需要的状态
    final count = ref.watch(counterProvider);
    return Text('计数: $count');
  }
}

3. 依赖循环

// 避免循环依赖
final providerA = Provider((ref) {
  // 不要在这里监听 providerB
  return SomeValue();
});

final providerB = Provider((ref) {
  // 不要在这里监听 providerA
  return AnotherValue();
});

七、与其他状态管理方案的比较

特性RiverpodProviderBlocRedux
类型安全
依赖注入
异步支持
代码简洁
学习曲线⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

八、总结

Riverpod 是 Flutter 生态系统中强大的状态管理解决方案,它提供了类型安全、依赖注入、异步支持等特性,让我们能够构建更加可维护、可测试的应用。作为一名 UI 匠人,我推荐使用 Riverpod 来管理应用的状态,它能够帮助我们创建出更加优雅、高效的 Flutter 应用。


Riverpod 为 Flutter 状态管理带来了新的可能性,让状态管理变得更加简单、灵活和可扩展。

#flutter #riverpod #state-management #ui #dart

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/leopold_man/article/details/159954811

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--