How to prevent multiple network calls from a flutter bloc?

Viewed 283

I am building an app using flutter and I'm using the flutter_bloc package for managing state. I have a simple Avatar widget used for displaying for displaying profile photo of a user.

/// A widget for displaying a user profile avatar.
class UserAvatar extends StatelessWidget {
  /// The unique identifier for a particular user.
  final int userId;

  /// The size of the avatar.
  final double radius;

  UserAvatar({required this.userId, this.radius = 30}) {}

  @override
  Widget build(BuildContext context) {
    context.read<UserInfoBloc>().add(UserInfoEvent.getUserInfo(userId));

    return BlocBuilder<UserInfoBloc, UserInfoState>(
      builder: (context, state) {
        return state.when(initial: () {
          return Text('Initial');
        }, loading: () {
          return Text('Loading');
        }, loaded: (user) {
          print(user);
          return Container(
            child: Avatar(
              shape: AvatarShape.circle(radius),
              loader: Center(
                child: ClipOval(
                  child: Skeleton(
                    height: radius * 2,
                    width: radius * 2,
                  ),
                ),
              ),
              useCache: true,
              sources: [NetworkSource(user.avatarUrl ?? '')],
              name: user.firstName!.trim(),
              onTap: () {
                //TODO implement Navigation to profile page
              },
            ),
          );
        }, error: () {
          return Text('error');
        });
      },
    );
  }
}

My problem is that the widget will be used multiple times (when displaying a feed and there are contents by the same user). I initially just have the id of the user, then I try to make a network call and try to get the user. I've implemented some form of caching in my repository:

@LazySingleton(as: UserInfoRepository)
class UserInfoRepositoryImpl extends UserInfoRepository {
  final GetUserInfoRemoteDataSource remoteDataSource;
  final GetUserInfoLocalDataSource localDataSource;

  UserInfoRepositoryImpl(
      {required this.remoteDataSource, required this.localDataSource});

  @override
  Future<Either<Failure, User>> getUserInfo(int id) async {
    try {
      final existsInCache = localDataSource.containsUserModel(id);
      if (existsInCache) {
        return right(localDataSource.getUserModel(id));
      } else {
        final result = await remoteDataSource.getUserInfo(id);
        localDataSource.cacheUserModel(result);
        return right(result);
      }
    } on ServerExceptions catch (e) {
      return left(e.when(() => Failure(),
          requestCancelled: () => Failure.requestCancelled(),
          unauthorisedRequest: () => Failure.unauthorisedRequest(),
          badRequest: (e) => Failure.badRequest(e),
          notFound: () => Failure.notFound(),
          internalServerError: () => Failure.internalServerError(),
          receiveTimeout: () => Failure.receiveTimeout(),
          sendTimeout: () => Failure.sendTimeout(),
          noInternetConnection: () => Failure.noInternetConnection()));
    } on CacheException {
      return left(Failure.cacheFailure());
    }
  }
}

Of course, I used the injectable package for dealing with my dependencies, and I've used the @LazySingleton annotation on the repository. But unfortunately, if I try to display two avatars of the same user, two separate network calls will be made. Of course, I don't want that. How can I solve this problem?

0 Answers
Related