bottom_action_bar_widget.dart 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import 'dart:ui';
  2. import 'package:expandable/expandable.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:photos/core/constants.dart';
  5. import 'package:photos/models/gallery_type.dart';
  6. import 'package:photos/models/selected_files.dart';
  7. import 'package:photos/theme/effects.dart';
  8. import 'package:photos/theme/ente_theme.dart';
  9. import 'package:photos/ui/components/bottom_action_bar/action_bar_widget.dart';
  10. import 'package:photos/ui/components/icon_button_widget.dart';
  11. class BottomActionBarWidget extends StatelessWidget {
  12. final String? text;
  13. final List<Widget>? iconButtons;
  14. final Widget expandedMenu;
  15. final SelectedFiles? selectedFiles;
  16. final VoidCallback? onCancel;
  17. final bool hasSmallerBottomPadding;
  18. final GalleryType type;
  19. BottomActionBarWidget({
  20. required this.expandedMenu,
  21. required this.hasSmallerBottomPadding,
  22. required this.type,
  23. this.selectedFiles,
  24. this.text,
  25. this.iconButtons,
  26. this.onCancel,
  27. super.key,
  28. });
  29. final ExpandableController _expandableController =
  30. ExpandableController(initialExpanded: false);
  31. @override
  32. Widget build(BuildContext context) {
  33. final widthOfScreen = MediaQuery.of(context).size.width;
  34. final colorScheme = getEnteColorScheme(context);
  35. final textTheme = getEnteTextTheme(context);
  36. final double leftRightPadding = widthOfScreen > restrictedMaxWidth
  37. ? (widthOfScreen - restrictedMaxWidth) / 2
  38. : 0;
  39. return ClipRRect(
  40. child: BackdropFilter(
  41. filter: ImageFilter.blur(sigmaX: blurFaint, sigmaY: blurFaint),
  42. child: Container(
  43. color: colorScheme.backdropBase,
  44. padding: EdgeInsets.only(
  45. top: 4,
  46. bottom: hasSmallerBottomPadding ? 24 : 36,
  47. right: leftRightPadding,
  48. left: leftRightPadding,
  49. ),
  50. child: Column(
  51. mainAxisSize: MainAxisSize.min,
  52. children: [
  53. ExpandableNotifier(
  54. controller: _expandableController,
  55. child: ExpandablePanel(
  56. theme: _getExpandableTheme(),
  57. header: Padding(
  58. padding: EdgeInsets.symmetric(
  59. horizontal: text == null ? 12 : 0,
  60. ),
  61. child: ActionBarWidget(
  62. selectedFiles: selectedFiles,
  63. galleryType: type,
  64. text: text,
  65. iconButtons: _iconButtons(context),
  66. ),
  67. ),
  68. expanded: expandedMenu,
  69. collapsed: const SizedBox.shrink(),
  70. controller: _expandableController,
  71. ),
  72. ),
  73. Padding(
  74. padding: const EdgeInsets.symmetric(
  75. horizontal: 16,
  76. vertical: 14,
  77. ),
  78. child: GestureDetector(
  79. behavior: HitTestBehavior.opaque,
  80. onTap: () {
  81. onCancel?.call();
  82. _expandableController.value = false;
  83. },
  84. child: Center(
  85. child: Text(
  86. "Cancel",
  87. style: textTheme.bodyBold
  88. .copyWith(color: colorScheme.blurTextBase),
  89. ),
  90. ),
  91. ),
  92. )
  93. ],
  94. ),
  95. ),
  96. ),
  97. );
  98. }
  99. List<Widget> _iconButtons(BuildContext context) {
  100. final iconButtonsWithExpansionIcon = <Widget>[
  101. ...?iconButtons,
  102. ExpansionIconWidget(expandableController: _expandableController)
  103. ];
  104. return iconButtonsWithExpansionIcon;
  105. }
  106. ExpandableThemeData _getExpandableTheme() {
  107. return const ExpandableThemeData(
  108. hasIcon: false,
  109. useInkWell: false,
  110. tapBodyToCollapse: false,
  111. tapBodyToExpand: false,
  112. tapHeaderToExpand: false,
  113. animationDuration: Duration(milliseconds: 400),
  114. crossFadePoint: 0.5,
  115. );
  116. }
  117. }
  118. class ExpansionIconWidget extends StatefulWidget {
  119. final ExpandableController expandableController;
  120. const ExpansionIconWidget({required this.expandableController, super.key});
  121. @override
  122. State<ExpansionIconWidget> createState() => _ExpansionIconWidgetState();
  123. }
  124. class _ExpansionIconWidgetState extends State<ExpansionIconWidget> {
  125. @override
  126. void initState() {
  127. widget.expandableController.addListener(_expandableListener);
  128. super.initState();
  129. }
  130. @override
  131. void dispose() {
  132. widget.expandableController.removeListener(_expandableListener);
  133. super.dispose();
  134. }
  135. @override
  136. Widget build(BuildContext context) {
  137. final iconColor = getEnteColorScheme(context).blurStrokeBase;
  138. return AnimatedSwitcher(
  139. duration: const Duration(milliseconds: 200),
  140. switchInCurve: Curves.easeInOutExpo,
  141. child: widget.expandableController.value
  142. ? IconButtonWidget(
  143. key: const ValueKey<bool>(false),
  144. onTap: () {
  145. widget.expandableController.value = false;
  146. setState(() {});
  147. },
  148. icon: Icons.expand_more_outlined,
  149. iconButtonType: IconButtonType.primary,
  150. iconColor: iconColor,
  151. )
  152. : IconButtonWidget(
  153. key: const ValueKey<bool>(true),
  154. onTap: () {
  155. widget.expandableController.value = true;
  156. setState(() {});
  157. },
  158. icon: Icons.more_horiz_outlined,
  159. iconButtonType: IconButtonType.primary,
  160. iconColor: iconColor,
  161. ),
  162. );
  163. }
  164. _expandableListener() {
  165. if (mounted) {
  166. setState(() {});
  167. }
  168. }
  169. }