bottom_action_bar_widget.dart 5.4 KB

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