From a75bee5267d267a9f51a129398a895d0a14dfcd2 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 29 Oct 2022 15:24:50 +0530 Subject: [PATCH 1/9] toggle ToggleSwitch before what in turn changes internally is changed --- lib/ui/components/toggle_switch_widget.dart | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/ui/components/toggle_switch_widget.dart b/lib/ui/components/toggle_switch_widget.dart index 5192dd4b7..f48339cec 100644 --- a/lib/ui/components/toggle_switch_widget.dart +++ b/lib/ui/components/toggle_switch_widget.dart @@ -18,6 +18,13 @@ class ToggleSwitchWidget extends StatefulWidget { } class _ToggleSwitchWidgetState extends State { + late bool toggleValue; + @override + void initState() { + toggleValue = widget.value.call(); + super.initState(); + } + @override Widget build(BuildContext context) { final enteColorScheme = Theme.of(context).colorScheme.enteTheme.colorScheme; @@ -31,10 +38,15 @@ class _ToggleSwitchWidgetState extends State { activeColor: enteColorScheme.primary400, inactiveTrackColor: enteColorScheme.fillMuted, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - value: widget.value.call(), + value: toggleValue, onChanged: (value) async { + setState(() { + toggleValue = value; + }); await widget.onChanged.call(); - setState(() {}); + setState(() { + toggleValue = widget.value.call(); + }); }, ), ), From e7c0b5fad65592e9e8dc48fc9c94896961fc6daf Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 29 Oct 2022 16:12:34 +0530 Subject: [PATCH 2/9] minor layout change for ToggleSwitch --- lib/ui/components/toggle_switch_widget.dart | 39 ++++++++++----------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/lib/ui/components/toggle_switch_widget.dart b/lib/ui/components/toggle_switch_widget.dart index f48339cec..80b3e908c 100644 --- a/lib/ui/components/toggle_switch_widget.dart +++ b/lib/ui/components/toggle_switch_widget.dart @@ -28,27 +28,24 @@ class _ToggleSwitchWidgetState extends State { @override Widget build(BuildContext context) { final enteColorScheme = Theme.of(context).colorScheme.enteTheme.colorScheme; - return Padding( - padding: const EdgeInsets.symmetric(vertical: 4), - child: SizedBox( - height: 30, - child: FittedBox( - fit: BoxFit.contain, - child: Switch.adaptive( - activeColor: enteColorScheme.primary400, - inactiveTrackColor: enteColorScheme.fillMuted, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - value: toggleValue, - onChanged: (value) async { - setState(() { - toggleValue = value; - }); - await widget.onChanged.call(); - setState(() { - toggleValue = widget.value.call(); - }); - }, - ), + return SizedBox( + height: 32, + child: FittedBox( + fit: BoxFit.contain, + child: Switch.adaptive( + activeColor: enteColorScheme.primary400, + inactiveTrackColor: enteColorScheme.fillMuted, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + value: toggleValue, + onChanged: (value) async { + setState(() { + toggleValue = value; + }); + await widget.onChanged.call(); + setState(() { + toggleValue = widget.value.call(); + }); + }, ), ), ); From 8c2f3efb0c3059448a5672cace8bafd878f81331 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 29 Oct 2022 16:36:38 +0530 Subject: [PATCH 3/9] minor layout changes to EnteLoadingWidget --- lib/ui/common/loading_widget.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ui/common/loading_widget.dart b/lib/ui/common/loading_widget.dart index 43d59ed4c..7ddcd03a4 100644 --- a/lib/ui/common/loading_widget.dart +++ b/lib/ui/common/loading_widget.dart @@ -10,9 +10,9 @@ class EnteLoadingWidget extends StatelessWidget { Widget build(BuildContext context) { return Center( child: Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(5), child: SizedBox.fromSize( - size: const Size.square(16), + size: const Size.square(14), child: CircularProgressIndicator( strokeWidth: 2, color: color ?? getEnteColorScheme(context).strokeBase, From 7f064b7a981fd4efbba4821a4a995311e706119c Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 29 Oct 2022 18:42:35 +0530 Subject: [PATCH 4/9] show EnteLoadingWidget when changes are being made internally on toggling switch --- lib/ui/components/toggle_switch_widget.dart | 52 +++++++++++++-------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/lib/ui/components/toggle_switch_widget.dart b/lib/ui/components/toggle_switch_widget.dart index 80b3e908c..91c2d9cdb 100644 --- a/lib/ui/components/toggle_switch_widget.dart +++ b/lib/ui/components/toggle_switch_widget.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:photos/ente_theme_data.dart'; +import 'package:photos/ui/common/loading_widget.dart'; typedef OnChangedCallBack = Future Function(); typedef ValueCallBack = bool Function(); @@ -19,35 +20,48 @@ class ToggleSwitchWidget extends StatefulWidget { class _ToggleSwitchWidgetState extends State { late bool toggleValue; + late bool inProgress; @override void initState() { toggleValue = widget.value.call(); + inProgress = false; super.initState(); } @override Widget build(BuildContext context) { final enteColorScheme = Theme.of(context).colorScheme.enteTheme.colorScheme; - return SizedBox( - height: 32, - child: FittedBox( - fit: BoxFit.contain, - child: Switch.adaptive( - activeColor: enteColorScheme.primary400, - inactiveTrackColor: enteColorScheme.fillMuted, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - value: toggleValue, - onChanged: (value) async { - setState(() { - toggleValue = value; - }); - await widget.onChanged.call(); - setState(() { - toggleValue = widget.value.call(); - }); - }, + return Row( + children: [ + inProgress + ? EnteLoadingWidget(color: enteColorScheme.strokeMuted) + : const SizedBox.shrink(), + const SizedBox(width: 8), + SizedBox( + height: 32, + child: FittedBox( + fit: BoxFit.contain, + child: Switch.adaptive( + activeColor: enteColorScheme.primary400, + inactiveTrackColor: enteColorScheme.fillMuted, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + value: toggleValue, + onChanged: (negationOfToggleValue) async { + setState(() { + toggleValue = negationOfToggleValue; + inProgress = true; + }); + await widget.onChanged.call(); + setState(() { + final newValue = widget.value.call(); + toggleValue = newValue; + inProgress = false; + }); + }, + ), + ), ), - ), + ], ); } } From 01d5ed6be2625a53d349b702fa1d9278a66ec5a9 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Mon, 31 Oct 2022 18:40:01 +0530 Subject: [PATCH 5/9] show check mark if toggle is successful --- lib/ui/components/toggle_switch_widget.dart | 52 +++++++++++++++++---- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/lib/ui/components/toggle_switch_widget.dart b/lib/ui/components/toggle_switch_widget.dart index 91c2d9cdb..5232a35f2 100644 --- a/lib/ui/components/toggle_switch_widget.dart +++ b/lib/ui/components/toggle_switch_widget.dart @@ -2,6 +2,12 @@ import 'package:flutter/material.dart'; import 'package:photos/ente_theme_data.dart'; import 'package:photos/ui/common/loading_widget.dart'; +enum ExecutionState { + idle, + inProgress, + successful, +} + typedef OnChangedCallBack = Future Function(); typedef ValueCallBack = bool Function(); @@ -20,23 +26,27 @@ class ToggleSwitchWidget extends StatefulWidget { class _ToggleSwitchWidgetState extends State { late bool toggleValue; - late bool inProgress; + late ExecutionState executionState; @override void initState() { toggleValue = widget.value.call(); - inProgress = false; + executionState = ExecutionState.idle; super.initState(); } @override Widget build(BuildContext context) { final enteColorScheme = Theme.of(context).colorScheme.enteTheme.colorScheme; + final Widget stateIcon = _stateIcon(enteColorScheme); + return Row( children: [ - inProgress - ? EnteLoadingWidget(color: enteColorScheme.strokeMuted) - : const SizedBox.shrink(), - const SizedBox(width: 8), + AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + switchInCurve: Curves.easeInExpo, + switchOutCurve: Curves.easeOutExpo, + child: stateIcon, + ), SizedBox( height: 32, child: FittedBox( @@ -49,13 +59,22 @@ class _ToggleSwitchWidgetState extends State { onChanged: (negationOfToggleValue) async { setState(() { toggleValue = negationOfToggleValue; - inProgress = true; + executionState = ExecutionState.inProgress; }); await widget.onChanged.call(); setState(() { final newValue = widget.value.call(); toggleValue = newValue; - inProgress = false; + if (toggleValue == newValue) { + executionState = ExecutionState.successful; + Future.delayed(const Duration(seconds: 1), () { + setState(() { + executionState = ExecutionState.idle; + }); + }); + } else { + executionState = ExecutionState.idle; + } }); }, ), @@ -64,4 +83,21 @@ class _ToggleSwitchWidgetState extends State { ], ); } + + Widget _stateIcon(enteColorScheme) { + if (executionState == ExecutionState.idle) { + return const SizedBox.shrink(); + } else if (executionState == ExecutionState.inProgress) { + return EnteLoadingWidget( + color: enteColorScheme.strokeMuted, + ); + } else if (executionState == ExecutionState.successful) { + return Icon( + Icons.check_outlined, + color: enteColorScheme.primary500, + ); + } else { + return const SizedBox.shrink(); + } + } } From 8e1278c7e44dff0a01c32d0e131015a26e73c90a Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 1 Nov 2022 07:44:29 +0530 Subject: [PATCH 6/9] added debouncer to toggle status --- lib/ui/components/toggle_switch_widget.dart | 35 +++++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/ui/components/toggle_switch_widget.dart b/lib/ui/components/toggle_switch_widget.dart index 5232a35f2..fb5797079 100644 --- a/lib/ui/components/toggle_switch_widget.dart +++ b/lib/ui/components/toggle_switch_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:photos/ente_theme_data.dart'; import 'package:photos/ui/common/loading_widget.dart'; +import 'package:photos/utils/debouncer.dart'; enum ExecutionState { idle, @@ -26,11 +27,11 @@ class ToggleSwitchWidget extends StatefulWidget { class _ToggleSwitchWidgetState extends State { late bool toggleValue; - late ExecutionState executionState; + ExecutionState executionState = ExecutionState.idle; + final _debouncer = Debouncer(const Duration(milliseconds: 300)); @override void initState() { toggleValue = widget.value.call(); - executionState = ExecutionState.idle; super.initState(); } @@ -42,7 +43,7 @@ class _ToggleSwitchWidgetState extends State { return Row( children: [ AnimatedSwitcher( - duration: const Duration(milliseconds: 200), + duration: const Duration(milliseconds: 175), switchInCurve: Curves.easeInExpo, switchOutCurve: Curves.easeOutExpo, child: stateIcon, @@ -59,20 +60,34 @@ class _ToggleSwitchWidgetState extends State { onChanged: (negationOfToggleValue) async { setState(() { toggleValue = negationOfToggleValue; - executionState = ExecutionState.inProgress; + //start showing inProgress statu icons if toggle takes more than debounce time + _debouncer.run( + () => Future( + () { + setState(() { + executionState = ExecutionState.inProgress; + }); + }, + ), + ); }); await widget.onChanged.call(); + //debouncer gets canceled if onChanged takes less than debounce time + _debouncer.cancelDebounce(); setState(() { final newValue = widget.value.call(); - toggleValue = newValue; + //if onchanged on toggle is successful if (toggleValue == newValue) { - executionState = ExecutionState.successful; - Future.delayed(const Duration(seconds: 1), () { - setState(() { - executionState = ExecutionState.idle; + if (executionState == ExecutionState.inProgress) { + executionState = ExecutionState.successful; + Future.delayed(const Duration(seconds: 1), () { + setState(() { + executionState = ExecutionState.idle; + }); }); - }); + } } else { + toggleValue = !toggleValue; executionState = ExecutionState.idle; } }); From 1b245a5021ffa44cc0cac48154b4712d03e909a0 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 1 Nov 2022 08:24:25 +0530 Subject: [PATCH 7/9] show toggle feedback on short unsuccessful onchanged --- lib/ui/components/toggle_switch_widget.dart | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/ui/components/toggle_switch_widget.dart b/lib/ui/components/toggle_switch_widget.dart index fb5797079..ccf7c9e1f 100644 --- a/lib/ui/components/toggle_switch_widget.dart +++ b/lib/ui/components/toggle_switch_widget.dart @@ -71,7 +71,10 @@ class _ToggleSwitchWidgetState extends State { ), ); }); + final Stopwatch stopwatch = Stopwatch()..start(); await widget.onChanged.call(); + //for toggle feedback on short unsuccessful onChanged + await _feedbackOnUnsuccessfulToggle(stopwatch); //debouncer gets canceled if onChanged takes less than debounce time _debouncer.cancelDebounce(); setState(() { @@ -115,4 +118,13 @@ class _ToggleSwitchWidgetState extends State { return const SizedBox.shrink(); } } + + Future _feedbackOnUnsuccessfulToggle(Stopwatch stopwatch) async { + final timeElapsed = stopwatch.elapsedMilliseconds; + if (timeElapsed < 200) { + await Future.delayed( + Duration(milliseconds: 200 - timeElapsed), + ); + } + } } From dd78c284442b5aaac4160e1e7a6844b77ba0c0cc Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 1 Nov 2022 09:49:39 +0530 Subject: [PATCH 8/9] avoid popping menuItem when state icon is visible and text is long --- lib/ui/components/toggle_switch_widget.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ui/components/toggle_switch_widget.dart b/lib/ui/components/toggle_switch_widget.dart index ccf7c9e1f..41e91d1b8 100644 --- a/lib/ui/components/toggle_switch_widget.dart +++ b/lib/ui/components/toggle_switch_widget.dart @@ -83,7 +83,7 @@ class _ToggleSwitchWidgetState extends State { if (toggleValue == newValue) { if (executionState == ExecutionState.inProgress) { executionState = ExecutionState.successful; - Future.delayed(const Duration(seconds: 1), () { + Future.delayed(const Duration(seconds: 2), () { setState(() { executionState = ExecutionState.idle; }); @@ -104,7 +104,7 @@ class _ToggleSwitchWidgetState extends State { Widget _stateIcon(enteColorScheme) { if (executionState == ExecutionState.idle) { - return const SizedBox.shrink(); + return const SizedBox(width: 24); } else if (executionState == ExecutionState.inProgress) { return EnteLoadingWidget( color: enteColorScheme.strokeMuted, @@ -115,7 +115,7 @@ class _ToggleSwitchWidgetState extends State { color: enteColorScheme.primary500, ); } else { - return const SizedBox.shrink(); + return const SizedBox(width: 24); } } From 70cf65cc6be065d59ea0d34b7a4110d0c473209b Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 1 Nov 2022 11:58:54 +0530 Subject: [PATCH 9/9] minor padding and size changes --- lib/ui/components/toggle_switch_widget.dart | 23 ++++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/ui/components/toggle_switch_widget.dart b/lib/ui/components/toggle_switch_widget.dart index 41e91d1b8..370b70ea4 100644 --- a/lib/ui/components/toggle_switch_widget.dart +++ b/lib/ui/components/toggle_switch_widget.dart @@ -42,11 +42,14 @@ class _ToggleSwitchWidgetState extends State { return Row( children: [ - AnimatedSwitcher( - duration: const Duration(milliseconds: 175), - switchInCurve: Curves.easeInExpo, - switchOutCurve: Curves.easeOutExpo, - child: stateIcon, + Padding( + padding: const EdgeInsets.only(right: 2), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 175), + switchInCurve: Curves.easeInExpo, + switchOutCurve: Curves.easeOutExpo, + child: stateIcon, + ), ), SizedBox( height: 32, @@ -110,9 +113,13 @@ class _ToggleSwitchWidgetState extends State { color: enteColorScheme.strokeMuted, ); } else if (executionState == ExecutionState.successful) { - return Icon( - Icons.check_outlined, - color: enteColorScheme.primary500, + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 1), + child: Icon( + Icons.check_outlined, + size: 22, + color: enteColorScheme.primary500, + ), ); } else { return const SizedBox(width: 24);