rrousselGit / riverpod

A reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze.
https://riverpod.dev
MIT License
5.79k stars 886 forks source link

iOS rendering issue #3456

Open urvesh-sm opened 1 month ago

urvesh-sm commented 1 month ago

Describe the bug We have listview which has heterogeneous widget loading inside. Some of this widget just loads image using CachedNetworkImage and some have API call using HookConsumerWidget's useEffect & Dio.

This HookConsumerWidget items of listview sometimes just initiate API call but are not completed. This behaviour only happens on release iOS build, on debug it works perfectly and also on Android works well for debug & release.

I tried changing useEffect with FutureProvider & FutureBuilder but the behaviour is still random.

To Reproduce

To reproduce its simple to re-launch app multiple times and the list item having API call loads randomly.

Expected behavior The behaviour of loading the items should be consistent on debug and release. Right now it is working on debug iOS build but not on iOS release.

Sample Code:

One of the ListView item widget

import 'package:common/common.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:stable_money_flutter/gen/colors.gen.dart';

class UserSpecificFdCardsListWidget extends HookConsumerWidget {
  final String? title;

  final double? topMargin;
  final double? rightMargin;
  final double? bottomMargin;
  final double? leftMargin;
  const UserSpecificFdCardsListWidget({
    super.key,
    this.title,
    this.bottomMargin,
    this.leftMargin,
    this.rightMargin,
    this.topMargin,
  });

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    useAutomaticKeepAlive(wantKeepAlive: true);

    var pageState = useAppLifecycleState();
    var viewModel = ref.watch(userSpecificFdCardsViewModel);

    void getCardsData() {
      viewModel.getUserCardsData(
        onSuccess: () {},
        onFailure: (error) {},
        onUnAuthroized: context.onUnAuthorized,
      );
    }

    useEffect(
      () {
        if (pageState == AppLifecycleState.resumed) {
          WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
            getCardsData();
          });
        }
        return null;
      },
      [pageState],
    );

    if (viewModel.cardsList == null ||
        viewModel.cardsList!.isEmpty ||
        viewModel.isLoading) {
      return const SizedBox();
    }

    // Main Widget
    return Container(
      color: ColorName.purpleLight5,
      margin: EdgeInsets.only(
        top: topMargin ?? 0,
        left: leftMargin ?? 0,
        bottom: bottomMargin ?? 0,
        right: rightMargin ?? 0,
      ),
      padding: const EdgeInsets.only(bottom: dimen22),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [],
      ),
    );
  }
}

userSpecificFdCardsViewModel

import 'package:hooks_riverpod/hooks_riverpod.dart';

final userSpecificFdCardsViewModel = ChangeNotifierProvider((ref) {
  return UserSpecificFdCardsViewModel(ref);
});

class UserSpecificFdCardsViewModel extends BaseViewModel {
  UserSpecificFdCardsViewModel(super.ref);

  late final Repository _repository = ref.read(repositoryProvider);

  List<dynamic>? _cardsList;
  List<dynamic>? get cardsList => _cardsList;

  Future<void> getUserCardsData({
    required Function() onSuccess,
    required OnFailure onFailure,
    required OnUnAuthroized onUnAuthroized,
  }) async {
    makeAPICall<Result<dynamic>,
        dynamic>(
      showLoader: false,
      call: _repository.getList(),
      onFailure: onFailure,
      onUnAuthorized: onUnAuthroized,
      onRetry: () {
        getUserFdCardsData(
          onFailure: onFailure,
          onUnAuthroized: onUnAuthroized,
          onSuccess: onSuccess,
        );
      },
      onSuccess: (dynamic response) {
        try {
            _fdCardsList = response.list;
          notifyListeners();
        } catch (e) {
          //Ignore
        }
      },
    );
  }
}
rrousselGit commented 1 month ago

Please make a minimal reproducible example. Your code is not executable as it is.