action_sheet_widget.dart 5.6 KB

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