Riverpod与Flutter框架集成:Flutter在鸿蒙平台完整集成方案
·


作者:付文龙(红目香薰)
仓库地址:https://gitcode.com/feng8403000/FlutterfromBeginnertoAdvancedForHarmonyOS.git
联系邮箱:372699828@qq.com
引言
Riverpod作为新一代状态管理方案,与Flutter框架的集成非常紧密。本文将详细介绍Riverpod与Flutter框架的完整集成方案,包括入口配置、路由集成、主题管理、导航守卫等高级特性,帮助开发者在鸿蒙平台开发Flutter应用时实现优雅的状态管理集成。
入口配置
基本集成
import "package:flutter/material.dart";
import "package:flutter_riverpod/flutter_riverpod.dart";
void main() {
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp(
title: "Flutter Riverpod应用",
home: HomePage(),
);
}
}
配置overrides
void main() {
runApp(
ProviderScope(
overrides: [
apiProvider.overrideWithValue(ApiServiceImpl()),
storageProvider.overrideWithValue(StorageServiceImpl()),
],
child: MyApp(),
),
);
}
配置observers
void main() {
runApp(
ProviderScope(
observers: [
ProviderLogger(),
],
child: MyApp(),
),
);
}
class ProviderLogger extends ProviderObserver {
void didUpdateProvider(
ProviderBase provider,
Object? previousValue,
Object? newValue,
ProviderContainer container,
) {
print("Provider更新: ${provider.name}, 旧值: $previousValue, 新值: $newValue");
}
}
路由集成
基本路由配置
class MyApp extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp(
title: "Flutter Riverpod应用",
initialRoute: "/",
routes: {
"/": (context) => const HomePage(),
"/cart": (context) => const CartPage(),
"/profile": (context) => const ProfilePage(),
"/login": (context) => const LoginPage(),
},
);
}
}
动态路由配置
class MyApp extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final authState = ref.watch(authProvider);
return MaterialApp(
title: "Flutter Riverpod应用",
initialRoute: authState.isAuthenticated ? "/" : "/login",
onGenerateRoute: (settings) {
switch (settings.name) {
case "/":
return MaterialPageRoute(builder: (_) => HomePage());
case "/cart":
return MaterialPageRoute(builder: (_) => CartPage());
case "/product/:id":
final id = settings.arguments as String? ?? "";
return MaterialPageRoute(builder: (_) => ProductDetailPage(id: id));
default:
return MaterialPageRoute(builder: (_) => NotFoundPage());
}
},
);
}
}
导航守卫
class MyApp extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp(
title: "Flutter Riverpod应用",
onGenerateRoute: (settings) {
final isAuthenticated = ref.read(isAuthenticatedProvider);
// 需要登录的页面
if (["/cart", "/profile", "/orders"].contains(settings.name)) {
if (!isAuthenticated) {
return MaterialPageRoute(builder: (_) => LoginPage());
}
}
// 已登录用户访问登录页自动跳转
if (settings.name == "/login" && isAuthenticated) {
return MaterialPageRoute(builder: (_) => HomePage());
}
// 正常路由
return _generateRoute(settings);
},
);
}
Route<dynamic>? _generateRoute(RouteSettings settings) {
switch (settings.name) {
case "/":
return MaterialPageRoute(builder: (_) => HomePage());
case "/cart":
return MaterialPageRoute(builder: (_) => CartPage());
case "/profile":
return MaterialPageRoute(builder: (_) => ProfilePage());
case "/login":
return MaterialPageRoute(builder: (_) => LoginPage());
default:
return MaterialPageRoute(builder: (_) => NotFoundPage());
}
}
}
主题管理
动态主题配置
final themeModeProvider = StateProvider<ThemeMode>((ref) => ThemeMode.system);
final primaryColorProvider = StateProvider<Color>((ref) => Colors.blue);
final themeProvider = Provider<ThemeData>((ref) {
final themeMode = ref.watch(themeModeProvider);
final primaryColor = ref.watch(primaryColorProvider);
return ThemeData(
primaryColor: primaryColor,
brightness: themeMode == ThemeMode.dark ? Brightness.dark : Brightness.light,
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: primaryColor,
brightness: themeMode == ThemeMode.dark ? Brightness.dark : Brightness.light,
),
);
});
class MyApp extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themeProvider);
return MaterialApp(
title: "Flutter Riverpod应用",
theme: theme,
home: HomePage(),
);
}
}
主题切换页面
class SettingsPage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final themeMode = ref.watch(themeModeProvider);
final primaryColor = ref.watch(primaryColorProvider);
return Scaffold(
appBar: AppBar(title: const Text("设置")),
body: ListView(
children: [
ListTile(
title: const Text("主题模式"),
subtitle: Text(themeMode.name),
trailing: DropdownButton<ThemeMode>(
value: themeMode,
items: const [
DropdownMenuItem(value: ThemeMode.system, child: Text("系统")),
DropdownMenuItem(value: ThemeMode.light, child: Text("浅色")),
DropdownMenuItem(value: ThemeMode.dark, child: Text("深色")),
],
onChanged: (value) {
if (value != null) {
ref.read(themeModeProvider.notifier).state = value;
}
},
),
),
ListTile(
title: const Text("主题颜色"),
trailing: Wrap(
spacing: 8,
children: [
_colorButton(context, ref, Colors.blue),
_colorButton(context, ref, Colors.green),
_colorButton(context, ref, Colors.red),
_colorButton(context, ref, Colors.purple),
_colorButton(context, ref, Colors.orange),
],
),
),
],
),
);
}
Widget _colorButton(BuildContext context, WidgetRef ref, Color color) {
final currentColor = ref.watch(primaryColorProvider);
return InkWell(
onTap: () => ref.read(primaryColorProvider.notifier).state = color,
borderRadius: BorderRadius.circular(50),
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(50),
border: Border.all(
color: currentColor == color ? Colors.white : Colors.transparent,
width: 3,
),
boxShadow: [
if (currentColor == color)
BoxShadow(color: color, blurRadius: 10),
],
),
),
);
}
}
状态持久化
SharedPreferences集成
import "package:shared_preferences/shared_preferences.dart";
final sharedPreferencesProvider = FutureProvider<SharedPreferences>((ref) async {
return await SharedPreferences.getInstance();
});
final themeModeProvider = StateProvider<ThemeMode>((ref) => ThemeMode.system);
class ThemeNotifier extends StateNotifier<ThemeMode> {
ThemeNotifier(this.ref) : super(ThemeMode.system) {
_loadThemeMode();
}
final Ref ref;
Future<void> _loadThemeMode() async {
final prefs = await ref.read(sharedPreferencesProvider.future);
final savedMode = prefs.getString("theme_mode");
if (savedMode != null) {
state = ThemeMode.values.firstWhere(
(mode) => mode.name == savedMode,
orElse: () => ThemeMode.system,
);
}
}
Future<void> setThemeMode(ThemeMode mode) async {
state = mode;
final prefs = await ref.read(sharedPreferencesProvider.future);
await prefs.setString("theme_mode", mode.name);
}
}
final themeNotifierProvider = StateNotifierProvider<ThemeNotifier, ThemeMode>((ref) {
return ThemeNotifier(ref);
});
购物车持久化
import "dart:convert";
final cartProvider = StateNotifierProvider<CartNotifier, List<CartItem>>((ref) {
return CartNotifier(ref);
});
class CartNotifier extends StateNotifier<List<CartItem>> {
CartNotifier(this.ref) : super([]) {
_loadCart();
}
final Ref ref;
Future<void> _loadCart() async {
final prefs = await ref.read(sharedPreferencesProvider.future);
final savedCart = prefs.getString("cart");
if (savedCart != null) {
final List<dynamic> jsonList = jsonDecode(savedCart);
state = jsonList.map((json) => CartItem.fromJson(json)).toList();
}
}
Future<void> _saveCart() async {
final prefs = await ref.read(sharedPreferencesProvider.future);
final jsonList = state.map((item) => item.toJson()).toList();
await prefs.setString("cart", jsonEncode(jsonList));
}
void addItem(CartItem item) {
final existingIndex = state.indexWhere((i) => i.id == item.id);
if (existingIndex >= 0) {
final newState = List<CartItem>.from(state);
newState[existingIndex] = newState[existingIndex].copyWith(
quantity: newState[existingIndex].quantity + 1,
);
state = newState;
} else {
state = [...state, item];
}
_saveCart();
}
void removeItem(String id) {
state = state.where((item) => item.id != id).toList();
_saveCart();
}
void clear() {
state = [];
_saveCart();
}
}
class CartItem {
final String id;
final String name;
final double price;
final int quantity;
CartItem({
required this.id,
required this.name,
required this.price,
this.quantity = 1,
});
CartItem copyWith({int? quantity}) {
return CartItem(
id: id,
name: name,
price: price,
quantity: quantity ?? this.quantity,
);
}
Map<String, dynamic> toJson() {
return {
"id": id,
"name": name,
"price": price,
"quantity": quantity,
};
}
factory CartItem.fromJson(Map<String, dynamic> json) {
return CartItem(
id: json["id"],
name: json["name"],
price: json["price"],
quantity: json["quantity"],
);
}
}
核心集成示例
示例1:完整的认证与路由集成
// 认证Provider
enum AuthStatus { loading, authenticated, unauthenticated }
class AuthState {
final AuthStatus status;
final User? user;
final String? error;
AuthState({required this.status, this.user, this.error});
AuthState copyWith({AuthStatus? status, User? user, String? error}) {
return AuthState(
status: status ?? this.status,
user: user ?? this.user,
error: error ?? this.error,
);
}
}
class AuthNotifier extends StateNotifier<AuthState> {
AuthNotifier(this.ref) : super(AuthState(status: AuthStatus.loading)) {
_checkAuth();
}
final Ref ref;
Future<void> _checkAuth() async {
final prefs = await ref.read(sharedPreferencesProvider.future);
final token = prefs.getString("auth_token");
if (token != null) {
try {
final user = await ref.read(apiProvider).getCurrentUser(token);
state = state.copyWith(status: AuthStatus.authenticated, user: user);
} catch (_) {
await prefs.remove("auth_token");
state = state.copyWith(status: AuthStatus.unauthenticated);
}
} else {
state = state.copyWith(status: AuthStatus.unauthenticated);
}
}
Future<void> login(String email, String password) async {
state = state.copyWith(status: AuthStatus.loading, error: null);
try {
final token = await ref.read(apiProvider).login(email, password);
final prefs = await ref.read(sharedPreferencesProvider.future);
await prefs.setString("auth_token", token);
final user = await ref.read(apiProvider).getCurrentUser(token);
state = state.copyWith(status: AuthStatus.authenticated, user: user);
} catch (e) {
state = state.copyWith(
status: AuthStatus.unauthenticated,
error: e.toString(),
);
}
}
Future<void> logout() async {
final prefs = await ref.read(sharedPreferencesProvider.future);
await prefs.remove("auth_token");
state = state.copyWith(status: AuthStatus.unauthenticated, user: null);
}
}
final authProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) {
return AuthNotifier(ref);
});
final isAuthenticatedProvider = Provider<bool>((ref) {
return ref.watch(authProvider).status == AuthStatus.authenticated;
});
// 应用入口
class MyApp extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final authState = ref.watch(authProvider);
final theme = ref.watch(themeProvider);
// 加载中状态
if (authState.status == AuthStatus.loading) {
return MaterialApp(
home: Scaffold(
body: Center(child: CircularProgressIndicator()),
),
);
}
return MaterialApp(
title: "Flutter Riverpod应用",
theme: theme,
initialRoute: authState.status == AuthStatus.authenticated ? "/" : "/login",
onGenerateRoute: (settings) => _generateRoute(context, ref, settings),
);
}
Route<dynamic> _generateRoute(
BuildContext context,
WidgetRef ref,
RouteSettings settings,
) {
final isAuthenticated = ref.read(isAuthenticatedProvider);
// 登录守卫
if (["/cart", "/profile", "/orders"].contains(settings.name) && !isAuthenticated) {
return MaterialPageRoute(builder: (_) => LoginPage());
}
// 已登录用户访问登录页
if (settings.name == "/login" && isAuthenticated) {
return MaterialPageRoute(builder: (_) => HomePage());
}
// 正常路由
switch (settings.name) {
case "/":
return MaterialPageRoute(builder: (_) => HomePage());
case "/cart":
return MaterialPageRoute(builder: (_) => CartPage());
case "/profile":
return MaterialPageRoute(builder: (_) => ProfilePage());
case "/orders":
return MaterialPageRoute(builder: (_) => OrdersPage());
case "/login":
return MaterialPageRoute(builder: (_) => LoginPage());
case "/product/:id":
final id = settings.arguments as String? ?? "";
return MaterialPageRoute(builder: (_) => ProductDetailPage(id: id));
default:
return MaterialPageRoute(builder: (_) => NotFoundPage());
}
}
}
// 登录页面
class LoginPage extends ConsumerWidget {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
Widget build(BuildContext context, WidgetRef ref) {
final authState = ref.watch(authProvider);
final authNotifier = ref.read(authProvider.notifier);
return Scaffold(
appBar: AppBar(title: const Text("登录")),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
TextField(
controller: _emailController,
decoration: const InputDecoration(labelText: "邮箱"),
enabled: authState.status != AuthStatus.loading,
),
TextField(
controller: _passwordController,
decoration: const InputDecoration(labelText: "密码"),
obscureText: true,
enabled: authState.status != AuthStatus.loading,
),
const SizedBox(height: 16),
if (authState.status == AuthStatus.loading)
const CircularProgressIndicator()
else if (authState.error != null)
Text(authState.error!, style: TextStyle(color: Colors.red))
else
ElevatedButton(
onPressed: () {
authNotifier.login(
_emailController.text,
_passwordController.text,
);
},
child: const Text("登录"),
),
],
),
),
);
}
}
// 首页
class HomePage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(authProvider.select((state) => state.user));
final cartItemCount = ref.watch(cartItemCountProvider);
return Scaffold(
appBar: AppBar(title: const Text("首页")),
body: Center(
child: Column(
children: [
Text("欢迎, ${user?.name ?? "用户"}!"),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () => Navigator.pushNamed(context, "/cart"),
child: Text("购物车 ($cartItemCount)"),
),
ElevatedButton(
onPressed: () => Navigator.pushNamed(context, "/profile"),
child: const Text("个人中心"),
),
],
),
),
drawer: Drawer(
child: ListView(
children: [
DrawerHeader(child: Text("欢迎, ${user?.name}")),
ListTile(
title: const Text("购物车"),
onTap: () => Navigator.pushNamed(context, "/cart"),
),
ListTile(
title: const Text("订单"),
onTap: () => Navigator.pushNamed(context, "/orders"),
),
ListTile(
title: const Text("设置"),
onTap: () => Navigator.pushNamed(context, "/settings"),
),
ListTile(
title: const Text("退出登录"),
onTap: () => ref.read(authProvider.notifier).logout(),
),
],
),
),
);
}
}
示例2:异步数据加载与缓存
final apiProvider = Provider<ApiService>((ref) => ApiServiceImpl());
final postsProvider = FutureProvider<List<Post>>((ref) async {
final api = ref.read(apiProvider);
return await api.getPosts();
});
final postsWithCacheProvider = FutureProvider<List<Post>>((ref) async {
final api = ref.read(apiProvider);
// 先尝试读取缓存
final prefs = await ref.read(sharedPreferencesProvider.future);
final cachedPosts = prefs.getString("posts");
if (cachedPosts != null) {
final List<dynamic> jsonList = jsonDecode(cachedPosts);
final posts = jsonList.map((json) => Post.fromJson(json)).toList();
// 在后台刷新数据
ref.listenSelf((previous, next) {
// 数据更新时自动缓存
if (next.hasValue && next.value != null) {
_cachePosts(prefs, next.value!);
}
});
return posts;
}
// 无缓存,从网络获取
final posts = await api.getPosts();
await _cachePosts(prefs, posts);
return posts;
});
Future<void> _cachePosts(SharedPreferences prefs, List<Post> posts) async {
final jsonList = posts.map((post) => post.toJson()).toList();
await prefs.setString("posts", jsonEncode(jsonList));
}
class PostListPage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final posts = ref.watch(postsWithCacheProvider);
return Scaffold(
appBar: AppBar(title: const Text("帖子列表")),
body: posts.when(
data: (posts) {
return RefreshIndicator(
onRefresh: () async {
ref.invalidate(postsWithCacheProvider);
await ref.read(postsWithCacheProvider.future);
},
child: ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) {
final post = posts[index];
return ListTile(
title: Text(post.title),
subtitle: Text(post.content),
onTap: () => Navigator.pushNamed(context, "/post/${post.id}"),
);
},
),
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => Center(child: Text("加载失败: $error")),
),
);
}
}
class Post {
final String id;
final String title;
final String content;
Post({required this.id, required this.title, required this.content});
Map<String, dynamic> toJson() {
return {"id": id, "title": title, "content": content};
}
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
id: json["id"],
title: json["title"],
content: json["content"],
);
}
}
示例3:Tab导航与状态管理
final currentTabProvider = StateProvider<int>((ref) => 0);
final tabPages = [
HomePage(),
CartPage(),
ProfilePage(),
];
final tabTitles = ["首页", "购物车", "个人中心"];
final tabIcons = [
Icons.home,
Icons.shopping_cart,
Icons.person,
];
class MainPage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final currentTab = ref.watch(currentTabProvider);
final cartItemCount = ref.watch(cartItemCountProvider);
return Scaffold(
body: tabPages[currentTab],
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentTab,
onTap: (index) => ref.read(currentTabProvider.notifier).state = index,
items: [
BottomNavigationBarItem(
icon: const Icon(Icons.home),
label: tabTitles[0],
),
BottomNavigationBarItem(
icon: Badge(
label: Text("$cartItemCount"),
isLabelVisible: cartItemCount > 0,
child: const Icon(Icons.shopping_cart),
),
label: tabTitles[1],
),
BottomNavigationBarItem(
icon: const Icon(Icons.person),
label: tabTitles[2],
),
],
),
);
}
}
性能优化集成
懒加载Provider
final userProvider = FutureProvider.autoDispose<User>((ref) async {
final api = ref.read(apiProvider);
return await api.getUser();
});
class UserWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userProvider);
return user.when(
data: (user) => Text(user.name),
loading: () => const CircularProgressIndicator(),
error: (error, stack) => Text("错误: $error"),
);
}
}
选择性监听
class UserProfileWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
// 只监听用户名变化
final userName = ref.watch(userProvider.select((user) => user?.name ?? ""));
return Text(userName);
}
}
使用keepAlive缓存
final userProvider = FutureProvider.autoDispose<User>((ref) async {
ref.keepAlive(); // 保持活跃,不自动销毁
final api = ref.read(apiProvider);
return await api.getUser();
});
鸿蒙平台特别考虑
平台适配配置
void main() {
runApp(
ProviderScope(
overrides: [
platformProvider.overrideWithValue(PlatformInfo()),
],
child: MyApp(),
),
);
}
final platformProvider = Provider<PlatformInfo>((ref) => PlatformInfo());
class PlatformInfo {
bool get isHarmonyOS => false;
String get platformName => "Android";
}
// 鸿蒙平台实现
class HarmonyOSPlatformInfo extends PlatformInfo {
bool get isHarmonyOS => true;
String get platformName => "HarmonyOS";
}
权限管理集成
final permissionProvider = StateNotifierProvider<PermissionNotifier, PermissionState>((ref) {
return PermissionNotifier();
});
class PermissionState {
final bool hasStoragePermission;
final bool hasCameraPermission;
PermissionState({
required this.hasStoragePermission,
required this.hasCameraPermission,
});
PermissionState copyWith({bool? hasStoragePermission, bool? hasCameraPermission}) {
return PermissionState(
hasStoragePermission: hasStoragePermission ?? this.hasStoragePermission,
hasCameraPermission: hasCameraPermission ?? this.hasCameraPermission,
);
}
}
class PermissionNotifier extends StateNotifier<PermissionState> {
PermissionNotifier() : super(PermissionState(
hasStoragePermission: false,
hasCameraPermission: false,
));
Future<void> requestStoragePermission() async {
// 鸿蒙平台权限请求
state = state.copyWith(hasStoragePermission: true);
}
Future<void> requestCameraPermission() async {
// 鸿蒙平台权限请求
state = state.copyWith(hasCameraPermission: true);
}
Future<void> checkPermissions() async {
// 检查已有的权限
state = state.copyWith(
hasStoragePermission: true,
hasCameraPermission: true,
);
}
}
最佳实践
1. Provider分层管理
// 服务层Provider
final apiProvider = Provider<ApiService>((ref) => ApiServiceImpl());
final storageProvider = Provider<StorageService>((ref) => StorageServiceImpl());
// 状态层Provider
final authProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) => AuthNotifier(ref));
final cartProvider = StateNotifierProvider<CartNotifier, List<CartItem>>((ref) => CartNotifier(ref));
// 计算层Provider
final isAuthenticatedProvider = Provider<bool>((ref) {
return ref.watch(authProvider).status == AuthStatus.authenticated;
});
final cartTotalProvider = Provider<double>((ref) {
final cart = ref.watch(cartProvider);
return cart.fold(0, (sum, item) => sum + item.price * item.quantity);
});
2. 使用Consumer减少重建
class CartPage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final cart = ref.watch(cartProvider);
return Scaffold(
appBar: AppBar(title: const Text("购物车")),
body: cart.isEmpty
? const Center(child: Text("购物车为空"))
: ListView.builder(
itemCount: cart.length,
itemBuilder: (context, index) {
final item = cart[index];
return Consumer(
cartProvider,
(context, cart, child) {
return ListTile(
title: Text(item.name),
subtitle: Text("¥${item.price}"),
);
},
);
},
),
);
}
}
3. 异步状态处理统一
class UserWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userProvider);
return user.when(
data: (user) => UserProfile(user: user),
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => ErrorWidget(error),
);
}
}
class UserProfile extends StatelessWidget {
final User user;
const UserProfile({super.key, required this.user});
Widget build(BuildContext context) {
return Column(
children: [
Text(user.name),
Text(user.email),
],
);
}
}
总结
Riverpod与Flutter框架的集成是一个系统性的过程,涉及多个方面:
- 入口配置:使用
ProviderScope包裹应用,配置overrides和observers。 - 路由集成:支持静态路由和动态路由,实现导航守卫。
- 主题管理:动态配置主题模式和颜色,支持主题切换。
- 状态持久化:与SharedPreferences集成,实现状态保存和恢复。
- 性能优化:使用autoDispose、select、keepAlive等特性优化性能。
- 鸿蒙适配:处理平台差异,实现权限管理等平台特定功能。
通过合理的集成方案,可以构建出结构清晰、性能优异的Flutter应用,特别是在鸿蒙平台开发时,这些集成方案将帮助开发者更好地利用Riverpod的强大功能。
更多推荐



所有评论(0)