我正在尝试为我的个人资料消费者小部件创建一个简单的 FamilyAsyncNotifier。但是,由于某种原因,状态不断重建,并且我的 FamilyAsyncNotifier 中的构建方法不断执行。
处理此类事情时的最佳实践是什么?
// TheTime\frontend\lib\src\features\profile\presentation\profile_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:frontend/src/features/profile/presentation/profile_screen_controller_wp.dart';
class ProfileScreen extends ConsumerWidget {
const ProfileScreen({super.key, required this.userId, required this.isAdmin});
final String userId;
final bool isAdmin;
@override
Widget build(BuildContext context, WidgetRef ref) {
final controllerParams = ProfileScreenControllerParams(userId, isAdmin);
final profileState = ref.watch(profileScreenControllerProvider(controllerParams));
return Scaffold(
appBar: AppBar(
leading: OverflowBox(
maxWidth: 80,
child: Row(
children: [
IconButton(
icon: const Icon(Icons.settings),
onPressed: () {
// Handle settings icon pressed
},
),
IconButton(
icon: const Icon(Icons.share),
onPressed: () {
// Handle share icon pressed
},
),
],
),
),
title: profileState.when(
data: (profile) {
return Center(
child: Text('${profile?.firstName ?? ''} ${profile?.lastName ?? ''}'),
);
},
loading: () {
return const Center(child: CircularProgressIndicator());
},
error: (error, stackTrace) {
return const Center(child: Text('Error'));
},
),
actions: [
IconButton(
icon: const Icon(Icons.message),
onPressed: () {
// Handle messages icon pressed
},
),
],
),
body: profileState.when(
data: (profile) {
if (profile != null) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 16),
Row(
children: [
const SizedBox(width: 16),
const CircleAvatar(
radius: 40,
),
const SizedBox(width: 16),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: () {
// Handle friends pressed
},
child: const Text('Friends'),
),
TextButton(
onPressed: () {
// Handle followers pressed
},
child: const Text('Followers'),
),
TextButton(
onPressed: () {
// Handle following pressed
},
child: const Text('Following'),
),
],
),
),
],
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
'Username: ${profile.username}',
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
profile.biography ?? '',
style: const TextStyle(fontSize: 16),
),
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () {
// Handle create program pressed
},
child: const Text('Create Program'),
),
),
const SizedBox(width: 16),
Expanded(
child: ElevatedButton(
onPressed: () {
// Handle edit profile pressed
},
child: const Text('Edit Profile'),
),
),
],
),
),
],
);
} else {
return const Center(child: Text('User not found'));
}
},
loading: () {
return const Center(child: CircularProgressIndicator());
},
error: (error, stackTrace) {
return const Center(child: Text('Error occurred'));
},
),
);
}
}
// TheTime\\frontend\\lib\\src\\features\\profile\\presentation\\profile_screen_controller_wp.dart
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:frontend/src/features/profile/data/profile_repository_wp.dart';
import 'package:frontend/src/features/profile/domain/profile_model.dart';
import 'package:frontend/src/features/authentication/data/auth_repository_providers.dart';
//TODO: Figure out how to dispose of this appropriately.
//TODO: Figure out how to handle multiple controller/controller-provider instances simulataneouly. Probably another collections provider.
//TODO: Above todos are kept for later.
//TODO: Need to add retry mechanisms and routes different flows depending on exception.
final profileScreenControllerProvider = AsyncNotifierProviderFamily<ProfileScreenController, Profile?, ProfileScreenControllerParams>(() {
return ProfileScreenController();
});
class ProfileScreenControllerParams {
final String userId;
final bool isAdmin;
ProfileScreenControllerParams(this.userId, this.isAdmin);
}
class ProfileScreenController extends FamilyAsyncNotifier<Profile?, ProfileScreenControllerParams> { //Remeber that ProfileScreenControllerParams is arg.
@override //TODO: Consider storing references to obej
FutureOr<Profile?> build(ProfileScreenControllerParams arg) async { //TODO this is getting stuck in some kind of infinite loop. Is there something wrong with my family implementation? //This is very bootleg!
return await getProfileById(arg.userId);
}
Future<Profile?> getProfileById(String userId) async {
state = const AsyncValue.loading();
final profileRepository = ref.read(profileRepositoryProvider);
try {
final profile = profileRepository.getProfileById(userId);
if (profile != null) {
state = AsyncValue.data(profile);
return profile;
} else {
await profileRepository.fetchRemoteProfileById(userId);
final fetchedProfile = profileRepository.getProfileById(userId);
if (fetchedProfile != null) {
state = AsyncValue.data(fetchedProfile);
return fetchedProfile;
} else {
state = AsyncValue.error(Exception('Profile not found'), StackTrace.current);
return null;
}
}
} catch (e, stackTrace) {
state = AsyncValue.error(e, stackTrace);
return null;
}
}
}
我尝试过直接返回参数
final profileScreenControllerProvider = AsyncNotifierProviderFamily<ProfileScreenController, Profile?, ProfileScreenControllerParams>(
(ref, params) {
return ProfileScreenController(ref, params);
},
dependencies: [profileRepositoryProvider],
).autoDispose();```
But AsyncNotifierProviderFamily does not want you to pass parameters when creating instances of the FamilyAsyncNotifier.
I want to avoid using generator as I want to manually implement my providers.
class ProfileScreenControllerParams {
final String userId;
final bool isAdmin;
ProfileScreenControllerParams(this.userId, this.isAdmin);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ProfileScreenControllerParams &&
runtimeType == other.runtimeType &&
userId == other.userId &&
isAdmin == other.isAdmin;
@override
int get hashCode => userId.hashCode ^ isAdmin.hashCode;
}
您必须识别传递给系列的参数,以便它知道它何时实际发生变化。