toggle_switch_widget.dart 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import 'package:flutter/material.dart';
  2. import 'package:photos/ente_theme_data.dart';
  3. import 'package:photos/ui/common/loading_widget.dart';
  4. enum ExecutionState {
  5. idle,
  6. inProgress,
  7. successful,
  8. }
  9. typedef OnChangedCallBack = Future<void> Function();
  10. typedef ValueCallBack = bool Function();
  11. class ToggleSwitchWidget extends StatefulWidget {
  12. final ValueCallBack value;
  13. final OnChangedCallBack onChanged;
  14. const ToggleSwitchWidget({
  15. required this.value,
  16. required this.onChanged,
  17. Key? key,
  18. }) : super(key: key);
  19. @override
  20. State<ToggleSwitchWidget> createState() => _ToggleSwitchWidgetState();
  21. }
  22. class _ToggleSwitchWidgetState extends State<ToggleSwitchWidget> {
  23. late bool toggleValue;
  24. late ExecutionState executionState;
  25. @override
  26. void initState() {
  27. toggleValue = widget.value.call();
  28. executionState = ExecutionState.idle;
  29. super.initState();
  30. }
  31. @override
  32. Widget build(BuildContext context) {
  33. final enteColorScheme = Theme.of(context).colorScheme.enteTheme.colorScheme;
  34. final Widget stateIcon = _stateIcon(enteColorScheme);
  35. return Row(
  36. children: [
  37. AnimatedSwitcher(
  38. duration: const Duration(milliseconds: 200),
  39. switchInCurve: Curves.easeInExpo,
  40. switchOutCurve: Curves.easeOutExpo,
  41. child: stateIcon,
  42. ),
  43. SizedBox(
  44. height: 32,
  45. child: FittedBox(
  46. fit: BoxFit.contain,
  47. child: Switch.adaptive(
  48. activeColor: enteColorScheme.primary400,
  49. inactiveTrackColor: enteColorScheme.fillMuted,
  50. materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
  51. value: toggleValue,
  52. onChanged: (negationOfToggleValue) async {
  53. setState(() {
  54. toggleValue = negationOfToggleValue;
  55. executionState = ExecutionState.inProgress;
  56. });
  57. await widget.onChanged.call();
  58. setState(() {
  59. final newValue = widget.value.call();
  60. toggleValue = newValue;
  61. if (toggleValue == newValue) {
  62. executionState = ExecutionState.successful;
  63. Future.delayed(const Duration(seconds: 1), () {
  64. setState(() {
  65. executionState = ExecutionState.idle;
  66. });
  67. });
  68. } else {
  69. executionState = ExecutionState.idle;
  70. }
  71. });
  72. },
  73. ),
  74. ),
  75. ),
  76. ],
  77. );
  78. }
  79. Widget _stateIcon(enteColorScheme) {
  80. if (executionState == ExecutionState.idle) {
  81. return const SizedBox.shrink();
  82. } else if (executionState == ExecutionState.inProgress) {
  83. return EnteLoadingWidget(
  84. color: enteColorScheme.strokeMuted,
  85. );
  86. } else if (executionState == ExecutionState.successful) {
  87. return Icon(
  88. Icons.check_outlined,
  89. color: enteColorScheme.primary500,
  90. );
  91. } else {
  92. return const SizedBox.shrink();
  93. }
  94. }
  95. }