bottom_action_bar_widget.dart 5.4 KB

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