Parcourir la source

Merge pull request #609 from ente-io/redesign-with-components

Redesign with components
Neeraj Gupta il y a 2 ans
Parent
commit
1f6a08d71a

+ 1 - 1
lib/theme/text_style.dart

@@ -43,7 +43,7 @@ const TextStyle large = TextStyle(
 );
 const TextStyle body = TextStyle(
   fontSize: 16,
-  height: 19.4 / 16.0,
+  height: 20 / 16.0,
   fontWeight: _regularWeight,
   fontFamily: _fontFamily,
 );

+ 138 - 0
lib/ui/backup_settings_screen.dart

@@ -0,0 +1,138 @@
+import 'dart:io';
+
+import 'package:flutter/material.dart';
+import 'package:photos/core/configuration.dart';
+import 'package:photos/theme/ente_theme.dart';
+import 'package:photos/ui/components/captioned_text_widget.dart';
+import 'package:photos/ui/components/icon_button_widget.dart';
+import 'package:photos/ui/components/menu_item_widget.dart';
+import 'package:photos/ui/components/menu_section_description_widget.dart';
+import 'package:photos/ui/components/title_bar_title_widget.dart';
+import 'package:photos/ui/components/title_bar_widget.dart';
+import 'package:photos/ui/components/toggle_switch_widget.dart';
+
+class BackupSettingsScreen extends StatelessWidget {
+  const BackupSettingsScreen({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    final colorScheme = getEnteColorScheme(context);
+    return Scaffold(
+      body: CustomScrollView(
+        primary: false,
+        slivers: <Widget>[
+          TitleBarWidget(
+            flexibleSpaceTitle: const TitleBarTitleWidget(
+              title: "Backup settings",
+            ),
+            actionIcons: [
+              IconButtonWidget(
+                icon: Icons.close_outlined,
+                isSecondary: true,
+                onTap: () {
+                  Navigator.pop(context);
+                },
+              ),
+            ],
+          ),
+          SliverList(
+            delegate: SliverChildBuilderDelegate(
+              (context, index) {
+                return Padding(
+                  padding: const EdgeInsets.symmetric(horizontal: 16),
+                  child: Padding(
+                    padding: const EdgeInsets.symmetric(vertical: 20),
+                    child: Column(
+                      mainAxisSize: MainAxisSize.min,
+                      children: [
+                        Column(
+                          children: [
+                            MenuItemWidget(
+                              captionedTextWidget: const CaptionedTextWidget(
+                                title: "Backup over mobile data",
+                              ),
+                              menuItemColor: colorScheme.fillFaint,
+                              trailingSwitch: ToggleSwitchWidget(
+                                value: () {
+                                  return Configuration.instance
+                                      .shouldBackupOverMobileData();
+                                },
+                                onChanged: () async {
+                                  await Configuration.instance
+                                      .setBackupOverMobileData(
+                                    !Configuration.instance
+                                        .shouldBackupOverMobileData(),
+                                  );
+                                },
+                              ),
+                              borderRadius: 8,
+                              alignCaptionedTextToLeft: true,
+                              isBottomBorderRadiusRemoved: true,
+                              isGestureDetectorDisabled: true,
+                            ),
+                            const SizedBox(height: 1),
+                            MenuItemWidget(
+                              captionedTextWidget: const CaptionedTextWidget(
+                                title: "Backup videos",
+                              ),
+                              menuItemColor: colorScheme.fillFaint,
+                              trailingSwitch: ToggleSwitchWidget(
+                                value: () =>
+                                    Configuration.instance.shouldBackupVideos(),
+                                onChanged: () => Configuration.instance
+                                    .setShouldBackupVideos(
+                                  !Configuration.instance.shouldBackupVideos(),
+                                ),
+                              ),
+                              borderRadius: 8,
+                              alignCaptionedTextToLeft: true,
+                              isTopBorderRadiusRemoved: true,
+                              isGestureDetectorDisabled: true,
+                            ),
+                          ],
+                        ),
+                        const SizedBox(height: 24),
+                        Platform.isIOS
+                            ? Column(
+                                children: [
+                                  MenuItemWidget(
+                                    captionedTextWidget:
+                                        const CaptionedTextWidget(
+                                      title: "Disable auto lock",
+                                    ),
+                                    menuItemColor: colorScheme.fillFaint,
+                                    trailingSwitch: ToggleSwitchWidget(
+                                      value: () => Configuration.instance
+                                          .shouldKeepDeviceAwake(),
+                                      onChanged: () {
+                                        return Configuration.instance
+                                            .setShouldKeepDeviceAwake(
+                                          !Configuration.instance
+                                              .shouldKeepDeviceAwake(),
+                                        );
+                                      },
+                                    ),
+                                    borderRadius: 8,
+                                    alignCaptionedTextToLeft: true,
+                                    isGestureDetectorDisabled: true,
+                                  ),
+                                  const MenuSectionDescriptionWidget(
+                                    content:
+                                        "Disable the device screen lock when ente is in the foreground and there is a backup in progress. This is normally not needed, but may help big uploads and initial imports of large libraries complete faster.",
+                                  )
+                                ],
+                              )
+                            : const SizedBox.shrink(),
+                      ],
+                    ),
+                  ),
+                );
+              },
+              childCount: 1,
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 5 - 7
lib/ui/components/home_header_widget.dart

@@ -3,6 +3,7 @@ import 'dart:ui';
 import 'package:flutter/material.dart';
 import 'package:photos/core/event_bus.dart';
 import 'package:photos/events/opened_settings_event.dart';
+import 'package:photos/ui/components/icon_button_widget.dart';
 import 'package:photos/ui/viewer/search/search_widget.dart';
 
 class HomeHeaderWidget extends StatefulWidget {
@@ -23,16 +24,13 @@ class _HomeHeaderWidgetState extends State<HomeHeaderWidget> {
       child: Row(
         mainAxisAlignment: MainAxisAlignment.spaceBetween,
         children: [
-          IconButton(
-            visualDensity: const VisualDensity(horizontal: -2, vertical: -2),
-            onPressed: () {
+          IconButtonWidget(
+            isPrimary: true,
+            icon: Icons.menu_outlined,
+            onTap: () {
               Scaffold.of(context).openDrawer();
               Bus.instance.fire(OpenedSettingsEvent());
             },
-            splashColor: Colors.transparent,
-            icon: const Icon(
-              Icons.menu_outlined,
-            ),
           ),
           AnimatedSwitcher(
             duration: const Duration(milliseconds: 250),

+ 102 - 0
lib/ui/components/icon_button_widget.dart

@@ -0,0 +1,102 @@
+import 'package:flutter/material.dart';
+import 'package:photos/theme/colors.dart';
+import 'package:photos/theme/ente_theme.dart';
+
+class IconButtonWidget extends StatefulWidget {
+  final bool isPrimary;
+  final bool isSecondary;
+  final bool isRounded;
+  final IconData icon;
+  final bool disableGestureDetector;
+  final VoidCallback? onTap;
+  final Color? defaultColor;
+  final Color? pressedColor;
+  final Color? iconColor;
+  const IconButtonWidget({
+    required this.icon,
+    this.isPrimary = false,
+    this.isSecondary = false,
+    this.isRounded = false,
+    this.disableGestureDetector = false,
+    this.onTap,
+    this.defaultColor,
+    this.pressedColor,
+    this.iconColor,
+    super.key,
+  });
+
+  @override
+  State<IconButtonWidget> createState() => _IconButtonWidgetState();
+}
+
+class _IconButtonWidgetState extends State<IconButtonWidget> {
+  Color? iconStateColor;
+  @override
+  void didChangeDependencies() {
+    setState(() {
+      iconStateColor = null;
+    });
+    super.didChangeDependencies();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    if (!widget.isPrimary && !widget.isRounded && !widget.isSecondary) {
+      return const SizedBox.shrink();
+    }
+    final colorTheme = getEnteColorScheme(context);
+    iconStateColor ??
+        (iconStateColor = widget.defaultColor ??
+            (widget.isRounded ? colorTheme.fillFaint : null));
+    return widget.disableGestureDetector
+        ? _iconButton(colorTheme)
+        : GestureDetector(
+            onTapDown: _onTapDown,
+            onTapUp: _onTapUp,
+            onTapCancel: _onTapCancel,
+            onTap: widget.onTap,
+            child: _iconButton(colorTheme),
+          );
+  }
+
+  Widget _iconButton(EnteColorScheme colorTheme) {
+    return AnimatedContainer(
+      duration: const Duration(milliseconds: 20),
+      padding: const EdgeInsets.all(8),
+      decoration: BoxDecoration(
+        borderRadius: BorderRadius.circular(20),
+        color: iconStateColor,
+      ),
+      child: Icon(
+        widget.icon,
+        color: widget.iconColor ??
+            (widget.isSecondary
+                ? colorTheme.strokeMuted
+                : colorTheme.strokeBase),
+        size: 24,
+      ),
+    );
+  }
+
+  _onTapDown(details) {
+    final colorTheme = getEnteColorScheme(context);
+    setState(() {
+      iconStateColor = widget.pressedColor ??
+          (widget.isRounded ? colorTheme.fillMuted : colorTheme.fillFaint);
+    });
+  }
+
+  _onTapUp(details) {
+    Future.delayed(const Duration(milliseconds: 100), () {
+      setState(() {
+        iconStateColor = null;
+      });
+    });
+  }
+
+  _onTapCancel() {
+    setState(() {
+      iconStateColor = null;
+    });
+  }
+}

+ 16 - 5
lib/ui/components/menu_item_widget.dart

@@ -19,6 +19,10 @@ class MenuItemWidget extends StatefulWidget {
   final double borderRadius;
   final Color? pressedColor;
   final ExpandableController? expandableController;
+  final bool isBottomBorderRadiusRemoved;
+  final bool isTopBorderRadiusRemoved;
+  //disable gesture detector if not used
+  final bool isGestureDetectorDisabled;
   const MenuItemWidget({
     required this.captionedTextWidget,
     this.isExpandable = false,
@@ -34,6 +38,9 @@ class MenuItemWidget extends StatefulWidget {
     this.borderRadius = 4.0,
     this.pressedColor,
     this.expandableController,
+    this.isBottomBorderRadiusRemoved = false,
+    this.isTopBorderRadiusRemoved = false,
+    this.isGestureDetectorDisabled = false,
     Key? key,
   }) : super(key: key);
 
@@ -70,7 +77,7 @@ class _MenuItemWidgetState extends State<MenuItemWidget> {
 
   @override
   Widget build(BuildContext context) {
-    return widget.isExpandable
+    return widget.isExpandable || widget.isGestureDetectorDisabled
         ? menuItemWidget(context)
         : GestureDetector(
             onTap: widget.onTap,
@@ -86,7 +93,11 @@ class _MenuItemWidgetState extends State<MenuItemWidget> {
     final enteColorScheme = Theme.of(context).colorScheme.enteTheme.colorScheme;
     final borderRadius = Radius.circular(widget.borderRadius);
     final isExpanded = widget.expandableController?.value;
-    final bottomBorderRadius = isExpanded != null && isExpanded
+    final bottomBorderRadius =
+        (isExpanded != null && isExpanded) || widget.isBottomBorderRadiusRemoved
+            ? const Radius.circular(0)
+            : borderRadius;
+    final topBorderRadius = widget.isTopBorderRadiusRemoved
         ? const Radius.circular(0)
         : borderRadius;
     return AnimatedContainer(
@@ -95,8 +106,8 @@ class _MenuItemWidgetState extends State<MenuItemWidget> {
       padding: const EdgeInsets.symmetric(horizontal: 12),
       decoration: BoxDecoration(
         borderRadius: BorderRadius.only(
-          topLeft: borderRadius,
-          topRight: borderRadius,
+          topLeft: topBorderRadius,
+          topRight: topBorderRadius,
           bottomLeft: bottomBorderRadius,
           bottomRight: bottomBorderRadius,
         ),
@@ -155,7 +166,7 @@ class _MenuItemWidgetState extends State<MenuItemWidget> {
 
   void _onTapDown(details) {
     setState(() {
-      menuItemColor = widget.pressedColor;
+      menuItemColor = widget.pressedColor ?? widget.menuItemColor;
     });
   }
 

+ 20 - 0
lib/ui/components/menu_section_description_widget.dart

@@ -0,0 +1,20 @@
+import 'package:flutter/material.dart';
+import 'package:photos/theme/ente_theme.dart';
+
+class MenuSectionDescriptionWidget extends StatelessWidget {
+  final String content;
+  const MenuSectionDescriptionWidget({required this.content, super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Padding(
+      padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6),
+      child: Text(
+        content,
+        style: getEnteTextTheme(context)
+            .mini
+            .copyWith(color: getEnteColorScheme(context).textMuted),
+      ),
+    );
+  }
+}

+ 9 - 17
lib/ui/components/notification_warning_widget.dart

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
 import 'package:photos/ente_theme_data.dart';
 import 'package:photos/theme/colors.dart';
 import 'package:photos/theme/text_style.dart';
+import 'package:photos/ui/components/icon_button_widget.dart';
 
 class NotificationWarningWidget extends StatelessWidget {
   final IconData warningIcon;
@@ -51,23 +52,14 @@ class NotificationWarningWidget extends StatelessWidget {
                     ),
                   ),
                   const SizedBox(width: 12),
-                  ClipOval(
-                    child: Material(
-                      color: fillFaintDark,
-                      child: InkWell(
-                        splashColor: Colors.red, // Splash color
-                        onTap: onTap,
-                        child: SizedBox(
-                          width: 40,
-                          height: 40,
-                          child: Icon(
-                            actionIcon,
-                            color: Colors.white,
-                          ),
-                        ),
-                      ),
-                    ),
-                  ),
+                  IconButtonWidget(
+                    icon: actionIcon,
+                    isRounded: true,
+                    iconColor: strokeBaseDark,
+                    defaultColor: fillFaintDark,
+                    pressedColor: fillMutedDark,
+                    onTap: onTap,
+                  )
                 ],
               ),
             ),

+ 55 - 0
lib/ui/components/title_bar_title_widget.dart

@@ -0,0 +1,55 @@
+import 'package:flutter/material.dart';
+import 'package:photos/theme/ente_theme.dart';
+
+class TitleBarTitleWidget extends StatelessWidget {
+  final String? title;
+  final bool isTitleH2;
+  final IconData? icon;
+  const TitleBarTitleWidget({
+    this.title,
+    this.isTitleH2 = false,
+    this.icon,
+    super.key,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    final textTheme = getEnteTextTheme(context);
+    final colorTheme = getEnteColorScheme(context);
+    if (title != null) {
+      if (icon != null) {
+        return Row(
+          mainAxisSize: MainAxisSize.min,
+          crossAxisAlignment: CrossAxisAlignment.center,
+          children: <Widget>[
+            Text(
+              title!,
+              style: textTheme.h3Bold,
+              overflow: TextOverflow.ellipsis,
+              maxLines: 1,
+            ),
+            const SizedBox(width: 8),
+            Icon(icon, size: 20, color: colorTheme.strokeMuted),
+          ],
+        );
+      }
+      if (isTitleH2) {
+        return Text(
+          title!,
+          style: textTheme.h2Bold,
+          overflow: TextOverflow.ellipsis,
+          maxLines: 1,
+        );
+      } else {
+        return Text(
+          title!,
+          style: textTheme.h3Bold,
+          overflow: TextOverflow.ellipsis,
+          maxLines: 1,
+        );
+      }
+    }
+
+    return const SizedBox.shrink();
+  }
+}

+ 146 - 0
lib/ui/components/title_bar_widget.dart

@@ -0,0 +1,146 @@
+import 'package:flutter/material.dart';
+import 'package:photos/theme/ente_theme.dart';
+import 'package:photos/ui/components/icon_button_widget.dart';
+
+class TitleBarWidget extends StatelessWidget {
+  final String? title;
+  final String? caption;
+  final Widget? flexibleSpaceTitle;
+  final String? flexibleSpaceCaption;
+  final List<Widget>? actionIcons;
+  final bool isTitleH2WithoutLeading;
+  final bool isFlexibleSpaceDisabled;
+  const TitleBarWidget({
+    this.title,
+    this.caption,
+    this.flexibleSpaceTitle,
+    this.flexibleSpaceCaption,
+    this.actionIcons,
+    this.isTitleH2WithoutLeading = false,
+    this.isFlexibleSpaceDisabled = false,
+    super.key,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    const toolbarHeight = 48.0;
+    final textTheme = getEnteTextTheme(context);
+    final colorTheme = getEnteColorScheme(context);
+    return SliverAppBar(
+      toolbarHeight: toolbarHeight,
+      leadingWidth: 48,
+      automaticallyImplyLeading: false,
+      pinned: true,
+      expandedHeight: 102,
+      centerTitle: false,
+      titleSpacing: 0,
+      title: Padding(
+        padding: EdgeInsets.only(left: isTitleH2WithoutLeading ? 16 : 0),
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          mainAxisAlignment: MainAxisAlignment.start,
+          children: [
+            title == null
+                ? const SizedBox.shrink()
+                : Text(
+                    title!,
+                    style: isTitleH2WithoutLeading
+                        ? textTheme.h2Bold
+                        : textTheme.largeBold,
+                  ),
+            caption == null || isTitleH2WithoutLeading
+                ? const SizedBox.shrink()
+                : Text(
+                    caption!,
+                    style: textTheme.mini.copyWith(color: colorTheme.textMuted),
+                  )
+          ],
+        ),
+      ),
+      actions: [
+        Padding(
+          padding: const EdgeInsets.fromLTRB(4, 4, 12, 4),
+          child: Row(
+            children: _actionsWithPaddingInBetween(),
+          ),
+        ),
+      ],
+      leading: isTitleH2WithoutLeading
+          ? null
+          : Padding(
+              padding: const EdgeInsets.all(4),
+              child: IconButtonWidget(
+                icon: Icons.arrow_back_outlined,
+                isPrimary: true,
+                onTap: () {
+                  Navigator.pop(context);
+                },
+              ),
+            ),
+      flexibleSpace: isFlexibleSpaceDisabled
+          ? null
+          : FlexibleSpaceBar(
+              background: SafeArea(
+                child: Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  mainAxisSize: MainAxisSize.min,
+                  children: <Widget>[
+                    const SizedBox(height: toolbarHeight),
+                    Padding(
+                      padding: const EdgeInsets.symmetric(
+                        vertical: 4,
+                        horizontal: 16,
+                      ),
+                      child: Column(
+                        crossAxisAlignment: CrossAxisAlignment.start,
+                        children: [
+                          flexibleSpaceTitle == null
+                              ? const SizedBox.shrink()
+                              : flexibleSpaceTitle!,
+                          flexibleSpaceCaption == null
+                              ? const SizedBox.shrink()
+                              : Text(
+                                  flexibleSpaceCaption!,
+                                  style: textTheme.small.copyWith(
+                                    color: colorTheme.textMuted,
+                                  ),
+                                  overflow: TextOverflow.ellipsis,
+                                  maxLines: 1,
+                                )
+                        ],
+                      ),
+                    ),
+                  ],
+                ),
+              ),
+            ),
+    );
+  }
+
+  _actionsWithPaddingInBetween() {
+    if (actionIcons == null) {
+      return <Widget>[const SizedBox.shrink()];
+    }
+    final actions = <Widget>[];
+    bool addWhiteSpace = false;
+    final length = actionIcons!.length;
+    int index = 0;
+    if (length == 0) {
+      return <Widget>[const SizedBox.shrink()];
+    }
+    if (length == 1) {
+      return actionIcons;
+    }
+    while (index < length) {
+      if (!addWhiteSpace) {
+        actions.add(actionIcons![index]);
+        index++;
+        addWhiteSpace = true;
+      } else {
+        actions.add(const SizedBox(width: 4));
+        addWhiteSpace = false;
+      }
+    }
+    return actions;
+  }
+}

+ 8 - 4
lib/ui/components/toggle_switch_widget.dart

@@ -1,10 +1,11 @@
 import 'package:flutter/material.dart';
 import 'package:photos/ente_theme_data.dart';
 
-typedef OnChangedCallBack = void Function(bool);
+typedef OnChangedCallBack = Future<void> Function();
+typedef ValueCallBack = bool Function();
 
 class ToggleSwitchWidget extends StatefulWidget {
-  final bool value;
+  final ValueCallBack value;
   final OnChangedCallBack onChanged;
   const ToggleSwitchWidget({
     required this.value,
@@ -30,8 +31,11 @@ class _ToggleSwitchWidgetState extends State<ToggleSwitchWidget> {
             activeColor: enteColorScheme.primary400,
             inactiveTrackColor: enteColorScheme.fillMuted,
             materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
-            value: widget.value,
-            onChanged: widget.onChanged,
+            value: widget.value.call(),
+            onChanged: (value) async {
+              await widget.onChanged.call();
+              setState(() {});
+            },
           ),
         ),
       ),

+ 12 - 53
lib/ui/settings/backup_section_widget.dart

@@ -3,7 +3,6 @@
 import 'dart:io';
 
 import 'package:flutter/material.dart';
-import 'package:photos/core/configuration.dart';
 import 'package:photos/ente_theme_data.dart';
 import 'package:photos/models/backup_status.dart';
 import 'package:photos/models/duplicate_files.dart';
@@ -11,11 +10,10 @@ import 'package:photos/services/deduplication_service.dart';
 import 'package:photos/services/sync_service.dart';
 import 'package:photos/theme/ente_theme.dart';
 import 'package:photos/ui/backup_folder_selection_page.dart';
-import 'package:photos/ui/common/dialogs.dart';
+import 'package:photos/ui/backup_settings_screen.dart';
 import 'package:photos/ui/components/captioned_text_widget.dart';
 import 'package:photos/ui/components/expandable_menu_item_widget.dart';
 import 'package:photos/ui/components/menu_item_widget.dart';
-import 'package:photos/ui/components/toggle_switch_widget.dart';
 import 'package:photos/ui/settings/common_settings.dart';
 import 'package:photos/ui/tools/deduplicate_page.dart';
 import 'package:photos/ui/tools/free_space_page.dart';
@@ -64,60 +62,21 @@ class BackupSectionWidgetState extends State<BackupSectionWidget> {
       sectionOptionSpacing,
       MenuItemWidget(
         captionedTextWidget: const CaptionedTextWidget(
-          title: "Backup over mobile data",
-        ),
-        trailingSwitch: ToggleSwitchWidget(
-          value: Configuration.instance.shouldBackupOverMobileData(),
-          onChanged: (value) async {
-            Configuration.instance.setBackupOverMobileData(value);
-            setState(() {});
-          },
-        ),
-      ),
-      sectionOptionSpacing,
-      MenuItemWidget(
-        captionedTextWidget: const CaptionedTextWidget(
-          title: "Backup videos",
-        ),
-        trailingSwitch: ToggleSwitchWidget(
-          value: Configuration.instance.shouldBackupVideos(),
-          onChanged: (value) async {
-            Configuration.instance.setShouldBackupVideos(value);
-            setState(() {});
-          },
+          title: "Backup settings",
         ),
+        pressedColor: getEnteColorScheme(context).fillFaint,
+        trailingIcon: Icons.chevron_right_outlined,
+        trailingIconIsMuted: true,
+        onTap: () {
+          routeToPage(
+            context,
+            const BackupSettingsScreen(),
+          );
+        },
       ),
       sectionOptionSpacing,
     ];
-    if (Platform.isIOS) {
-      sectionOptions.addAll([
-        MenuItemWidget(
-          captionedTextWidget: const CaptionedTextWidget(
-            title: "Disable auto lock",
-          ),
-          trailingSwitch: ToggleSwitchWidget(
-            value: Configuration.instance.shouldKeepDeviceAwake(),
-            onChanged: (value) async {
-              if (value) {
-                final choice = await showChoiceDialog(
-                  context,
-                  "Disable automatic screen lock when ente is running?",
-                  "This will ensure faster uploads by ensuring your device does not sleep when uploads are in progress.",
-                  firstAction: "No",
-                  secondAction: "Yes",
-                );
-                if (choice != DialogUserChoice.secondChoice) {
-                  return;
-                }
-              }
-              await Configuration.instance.setShouldKeepDeviceAwake(value);
-              setState(() {});
-            },
-          ),
-        ),
-        sectionOptionSpacing,
-      ]);
-    }
+
     sectionOptions.addAll(
       [
         MenuItemWidget(

+ 79 - 85
lib/ui/settings/security_section_widget.dart

@@ -73,8 +73,8 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
                 ),
                 trailingSwitch: snapshot.hasData
                     ? ToggleSwitchWidget(
-                        value: snapshot.data,
-                        onChanged: (value) async {
+                        value: () => snapshot.data,
+                        onChanged: () async {
                           final hasAuthenticated =
                               await LocalAuthenticationService.instance
                                   .requestLocalAuthentication(
@@ -82,7 +82,7 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
                             "Please authenticate to configure two-factor authentication",
                           );
                           if (hasAuthenticated) {
-                            if (value) {
+                            if (!snapshot.data) {
                               UserService.instance.setupTwoFactor(context);
                             } else {
                               _disableTwoFactor();
@@ -106,18 +106,15 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
           title: "Lockscreen",
         ),
         trailingSwitch: ToggleSwitchWidget(
-          value: _config.shouldShowLockScreen(),
-          onChanged: (value) async {
-            final hasAuthenticated = await LocalAuthenticationService.instance
+          value: () => _config.shouldShowLockScreen(),
+          onChanged: () async {
+            await LocalAuthenticationService.instance
                 .requestLocalAuthForLockScreen(
               context,
-              value,
+              !_config.shouldShowLockScreen(),
               "Please authenticate to change lockscreen setting",
               "To enable lockscreen, please setup device passcode or screen lock in your system settings.",
             );
-            if (hasAuthenticated) {
-              setState(() {});
-            }
           },
         ),
       ),
@@ -131,81 +128,8 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
               title: "Hide from recents",
             ),
             trailingSwitch: ToggleSwitchWidget(
-              value: _config.shouldHideFromRecents(),
-              onChanged: (value) async {
-                if (value) {
-                  final AlertDialog alert = AlertDialog(
-                    title: const Text("Hide from recents?"),
-                    content: SingleChildScrollView(
-                      child: Column(
-                        mainAxisAlignment: MainAxisAlignment.start,
-                        crossAxisAlignment: CrossAxisAlignment.start,
-                        children: const [
-                          Text(
-                            "Hiding from the task switcher will prevent you from taking screenshots in this app.",
-                            style: TextStyle(
-                              height: 1.5,
-                            ),
-                          ),
-                          Padding(padding: EdgeInsets.all(8)),
-                          Text(
-                            "Are you sure?",
-                            style: TextStyle(
-                              height: 1.5,
-                            ),
-                          ),
-                        ],
-                      ),
-                    ),
-                    actions: [
-                      TextButton(
-                        child: Text(
-                          "No",
-                          style: TextStyle(
-                            color:
-                                Theme.of(context).colorScheme.defaultTextColor,
-                          ),
-                        ),
-                        onPressed: () {
-                          Navigator.of(context, rootNavigator: true)
-                              .pop('dialog');
-                        },
-                      ),
-                      TextButton(
-                        child: Text(
-                          "Yes",
-                          style: TextStyle(
-                            color:
-                                Theme.of(context).colorScheme.defaultTextColor,
-                          ),
-                        ),
-                        onPressed: () async {
-                          Navigator.of(context, rootNavigator: true)
-                              .pop('dialog');
-                          await _config.setShouldHideFromRecents(true);
-                          await FlutterWindowManager.addFlags(
-                            FlutterWindowManager.FLAG_SECURE,
-                          );
-                          setState(() {});
-                        },
-                      ),
-                    ],
-                  );
-
-                  showDialog(
-                    context: context,
-                    builder: (BuildContext context) {
-                      return alert;
-                    },
-                  );
-                } else {
-                  await _config.setShouldHideFromRecents(false);
-                  await FlutterWindowManager.clearFlags(
-                    FlutterWindowManager.FLAG_SECURE,
-                  );
-                  setState(() {});
-                }
-              },
+              value: () => _config.shouldHideFromRecents(),
+              onChanged: _hideFromRecentsOnChanged,
             ),
           ),
           sectionOptionSpacing,
@@ -284,4 +208,74 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
       },
     );
   }
+
+  Future<void> _hideFromRecentsOnChanged() async {
+    if (!_config.shouldHideFromRecents()) {
+      final AlertDialog alert = AlertDialog(
+        title: const Text("Hide from recents?"),
+        content: SingleChildScrollView(
+          child: Column(
+            mainAxisAlignment: MainAxisAlignment.start,
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: const [
+              Text(
+                "Hiding from the task switcher will prevent you from taking screenshots in this app.",
+                style: TextStyle(
+                  height: 1.5,
+                ),
+              ),
+              Padding(padding: EdgeInsets.all(8)),
+              Text(
+                "Are you sure?",
+                style: TextStyle(
+                  height: 1.5,
+                ),
+              ),
+            ],
+          ),
+        ),
+        actions: [
+          TextButton(
+            child: Text(
+              "No",
+              style: TextStyle(
+                color: Theme.of(context).colorScheme.defaultTextColor,
+              ),
+            ),
+            onPressed: () {
+              Navigator.of(context, rootNavigator: true).pop('dialog');
+            },
+          ),
+          TextButton(
+            child: Text(
+              "Yes",
+              style: TextStyle(
+                color: Theme.of(context).colorScheme.defaultTextColor,
+              ),
+            ),
+            onPressed: () async {
+              Navigator.of(context, rootNavigator: true).pop('dialog');
+              await _config.setShouldHideFromRecents(true);
+              await FlutterWindowManager.addFlags(
+                FlutterWindowManager.FLAG_SECURE,
+              );
+              setState(() {});
+            },
+          ),
+        ],
+      );
+
+      showDialog(
+        context: context,
+        builder: (BuildContext context) {
+          return alert;
+        },
+      );
+    } else {
+      await _config.setShouldHideFromRecents(false);
+      await FlutterWindowManager.clearFlags(
+        FlutterWindowManager.FLAG_SECURE,
+      );
+    }
+  }
 }