menu_item_widget.dart 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import 'package:expandable/expandable.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:photos/ente_theme_data.dart';
  4. class MenuItemWidget extends StatefulWidget {
  5. final Widget captionedTextWidget;
  6. final bool isExpandable;
  7. /// leading icon can be passed without specifing size of icon,
  8. /// this component sets size to 20x20 irrespective of passed icon's size
  9. final IconData? leadingIcon;
  10. final Color? leadingIconColor;
  11. /// trailing icon can be passed without size as default size set by
  12. /// flutter is what this component expects
  13. final IconData? trailingIcon;
  14. final Widget? trailingSwitch;
  15. final bool trailingIconIsMuted;
  16. final VoidCallback? onTap;
  17. final VoidCallback? onDoubleTap;
  18. final Color? menuItemColor;
  19. final bool alignCaptionedTextToLeft;
  20. final double borderRadius;
  21. final Color? pressedColor;
  22. final ExpandableController? expandableController;
  23. final bool isBottomBorderRadiusRemoved;
  24. final bool isTopBorderRadiusRemoved;
  25. /// disable gesture detector if not used
  26. final bool isGestureDetectorDisabled;
  27. const MenuItemWidget({
  28. required this.captionedTextWidget,
  29. this.isExpandable = false,
  30. this.leadingIcon,
  31. this.leadingIconColor,
  32. this.trailingIcon,
  33. this.trailingSwitch,
  34. this.trailingIconIsMuted = false,
  35. this.onTap,
  36. this.onDoubleTap,
  37. this.menuItemColor,
  38. this.alignCaptionedTextToLeft = false,
  39. this.borderRadius = 4.0,
  40. this.pressedColor,
  41. this.expandableController,
  42. this.isBottomBorderRadiusRemoved = false,
  43. this.isTopBorderRadiusRemoved = false,
  44. this.isGestureDetectorDisabled = false,
  45. Key? key,
  46. }) : super(key: key);
  47. @override
  48. State<MenuItemWidget> createState() => _MenuItemWidgetState();
  49. }
  50. class _MenuItemWidgetState extends State<MenuItemWidget> {
  51. Color? menuItemColor;
  52. @override
  53. void initState() {
  54. menuItemColor = widget.menuItemColor;
  55. if (widget.expandableController != null) {
  56. widget.expandableController!.addListener(() {
  57. setState(() {});
  58. });
  59. }
  60. super.initState();
  61. }
  62. @override
  63. void didChangeDependencies() {
  64. menuItemColor = widget.menuItemColor;
  65. super.didChangeDependencies();
  66. }
  67. @override
  68. void dispose() {
  69. if (widget.expandableController != null) {
  70. widget.expandableController!.dispose();
  71. }
  72. super.dispose();
  73. }
  74. @override
  75. Widget build(BuildContext context) {
  76. return widget.isExpandable || widget.isGestureDetectorDisabled
  77. ? menuItemWidget(context)
  78. : GestureDetector(
  79. onTap: widget.onTap,
  80. onDoubleTap: widget.onDoubleTap,
  81. onTapDown: _onTapDown,
  82. onTapUp: _onTapUp,
  83. onTapCancel: _onCancel,
  84. child: menuItemWidget(context),
  85. );
  86. }
  87. Widget menuItemWidget(BuildContext context) {
  88. final enteColorScheme = Theme.of(context).colorScheme.enteTheme.colorScheme;
  89. final borderRadius = Radius.circular(widget.borderRadius);
  90. final isExpanded = widget.expandableController?.value;
  91. final bottomBorderRadius =
  92. (isExpanded != null && isExpanded) || widget.isBottomBorderRadiusRemoved
  93. ? const Radius.circular(0)
  94. : borderRadius;
  95. final topBorderRadius = widget.isTopBorderRadiusRemoved
  96. ? const Radius.circular(0)
  97. : borderRadius;
  98. return AnimatedContainer(
  99. duration: const Duration(milliseconds: 20),
  100. width: double.infinity,
  101. padding: const EdgeInsets.only(left: 16, right: 12),
  102. decoration: BoxDecoration(
  103. borderRadius: BorderRadius.only(
  104. topLeft: topBorderRadius,
  105. topRight: topBorderRadius,
  106. bottomLeft: bottomBorderRadius,
  107. bottomRight: bottomBorderRadius,
  108. ),
  109. color: menuItemColor,
  110. ),
  111. child: Row(
  112. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  113. children: [
  114. widget.alignCaptionedTextToLeft && widget.leadingIcon == null
  115. ? const SizedBox.shrink()
  116. : Padding(
  117. padding: const EdgeInsets.only(right: 10),
  118. child: SizedBox(
  119. height: 20,
  120. width: 20,
  121. child: widget.leadingIcon == null
  122. ? const SizedBox.shrink()
  123. : FittedBox(
  124. fit: BoxFit.contain,
  125. child: Icon(
  126. widget.leadingIcon,
  127. color: widget.leadingIconColor ??
  128. enteColorScheme.strokeBase,
  129. ),
  130. ),
  131. ),
  132. ),
  133. widget.captionedTextWidget,
  134. widget.expandableController != null
  135. ? AnimatedOpacity(
  136. duration: const Duration(milliseconds: 100),
  137. curve: Curves.easeInOut,
  138. opacity: isExpanded! ? 0 : 1,
  139. child: AnimatedSwitcher(
  140. transitionBuilder: (child, animation) {
  141. return ScaleTransition(scale: animation, child: child);
  142. },
  143. duration: const Duration(milliseconds: 200),
  144. switchInCurve: Curves.easeOut,
  145. child: isExpanded
  146. ? const SizedBox.shrink()
  147. : Icon(widget.trailingIcon),
  148. ),
  149. )
  150. : widget.trailingIcon != null
  151. ? Icon(
  152. widget.trailingIcon,
  153. color: widget.trailingIconIsMuted
  154. ? enteColorScheme.strokeMuted
  155. : null,
  156. )
  157. : widget.trailingSwitch ?? const SizedBox.shrink(),
  158. ],
  159. ),
  160. );
  161. }
  162. void _onTapDown(details) {
  163. setState(() {
  164. menuItemColor = widget.pressedColor ?? widget.menuItemColor;
  165. });
  166. }
  167. void _onTapUp(details) {
  168. Future.delayed(
  169. const Duration(milliseconds: 100),
  170. () => setState(() {
  171. menuItemColor = widget.menuItemColor;
  172. }),
  173. );
  174. }
  175. void _onCancel() {
  176. setState(() {
  177. menuItemColor = widget.menuItemColor;
  178. });
  179. }
  180. }