Loading and success states for MenuItemWidget
This commit is contained in:
parent
2d9d8ca102
commit
bc3f0ed464
|
@ -98,7 +98,7 @@ class _AdvancedSettingsScreenState extends State<AdvancedSettingsScreen> {
|
|||
),
|
||||
borderRadius: 8,
|
||||
alignCaptionedTextToLeft: true,
|
||||
onTap: () {
|
||||
onTap: () async {
|
||||
routeToPage(context, const AppStorageViewer());
|
||||
},
|
||||
),
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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());
|
||||
},
|
||||
),
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
),
|
||||
|
|
|
@ -69,7 +69,7 @@ class SocialsMenuItemWidget extends StatelessWidget {
|
|||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () {
|
||||
onTap: () async {
|
||||
launchUrlString(urlSring);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue