hacker1024 / declarative_refresh_indicator.dart

A declarative refresh indicator alternative for Flutter that takes inspiration from the Switch and Checkbox APIs.
https://pub.dev/packages/declarative_refresh_indicator
MIT License
5 stars 7 forks source link

Error when calling hiding again immediately after showing #5

Open Almighty-Alpaca opened 1 year ago

Almighty-Alpaca commented 1 year ago

I have a loading state that sometimes only exists for a very short time (under one frame as far as I can tell), causing the following exception:

======== Exception caught by widgets library =======================================================
The following assertion was thrown building XXX:
Hide called, but not showing!
'package:declarative_refresh_indicator/declarative_refresh_indicator.dart':
Failed assertion: line 122 pos 12: '_showing'

The relevant error-causing widget was: 
  RefreshDataIndicator RefreshDataIndicator:file:///xxx/lib/something.dart:50:12
When the exception was thrown, this was the stack: 
#2      _DeclarativeRefreshIndicatorState._hide (package:declarative_refresh_indicator/declarative_refresh_indicator.dart:122:12)
#3      _DeclarativeRefreshIndicatorState.didUpdateWidget (package:declarative_refresh_indicator/declarative_refresh_indicator.dart:149:7)
#4      StatefulElement.update (package:flutter/src/widgets/framework.dart:5142:55)
#5      Element.updateChild (package:flutter/src/widgets/framework.dart:3660:15)
#6      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4993:16)
#7      StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5133:11)
#8      Element.rebuild (package:flutter/src/widgets/framework.dart:4690:5)
#9      StatefulElement.update (package:flutter/src/widgets/framework.dart:5156:5)
#10     Element.updateChild (package:flutter/src/widgets/framework.dart:3660:15)
#11     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4993:16)
#12     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5133:11)
#13     Element.rebuild (package:flutter/src/widgets/framework.dart:4690:5)
#14     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2743:19)
#15     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:863:21)
#16     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:381:5)
#17     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1289:15)
#18     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1218:9)
#19     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1076:5)
#20     _invoke (dart:ui/hooks.dart:145:13)
#21     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:338:5)
#22     _drawFrame (dart:ui/hooks.dart:112:31)
(elided 2 frames from class _AssertionError)
====================================================================================================

I think this is caused by _hide() being executed before the asynchronously called _onRefresh() can set the completer.

Almighty-Alpaca commented 1 year ago

2 seems to fix this bug

hacker1024 commented 1 year ago

Are you calling setState in initState? If so, this should not be done. If not, are you able to provide a minimal reproduction of the issue?

Almighty-Alpaca commented 1 year ago

No, I'm using this together with riverpod. I refresh a provider when clicking a button and provide refreshing: ref.watch(dataProvider).isLoading to the DeclarativeRefreshIndicator.

Code ```dart class RefreshDataButton extends ConsumerWidget { const RefreshDataButton({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { return IconButton( onPressed: () => ref.refresh(onlineDataProvider.future), icon: const Icon(Icons.refresh), ); } } class RefreshDataIndicator extends ConsumerWidget { const RefreshDataIndicator({super.key, required this.child}); final Widget child; @override Widget build(BuildContext context, WidgetRef ref) { final isLoading = ref.watch(dataProvider).isLoading; return DeclarativeRefreshIndicator( refreshing: isLoading, onRefresh: () => ref.refresh(onlineDataProvider.future), child: child, ); } } ``` `dataProvider` synchronously depends on `onlineDataProvider` and only merges its data with the offline data.