bottom_action_bar_widget.dart 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import 'package:expandable/expandable.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:photos/core/constants.dart';
  4. import "package:photos/generated/l10n.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/buttons/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 Container(
  40. decoration: BoxDecoration(
  41. color: colorScheme.backgroundElevated,
  42. boxShadow: shadowFloatFaintLight,
  43. ),
  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. S.of(context).cancel,
  87. style: textTheme.bodyBold
  88. .copyWith(color: colorScheme.blurTextBase),
  89. ),
  90. ),
  91. ),
  92. )
  93. ],
  94. ),
  95. );
  96. }
  97. List<Widget> _iconButtons(BuildContext context) {
  98. final iconButtonsWithExpansionIcon = <Widget>[
  99. ...?iconButtons,
  100. ExpansionIconWidget(expandableController: _expandableController)
  101. ];
  102. return iconButtonsWithExpansionIcon;
  103. }
  104. ExpandableThemeData _getExpandableTheme() {
  105. return const ExpandableThemeData(
  106. hasIcon: false,
  107. useInkWell: false,
  108. tapBodyToCollapse: false,
  109. tapBodyToExpand: false,
  110. tapHeaderToExpand: false,
  111. animationDuration: Duration(milliseconds: 400),
  112. crossFadePoint: 0.5,
  113. );
  114. }
  115. }
  116. class ExpansionIconWidget extends StatefulWidget {
  117. final ExpandableController expandableController;
  118. const ExpansionIconWidget({required this.expandableController, super.key});
  119. @override
  120. State<ExpansionIconWidget> createState() => _ExpansionIconWidgetState();
  121. }
  122. class _ExpansionIconWidgetState extends State<ExpansionIconWidget> {
  123. @override
  124. void initState() {
  125. widget.expandableController.addListener(_expandableListener);
  126. super.initState();
  127. }
  128. @override
  129. void dispose() {
  130. widget.expandableController.removeListener(_expandableListener);
  131. super.dispose();
  132. }
  133. @override
  134. Widget build(BuildContext context) {
  135. final iconColor = getEnteColorScheme(context).blurStrokeBase;
  136. return AnimatedSwitcher(
  137. duration: const Duration(milliseconds: 200),
  138. switchInCurve: Curves.easeInOutExpo,
  139. child: widget.expandableController.value
  140. ? IconButtonWidget(
  141. key: const ValueKey<bool>(false),
  142. onTap: () {
  143. widget.expandableController.value = false;
  144. setState(() {});
  145. },
  146. icon: Icons.expand_more_outlined,
  147. iconButtonType: IconButtonType.primary,
  148. iconColor: iconColor,
  149. )
  150. : IconButtonWidget(
  151. key: const ValueKey<bool>(true),
  152. onTap: () {
  153. widget.expandableController.value = true;
  154. setState(() {});
  155. },
  156. icon: Icons.more_horiz_outlined,
  157. iconButtonType: IconButtonType.primary,
  158. iconColor: iconColor,
  159. ),
  160. );
  161. }
  162. _expandableListener() {
  163. if (mounted) {
  164. setState(() {});
  165. }
  166. }
  167. }