Skip to content

Commit

Permalink
Add AppAsyncSliverWidget and AppAsyncSliver classes
Browse files Browse the repository at this point in the history
  • Loading branch information
hectorAguero committed Apr 17, 2024
1 parent 32019ca commit 6c57faf
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 22 deletions.
55 changes: 38 additions & 17 deletions lib/common_widgets/app_async_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:sliver_tools/sliver_tools.dart';

import '../extensions/app_localization_extension.dart';
import 'app_cupertino_button.dart';
import 'app_infinite_rotation_animation.dart';

class AppAsyncWidget<T> extends StatelessWidget {
const AppAsyncWidget({
Expand Down Expand Up @@ -53,8 +54,8 @@ class AppAsyncWidget<T> extends StatelessWidget {
}
}

class AppAsyncSliver<T> extends StatelessWidget {
const AppAsyncSliver({
class AppAsyncSliverWidget<T> extends StatefulWidget {
const AppAsyncSliverWidget({
required this.asyncValue,
required this.child,
this.onErrorRetry,
Expand All @@ -65,12 +66,31 @@ class AppAsyncSliver<T> extends StatelessWidget {
final VoidCallback? onErrorRetry;
final Widget Function(T) child;

@override
State<AppAsyncSliverWidget<T>> createState() =>
_AppAsyncSliverWidgetState<T>();
}

class _AppAsyncSliverWidgetState<T> extends State<AppAsyncSliverWidget<T>> {
bool isLoading = false;

@override
Widget build(BuildContext context) {
final isRefreshing = widget.asyncValue.isRefreshing;
return SliverAnimatedSwitcher(
duration: kThemeAnimationDuration,
child: switch (asyncValue) {
AsyncData(:final value) => child(value),
child: switch (widget.asyncValue) {
AsyncData(:final value) => widget.child(value),
AsyncLoading() => const SliverFillRemaining(
key: ValueKey('loading'),
child: Center(
child: SizedBox(
width: 32,
height: 32,
child: CircularProgressIndicator.adaptive(),
),
),
),
AsyncError(:final error) => SliverFillRemaining(
key: const ValueKey('error'),
child: Padding(
Expand All @@ -84,13 +104,17 @@ class AppAsyncSliver<T> extends StatelessWidget {
style: Theme.of(context).textTheme.titleLarge,
textAlign: TextAlign.center,
),
if (onErrorRetry != null)
if (widget.onErrorRetry != null)
Padding(
padding: const EdgeInsets.only(top: 16),
child: AppCupertinoButton.tinted(
color: Theme.of(context).colorScheme.primary,
onPressed: onErrorRetry,
icon: const Icon(Icons.refresh),
onPressed:
!isRefreshing && !isLoading ? errorRetry : null,
icon: AppInfiniteRotationAnimation(
isLoading: isRefreshing || isLoading,
child: const Icon(Icons.refresh),
),
child: Text(context.loc.retry),
),
),
Expand All @@ -99,17 +123,14 @@ class AppAsyncSliver<T> extends StatelessWidget {
),
),
),
AsyncLoading() => const SliverFillRemaining(
key: ValueKey('loading'),
child: Center(
child: SizedBox(
width: 32,
height: 32,
child: CircularProgressIndicator.adaptive(),
),
),
),
},
);
}

Future<void> errorRetry() async {
setState(() => isLoading = true);
widget.onErrorRetry!();
await Future<void>.delayed(const Duration(milliseconds: 700));
setState(() => isLoading = false);
}
}
58 changes: 58 additions & 0 deletions lib/common_widgets/app_infinite_rotation_animation.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';

class AppInfiniteRotationAnimation extends StatefulWidget {
const AppInfiniteRotationAnimation({
required this.isLoading,
required this.child,
this.duration = const Duration(milliseconds: 700),
super.key,
});
final bool isLoading;
final Widget child;
final Duration duration;

@override
State<AppInfiniteRotationAnimation> createState() =>
_AppInfiniteRotationAnimationState();
}

class _AppInfiniteRotationAnimationState
extends State<AppInfiniteRotationAnimation>
with SingleTickerProviderStateMixin {
late AnimationController _controller;

@override
void initState() {
super.initState();
_controller = AnimationController(
duration: widget.duration,
vsync: this,
);
}

@override
void didUpdateWidget(covariant AppInfiniteRotationAnimation oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.isLoading != oldWidget.isLoading) {
if (widget.isLoading) {
_controller.repeat();
} else {
_controller.animateTo(0).then((_) => _controller.stop());
}
}
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return RotationTransition(
turns: _controller,
child: widget.child,
);
}
}
2 changes: 1 addition & 1 deletion lib/features/instruments/instruments_tab_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class InstrumentsTabPage extends ConsumerWidget {
const SliverPadding(padding: EdgeInsets.only(top: 8)),
SliverAnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: AppAsyncSliver(
child: AppAsyncSliverWidget(
asyncValue: ref.watch(instrumentsTabProvider),
onErrorRetry: () => ref.invalidate(instrumentsTabProvider),
child: (value) => WebPaddingSliver.only(
Expand Down
7 changes: 5 additions & 2 deletions lib/features/parades/parades_tab_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,12 @@ class _ParadesTabPageState extends ConsumerState<ParadesTabPage> {
largeTitle: context.loc.paradesTitle,
),
),
AppAsyncSliver(
AppAsyncSliverWidget(
asyncValue: ref.watch(paradesProvider),
onErrorRetry: () => ref.invalidate(paradesProvider),
onErrorRetry: () async =>
await Future.delayed(const Duration(milliseconds: 500), () {
ref.invalidate(paradesProvider);
}),
child: (value) => SliverCrossAxisConstrained(
maxCrossAxisExtent: ScreenSize.md.value,
child: SuperSliverList.builder(
Expand Down
4 changes: 3 additions & 1 deletion lib/features/parades/parades_tab_providers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ class Parades extends _$Parades {

@override
FutureOr<ImmutableList<Parade>> build() async {
return getParades();
final parades = await getParades();

return parades;
}

Future<bool?> fetchNextPage({int pageSize = _pageSize}) async {
Expand Down
2 changes: 1 addition & 1 deletion lib/features/schools/widgets/schools_tab_body.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class SchoolsTabBody extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
return SliverSafeArea(
top: false,
sliver: AppAsyncSliver(
sliver: AppAsyncSliverWidget(
asyncValue: ref.watch(schoolsProvider),
onErrorRetry: () => ref.invalidate(schoolsProvider),
child: (value) => Consumer(
Expand Down

0 comments on commit 6c57faf

Please sign in to comment.