action_sheet_widget.dart 5.5 KB

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