action_sheet_widget.dart 5.5 KB

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