action_sheet_widget.dart 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import 'dart:ui';
  2. import 'package:flutter/material.dart';
  3. import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
  4. import 'package:photos/core/constants.dart';
  5. import "package:photos/models/search/button_result.dart";
  6. import 'package:photos/theme/colors.dart';
  7. import 'package:photos/theme/effects.dart';
  8. import 'package:photos/theme/ente_theme.dart';
  9. import 'package:photos/ui/components/button_widget.dart';
  10. import 'package:photos/utils/separators_util.dart';
  11. enum ActionSheetType {
  12. defaultActionSheet,
  13. iconOnly,
  14. }
  15. ///Returns null if dismissed
  16. Future<ButtonResult?> showActionSheet({
  17. required BuildContext context,
  18. required List<ButtonWidget> buttons,
  19. ActionSheetType actionSheetType = ActionSheetType.defaultActionSheet,
  20. bool enableDrag = true,
  21. bool isDismissible = true,
  22. bool isCheckIconGreen = false,
  23. String? title,
  24. Widget? bodyWidget,
  25. String? body,
  26. String? bodyHighlight,
  27. }) {
  28. return showMaterialModalBottomSheet(
  29. backgroundColor: Colors.transparent,
  30. barrierColor: backdropFaintDark,
  31. useRootNavigator: true,
  32. context: context,
  33. isDismissible: isDismissible,
  34. enableDrag: enableDrag,
  35. builder: (_) {
  36. return ActionSheetWidget(
  37. title: title,
  38. bodyWidget: bodyWidget,
  39. body: body,
  40. bodyHighlight: bodyHighlight,
  41. actionButtons: buttons,
  42. actionSheetType: actionSheetType,
  43. isCheckIconGreen: isCheckIconGreen,
  44. );
  45. },
  46. );
  47. }
  48. class ActionSheetWidget extends StatelessWidget {
  49. final String? title;
  50. final Widget? bodyWidget;
  51. final String? body;
  52. final String? bodyHighlight;
  53. final List<ButtonWidget> actionButtons;
  54. final ActionSheetType actionSheetType;
  55. final bool isCheckIconGreen;
  56. const ActionSheetWidget({
  57. required this.actionButtons,
  58. required this.actionSheetType,
  59. required this.isCheckIconGreen,
  60. this.title,
  61. this.bodyWidget,
  62. this.body,
  63. this.bodyHighlight,
  64. super.key,
  65. });
  66. @override
  67. Widget build(BuildContext context) {
  68. final isTitleAndBodyNull =
  69. title == null && bodyWidget == null && body == null;
  70. final blur = MediaQuery.of(context).platformBrightness == Brightness.light
  71. ? blurMuted
  72. : blurBase;
  73. final extraWidth = MediaQuery.of(context).size.width - restrictedMaxWidth;
  74. final double? horizontalPadding = extraWidth > 0 ? extraWidth / 2 : null;
  75. return Padding(
  76. padding: EdgeInsets.fromLTRB(
  77. horizontalPadding ?? 12,
  78. 12,
  79. horizontalPadding ?? 12,
  80. 32,
  81. ),
  82. child: Container(
  83. decoration: BoxDecoration(boxShadow: shadowMenuLight),
  84. child: ClipRRect(
  85. borderRadius: const BorderRadius.all(Radius.circular(8)),
  86. child: BackdropFilter(
  87. filter: ImageFilter.blur(sigmaX: blur, sigmaY: blur),
  88. child: Container(
  89. color: backdropMutedDark,
  90. child: Padding(
  91. padding: EdgeInsets.fromLTRB(
  92. 24,
  93. 24,
  94. 24,
  95. isTitleAndBodyNull ? 24 : 28,
  96. ),
  97. child: Column(
  98. mainAxisSize: MainAxisSize.min,
  99. children: [
  100. isTitleAndBodyNull
  101. ? const SizedBox.shrink()
  102. : Padding(
  103. padding: const EdgeInsets.only(bottom: 36),
  104. child: ContentContainerWidget(
  105. title: title,
  106. bodyWidget: bodyWidget,
  107. body: body,
  108. bodyHighlight: bodyHighlight,
  109. actionSheetType: actionSheetType,
  110. isCheckIconGreen: isCheckIconGreen,
  111. ),
  112. ),
  113. ActionButtons(
  114. actionButtons,
  115. ),
  116. ],
  117. ),
  118. ),
  119. ),
  120. ),
  121. ),
  122. ),
  123. );
  124. }
  125. }
  126. class ContentContainerWidget extends StatelessWidget {
  127. final String? title;
  128. final Widget? bodyWidget;
  129. final String? body;
  130. final String? bodyHighlight;
  131. final ActionSheetType actionSheetType;
  132. final bool isCheckIconGreen;
  133. const ContentContainerWidget({
  134. required this.actionSheetType,
  135. required this.isCheckIconGreen,
  136. this.title,
  137. this.bodyWidget,
  138. this.body,
  139. this.bodyHighlight,
  140. super.key,
  141. });
  142. @override
  143. Widget build(BuildContext context) {
  144. final textTheme = getEnteTextTheme(context);
  145. final bool bodyMissing = body == null && bodyWidget == null;
  146. debugPrint("body missing $bodyMissing");
  147. return Column(
  148. mainAxisSize: MainAxisSize.min,
  149. //todo: set cross axis to center when icon should be shown in place of body
  150. crossAxisAlignment: actionSheetType == ActionSheetType.defaultActionSheet
  151. ? CrossAxisAlignment.stretch
  152. : CrossAxisAlignment.center,
  153. children: [
  154. title == null
  155. ? const SizedBox.shrink()
  156. : Text(
  157. title!,
  158. style: textTheme.largeBold
  159. .copyWith(color: textBaseDark), //constant color
  160. ),
  161. title == null || bodyMissing
  162. ? const SizedBox.shrink()
  163. : const SizedBox(height: 19),
  164. actionSheetType == ActionSheetType.defaultActionSheet
  165. ? bodyMissing
  166. ? const SizedBox.shrink()
  167. : (bodyWidget != null
  168. ? bodyWidget!
  169. : Text(
  170. body!,
  171. style: textTheme.body
  172. .copyWith(color: textMutedDark), //constant color
  173. ))
  174. : Icon(
  175. Icons.check_outlined,
  176. size: 48,
  177. color: isCheckIconGreen
  178. ? getEnteColorScheme(context).primary700
  179. : strokeBaseDark,
  180. ),
  181. actionSheetType == ActionSheetType.defaultActionSheet &&
  182. bodyHighlight != null
  183. ? Padding(
  184. padding: const EdgeInsets.only(top: 19.0),
  185. child: Text(
  186. bodyHighlight!,
  187. style: textTheme.body
  188. .copyWith(color: textBaseDark), //constant color
  189. ),
  190. )
  191. : const SizedBox.shrink(),
  192. ],
  193. );
  194. }
  195. }
  196. class ActionButtons extends StatelessWidget {
  197. final List<Widget> actionButtons;
  198. const ActionButtons(this.actionButtons, {super.key});
  199. @override
  200. Widget build(BuildContext context) {
  201. final actionButtonsWithSeparators = actionButtons;
  202. return Column(
  203. children:
  204. //Separator height is 8pts in figma. -2pts here as the action
  205. //buttons are 2pts extra in height in code compared to figma because
  206. //of the border(1pt top + 1pt bottom) of action buttons.
  207. addSeparators(actionButtonsWithSeparators, const SizedBox(height: 6)),
  208. );
  209. }
  210. }