Consumer Widget + AsyncNotifierProviderFamily + FamilyAsyncNotifier 导致持续不受监管的 Widget 重建和状态更新

问题描述 投票:0回答:1

我正在尝试为我的个人资料消费者小部件创建一个简单的 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.
flutter riverpod
1个回答
0
投票
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;
}

您必须识别传递给系列的参数,以便它知道它何时实际发生变化。

© www.soinside.com 2019 - 2024. All rights reserved.