Loading and success states for MenuItemWidget

This commit is contained in:
ashilkn 2023-02-01 07:12:26 +05:30
parent 2d9d8ca102
commit bc3f0ed464
10 changed files with 105 additions and 18 deletions

View file

@ -98,7 +98,7 @@ class _AdvancedSettingsScreenState extends State<AdvancedSettingsScreen> {
),
borderRadius: 8,
alignCaptionedTextToLeft: true,
onTap: () {
onTap: () async {
routeToPage(context, const AppStorageViewer());
},
),

View file

@ -1,13 +1,17 @@
import 'package:flutter/material.dart';
import 'package:photos/theme/ente_theme.dart';
import 'package:photos/ui/common/loading_widget.dart';
import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart';
class TrailingWidget extends StatefulWidget {
final ValueNotifier executionStateNotifier;
final IconData? trailingIcon;
final Color? trailingIconColor;
final Widget? trailingWidget;
final bool trailingIconIsMuted;
final double trailingExtraMargin;
const TrailingWidget({
required this.executionStateNotifier,
this.trailingIcon,
this.trailingIconColor,
this.trailingWidget,
@ -20,23 +24,70 @@ class TrailingWidget extends StatefulWidget {
}
class _TrailingWidgetState extends State<TrailingWidget> {
Widget? trailingWidget;
@override
void initState() {
widget.executionStateNotifier.addListener(_executionStateListener);
super.initState();
}
@override
void dispose() {
widget.executionStateNotifier.removeListener(_executionStateListener);
super.dispose();
}
@override
Widget build(BuildContext context) {
if (trailingWidget == null) {
_setTrailingIcon();
}
return AnimatedSwitcher(
duration: const Duration(milliseconds: 175),
switchInCurve: Curves.easeInExpo,
switchOutCurve: Curves.easeOutExpo,
child: trailingWidget,
);
}
void _executionStateListener() {
final colorScheme = getEnteColorScheme(context);
setState(() {
if (widget.executionStateNotifier.value == ExecutionState.idle) {
_setTrailingIcon();
} else if (widget.executionStateNotifier.value ==
ExecutionState.inProgress) {
trailingWidget = EnteLoadingWidget(
color: colorScheme.strokeMuted,
);
} else if (widget.executionStateNotifier.value ==
ExecutionState.successful) {
trailingWidget = Icon(
Icons.check_outlined,
size: 22,
color: colorScheme.primary500,
);
} else {
trailingWidget = const SizedBox.shrink();
}
});
}
void _setTrailingIcon() {
if (widget.trailingIcon != null) {
return Padding(
trailingWidget = Padding(
padding: EdgeInsets.only(
right: widget.trailingExtraMargin,
),
child: Icon(
widget.trailingIcon,
color: widget.trailingIconIsMuted
? colorScheme.strokeMuted
? getEnteColorScheme(context).strokeMuted
: widget.trailingIconColor,
),
);
} else {
return widget.trailingWidget ?? const SizedBox.shrink();
trailingWidget = widget.trailingWidget ?? const SizedBox.shrink();
}
}
}

View file

@ -2,6 +2,16 @@ import 'package:expandable/expandable.dart';
import 'package:flutter/material.dart';
import 'package:photos/theme/ente_theme.dart';
import 'package:photos/ui/components/menu_item_widget/menu_item_child_widgets.dart';
import 'package:photos/utils/debouncer.dart';
enum ExecutionState {
idle,
inProgress,
error,
successful;
}
typedef FutureVoidCallback = Future<void> Function();
class MenuItemWidget extends StatefulWidget {
final Widget captionedTextWidget;
@ -25,7 +35,7 @@ class MenuItemWidget extends StatefulWidget {
/// If provided, add this much extra spacing to the right of the trailing icon.
final double trailingExtraMargin;
final VoidCallback? onTap;
final FutureVoidCallback? onTap;
final VoidCallback? onDoubleTap;
final Color? menuItemColor;
final bool alignCaptionedTextToLeft;
@ -68,6 +78,10 @@ class MenuItemWidget extends StatefulWidget {
}
class _MenuItemWidgetState extends State<MenuItemWidget> {
final _debouncer = Debouncer(const Duration(milliseconds: 300));
ValueNotifier<ExecutionState> executionStateNotifier =
ValueNotifier(ExecutionState.idle);
Color? menuItemColor;
@override
@ -92,6 +106,7 @@ class _MenuItemWidgetState extends State<MenuItemWidget> {
if (widget.expandableController != null) {
widget.expandableController!.dispose();
}
executionStateNotifier.dispose();
super.dispose();
}
@ -100,7 +115,7 @@ class _MenuItemWidgetState extends State<MenuItemWidget> {
return widget.isExpandable || widget.isGestureDetectorDisabled
? menuItemWidget(context)
: GestureDetector(
onTap: widget.onTap,
onTap: _onTap,
onDoubleTap: widget.onDoubleTap,
onTapDown: _onTapDown,
onTapUp: _onTapUp,
@ -152,6 +167,7 @@ class _MenuItemWidgetState extends State<MenuItemWidget> {
)
else
TrailingWidget(
executionStateNotifier: executionStateNotifier,
trailingIcon: widget.trailingIcon,
trailingIconColor: widget.trailingIconColor,
trailingWidget: widget.trailingWidget,
@ -163,6 +179,26 @@ class _MenuItemWidgetState extends State<MenuItemWidget> {
);
}
Future<void> _onTap() async {
_debouncer.run(
() => Future(
() {
executionStateNotifier.value = ExecutionState.inProgress;
},
),
);
await widget.onTap
?.call()
.onError((error, stackTrace) => _debouncer.cancelDebounce());
_debouncer.cancelDebounce();
if (executionStateNotifier.value == ExecutionState.inProgress) {
executionStateNotifier.value = ExecutionState.successful;
Future.delayed(const Duration(seconds: 2), () {
executionStateNotifier.value = ExecutionState.idle;
});
}
}
void _onTapDown(details) {
setState(() {
if (widget.pressedColor == null) {

View file

@ -9,12 +9,12 @@ enum ExecutionState {
successful,
}
typedef FutureVoidCallBack = Future<void> Function();
typedef FutureVoidCallback = Future<void> Function();
typedef BoolCallBack = bool Function();
class ToggleSwitchWidget extends StatefulWidget {
final BoolCallBack value;
final FutureVoidCallBack onChanged;
final FutureVoidCallback onChanged;
const ToggleSwitchWidget({
required this.value,
required this.onChanged,

View file

@ -113,7 +113,7 @@ class AboutMenuItemWidget extends StatelessWidget {
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () {
onTap: () async {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {

View file

@ -128,7 +128,7 @@ class AccountSectionWidget extends StatelessWidget {
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () {
onTap: () async {
_onLogoutTapped(context);
},
),
@ -140,7 +140,7 @@ class AccountSectionWidget extends StatelessWidget {
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () {
onTap: () async {
routeToPage(context, const DeleteAccountPage());
},
),

View file

@ -49,7 +49,7 @@ class BackupSectionWidgetState extends State<BackupSectionWidget> {
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () {
onTap: () async {
routeToPage(
context,
const BackupFolderSelectionPage(
@ -66,7 +66,7 @@ class BackupSectionWidgetState extends State<BackupSectionWidget> {
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () {
onTap: () async {
routeToPage(
context,
const BackupSettingsScreen(),

View file

@ -34,7 +34,7 @@ class GeneralSectionWidget extends StatelessWidget {
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () {
onTap: () async {
_onManageSubscriptionTapped(context);
},
),
@ -46,7 +46,7 @@ class GeneralSectionWidget extends StatelessWidget {
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () {
onTap: () async {
_onFamilyPlansTapped(context);
},
),
@ -58,7 +58,7 @@ class GeneralSectionWidget extends StatelessWidget {
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () {
onTap: () async {
_onAdvancedTapped(context);
},
),

View file

@ -69,7 +69,7 @@ class SocialsMenuItemWidget extends StatelessWidget {
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () {
onTap: () async {
launchUrlString(urlSring);
},
);

View file

@ -54,7 +54,7 @@ class SupportSectionWidget extends StatelessWidget {
pressedColor: getEnteColorScheme(context).fillFaint,
trailingIcon: Icons.chevron_right_outlined,
trailingIconIsMuted: true,
onTap: () {
onTap: () async {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {