menu_item_widget.dart 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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. ),
  129. ),
  130. ),
  131. ),
  132. widget.captionedTextWidget,
  133. widget.expandableController != null
  134. ? AnimatedOpacity(
  135. duration: const Duration(milliseconds: 100),
  136. curve: Curves.easeInOut,
  137. opacity: isExpanded! ? 0 : 1,
  138. child: AnimatedSwitcher(
  139. transitionBuilder: (child, animation) {
  140. return ScaleTransition(scale: animation, child: child);
  141. },
  142. duration: const Duration(milliseconds: 200),
  143. switchInCurve: Curves.easeOut,
  144. child: isExpanded
  145. ? const SizedBox.shrink()
  146. : Icon(widget.trailingIcon),
  147. ),
  148. )
  149. : widget.trailingIcon != null
  150. ? Icon(
  151. widget.trailingIcon,
  152. color: widget.trailingIconIsMuted
  153. ? enteColorScheme.strokeMuted
  154. : null,
  155. )
  156. : widget.trailingSwitch ?? const SizedBox.shrink(),
  157. ],
  158. ),
  159. );
  160. }
  161. void _onTapDown(details) {
  162. setState(() {
  163. menuItemColor = widget.pressedColor ?? widget.menuItemColor;
  164. });
  165. }
  166. void _onTapUp(details) {
  167. Future.delayed(
  168. const Duration(milliseconds: 100),
  169. () => setState(() {
  170. menuItemColor = widget.menuItemColor;
  171. }),
  172. );
  173. }
  174. void _onCancel() {
  175. setState(() {
  176. menuItemColor = widget.menuItemColor;
  177. });
  178. }
  179. }