fix: show bottom sheet on long press and remove slidable

This commit is contained in:
Prateek Sunal
2024-09-15 21:12:03 +05:30
parent a9c646ca77
commit 964075a32c
6 changed files with 208 additions and 114 deletions

View File

@@ -1,6 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'dart:ui' as ui;
import 'package:auto_size_text/auto_size_text.dart';
import 'package:clipboard/clipboard.dart';
@@ -24,7 +23,6 @@ import 'package:ente_auth/utils/totp_util.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_context_menu/flutter_context_menu.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:logging/logging.dart';
import 'package:move_to_background/move_to_background.dart';
@@ -214,7 +212,48 @@ class _CodeWidgetState extends State<CodeWidget> {
}
: null,
onLongPress: () {
_copyCurrentOTPToClipboard();
showOptionsForCode(
context,
isPinned: widget.code.isPinned,
isTrashed: widget.code.isTrashed,
onEdit: () async {
Future.delayed(
const Duration(milliseconds: 200),
() => _onEditPressed(null),
);
},
onShare: () async {
Future.delayed(
const Duration(milliseconds: 200),
() => _onSharePressed(null),
);
},
onPin: () async {
await _onPinPressed(null);
},
onTrashed: () async {
Future.delayed(
const Duration(milliseconds: 200),
() => _onTrashPressed(null),
);
},
onDelete: () async {
Future.delayed(
const Duration(milliseconds: 200),
() => _onDeletePressed(null),
);
},
onRestore: () async {
_onRestoreClicked(null);
},
onShowQR: () async {
Future.delayed(
const Duration(milliseconds: 200),
() => _onShowQrPressed(null),
);
},
);
// _copyCurrentOTPToClipboard();
},
child: getCardContents(l10n),
),
@@ -260,8 +299,8 @@ class _CodeWidgetState extends State<CodeWidget> {
label: l10n.edit,
icon: Icons.edit,
onSelected: () => _onEditPressed(null),
),
if (widget.code.isTrashed)
)
else
MenuItem(
label: l10n.restore,
icon: Icons.restore_outlined,
@@ -284,106 +323,8 @@ class _CodeWidgetState extends State<CodeWidget> {
child: clippedCard(l10n),
);
}
final double slideSpace = isCompactMode ? 4 : 8;
double extendRatio = isCompactMode ? 0.70 : 0.90;
if (widget.code.isTrashed) {
extendRatio = 0.50;
}
return Slidable(
key: ValueKey(widget.code.hashCode),
endActionPane: ActionPane(
extentRatio: extendRatio,
motion: const ScrollMotion(),
children: [
if (!widget.code.isTrashed && widget.code.type.isTOTPCompatible)
SizedBox(width: slideSpace),
if (!widget.code.isTrashed && widget.code.type.isTOTPCompatible)
SlidableAction(
onPressed: _onSharePressed,
backgroundColor: Colors.grey.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(8)),
foregroundColor:
Theme.of(context).colorScheme.inverseBackgroundColor,
icon: Icons.adaptive.share_outlined,
padding: const EdgeInsets.only(left: 4, right: 0),
spacing: 8,
),
if (!widget.code.isTrashed) SizedBox(width: slideSpace),
if (!widget.code.isTrashed)
CustomSlidableAction(
onPressed: _onPinPressed,
backgroundColor: Colors.grey.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(8)),
foregroundColor:
Theme.of(context).colorScheme.inverseBackgroundColor,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (widget.code.isPinned)
SvgPicture.asset(
"assets/svg/pin-active.svg",
colorFilter: ui.ColorFilter.mode(
Theme.of(context).colorScheme.primary,
BlendMode.srcIn,
),
)
else
SvgPicture.asset(
"assets/svg/pin-inactive.svg",
colorFilter: ui.ColorFilter.mode(
Theme.of(context).colorScheme.primary,
BlendMode.srcIn,
),
),
],
),
padding: const EdgeInsets.only(left: 4, right: 0),
),
if (!widget.code.isTrashed) SizedBox(width: slideSpace),
if (!widget.code.isTrashed)
SlidableAction(
onPressed: _onEditPressed,
backgroundColor: Colors.grey.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(8)),
foregroundColor:
Theme.of(context).colorScheme.inverseBackgroundColor,
icon: Icons.edit_outlined,
padding: const EdgeInsets.only(left: 4, right: 0),
spacing: 8,
),
if (widget.code.isTrashed) SizedBox(width: slideSpace),
if (widget.code.isTrashed)
SlidableAction(
onPressed: _onRestoreClicked,
backgroundColor: Colors.grey.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(8)),
foregroundColor:
Theme.of(context).colorScheme.inverseBackgroundColor,
icon: Icons.restore_outlined,
padding: const EdgeInsets.only(left: 4, right: 0),
spacing: 8,
),
SizedBox(width: slideSpace),
SlidableAction(
onPressed: widget.code.isTrashed
? _onDeletePressed
: _onTrashPressed,
backgroundColor: Colors.grey.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(8)),
foregroundColor: colorScheme.deleteCodeTextColor,
icon: widget.code.isTrashed
? Icons.delete_forever
: Icons.delete,
padding: const EdgeInsets.only(left: 0, right: 0),
spacing: 8,
),
],
),
child: Builder(
builder: (context) => clippedCard(l10n),
),
);
return clippedCard(l10n);
},
),
);

View File

@@ -14,7 +14,7 @@ import 'package:flutter/scheduler.dart';
enum ButtonSize { small, large }
enum ButtonAction { first, second, third, fourth, cancel, error }
enum ButtonAction { first, second, third, fourth, fifth, cancel, error }
class ButtonWidget extends StatelessWidget {
final IconData? icon;

View File

@@ -0,0 +1,89 @@
import 'dart:ui';
import 'package:ente_auth/theme/colors.dart';
import 'package:ente_auth/theme/effects.dart';
import 'package:ente_auth/ui/components/action_sheet_widget.dart';
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
import 'package:ente_auth/ui/components/components_constants.dart';
import 'package:ente_auth/ui/components/models/button_result.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
///Returns null if dismissed
Future<ButtonResult?> showGridActionSheet({
required BuildContext context,
required List<ButtonWidget> buttons,
ActionSheetType actionSheetType = ActionSheetType.defaultActionSheet,
bool enableDrag = true,
bool isDismissible = true,
bool isCheckIconGreen = false,
}) {
return showMaterialModalBottomSheet(
backgroundColor: Colors.transparent,
barrierColor: backdropFaintDark,
useRootNavigator: true,
context: context,
isDismissible: isDismissible,
enableDrag: enableDrag,
builder: (_) {
return GridActionSheetWidget(
actionButtons: buttons,
actionSheetType: actionSheetType,
isCheckIconGreen: isCheckIconGreen,
);
},
);
}
class GridActionSheetWidget extends StatelessWidget {
final List<ButtonWidget> actionButtons;
final ActionSheetType actionSheetType;
final bool isCheckIconGreen;
const GridActionSheetWidget({
required this.actionButtons,
required this.actionSheetType,
required this.isCheckIconGreen,
super.key,
});
@override
Widget build(BuildContext context) {
final blur = MediaQuery.of(context).platformBrightness == Brightness.light
? blurMuted
: blurBase;
final extraWidth = MediaQuery.of(context).size.width - restrictedMaxWidth;
final double? horizontalPadding = extraWidth > 0 ? extraWidth / 2 : null;
return Padding(
padding: EdgeInsets.fromLTRB(
horizontalPadding ?? 12,
0,
horizontalPadding ?? 12,
32,
),
child: Container(
decoration: BoxDecoration(boxShadow: shadowMenuLight),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: blur, sigmaY: blur),
child: Container(
color: backdropMutedDark,
child: AlignedGridView.count(
padding: const EdgeInsets.all(24),
shrinkWrap: true,
crossAxisCount: 2,
crossAxisSpacing: 6,
mainAxisSpacing: 6,
physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: (_, index) => actionButtons[index % 6],
itemCount: actionButtons.length,
),
),
),
),
),
);
}
}

View File

@@ -11,6 +11,7 @@ import 'package:ente_auth/ui/components/action_sheet_widget.dart';
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
import 'package:ente_auth/ui/components/components_constants.dart';
import 'package:ente_auth/ui/components/dialog_widget.dart';
import 'package:ente_auth/ui/components/grid_action_sheet_widget.dart';
import 'package:ente_auth/ui/components/models/button_result.dart';
import 'package:ente_auth/ui/components/models/button_type.dart';
import 'package:ente_auth/utils/email_util.dart';
@@ -424,3 +425,75 @@ Future<dynamic> showTextInputDialog(
},
);
}
///Will return null if dismissed by tapping outside
Future<ButtonResult?> showOptionsForCode(
BuildContext context, {
FutureVoidCallback? onShare,
FutureVoidCallback? onPin,
FutureVoidCallback? onShowQR,
FutureVoidCallback? onEdit,
FutureVoidCallback? onRestore,
FutureVoidCallback? onDelete,
FutureVoidCallback? onTrashed,
bool isPinned = false,
bool isTrashed = false,
}) async {
final buttons = [
ButtonWidget(
buttonType: ButtonType.neutral,
labelText: context.l10n.share,
isInAlert: true,
icon: Icons.adaptive.share_outlined,
onTap: onShare,
buttonAction: ButtonAction.first,
),
ButtonWidget(
buttonType: ButtonType.neutral,
isInAlert: true,
labelText: 'QR',
icon: Icons.qr_code_2_outlined,
onTap: onShowQR,
buttonAction: ButtonAction.second,
),
ButtonWidget(
buttonType: ButtonType.neutral,
isInAlert: true,
labelText: isPinned ? context.l10n.unpinText : context.l10n.pinText,
icon: isPinned ? Icons.push_pin : Icons.push_pin_outlined,
onTap: onPin,
buttonAction: ButtonAction.third,
),
if (isTrashed)
ButtonWidget(
isInAlert: true,
buttonType: ButtonType.neutral,
labelText: context.l10n.restore,
icon: Icons.restore_outlined,
onTap: onRestore,
buttonAction: ButtonAction.fourth,
)
else
ButtonWidget(
isInAlert: true,
buttonType: ButtonType.neutral,
labelText: context.l10n.edit,
icon: Icons.edit,
onTap: onEdit,
buttonAction: ButtonAction.fourth,
),
ButtonWidget(
isInAlert: true,
buttonType: ButtonType.neutral,
labelText: isTrashed ? context.l10n.delete : context.l10n.trash,
icon: isTrashed ? Icons.delete_forever : Icons.delete,
onTap: isTrashed ? onDelete : onTrashed,
buttonAction: ButtonAction.fifth,
),
];
return showGridActionSheet(
context: context,
buttons: buttons,
isDismissible: true,
);
}

View File

@@ -687,14 +687,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.1.2"
flutter_slidable:
dependency: "direct main"
description:
name: flutter_slidable
sha256: "673403d2eeef1f9e8483bd6d8d92aae73b1d8bd71f382bc3930f699c731bc27c"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
flutter_speed_dial:
dependency: "direct main"
description:

View File

@@ -59,7 +59,6 @@ dependencies:
sdk: flutter
flutter_native_splash: ^2.2.13
flutter_secure_storage: ^9.0.0
flutter_slidable: ^3.0.1
flutter_speed_dial: ^7.0.0
flutter_staggered_grid_view: ^0.7.0
flutter_svg: ^2.0.5