card_container.dart 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import 'package:flowy_infra/theme.dart';
  2. import 'package:flowy_infra_ui/style_widget/hover.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:provider/provider.dart';
  5. import 'package:styled_widget/styled_widget.dart';
  6. class BoardCardContainer extends StatelessWidget {
  7. final Widget child;
  8. final CardAccessoryBuilder? accessoryBuilder;
  9. final void Function(BuildContext) onTap;
  10. const BoardCardContainer({
  11. required this.child,
  12. required this.onTap,
  13. this.accessoryBuilder,
  14. Key? key,
  15. }) : super(key: key);
  16. @override
  17. Widget build(BuildContext context) {
  18. return ChangeNotifierProvider(
  19. create: (_) => _CardContainerNotifier(),
  20. child: Consumer<_CardContainerNotifier>(
  21. builder: (context, notifier, _) {
  22. Widget container = Center(child: child);
  23. if (accessoryBuilder != null) {
  24. final accessories = accessoryBuilder!(context);
  25. if (accessories.isNotEmpty) {
  26. container = _CardEnterRegion(
  27. accessories: accessories,
  28. child: container,
  29. );
  30. }
  31. }
  32. return GestureDetector(
  33. onTap: () => onTap(context),
  34. child: Padding(
  35. padding: const EdgeInsets.all(8),
  36. child: ConstrainedBox(
  37. constraints: const BoxConstraints(minHeight: 30),
  38. child: container,
  39. ),
  40. ),
  41. );
  42. },
  43. ),
  44. );
  45. }
  46. }
  47. abstract class CardAccessory implements Widget {
  48. void onTap(BuildContext context);
  49. }
  50. typedef CardAccessoryBuilder = List<CardAccessory> Function(
  51. BuildContext buildContext,
  52. );
  53. class CardAccessoryContainer extends StatelessWidget {
  54. final List<CardAccessory> accessories;
  55. const CardAccessoryContainer({required this.accessories, Key? key})
  56. : super(key: key);
  57. @override
  58. Widget build(BuildContext context) {
  59. final theme = context.read<AppTheme>();
  60. final children = accessories.map((accessory) {
  61. final hover = FlowyHover(
  62. style: HoverStyle(
  63. hoverColor: theme.hover,
  64. backgroundColor: theme.surface,
  65. borderRadius: BorderRadius.zero,
  66. ),
  67. builder: (_, onHover) => SizedBox(
  68. width: 24,
  69. height: 24,
  70. child: accessory,
  71. ),
  72. );
  73. return GestureDetector(
  74. behavior: HitTestBehavior.opaque,
  75. onTap: () => accessory.onTap(context),
  76. child: hover,
  77. );
  78. }).toList();
  79. return Container(
  80. clipBehavior: Clip.hardEdge,
  81. decoration: _makeBoxDecoration(context),
  82. child: Row(children: children),
  83. );
  84. }
  85. }
  86. BoxDecoration _makeBoxDecoration(BuildContext context) {
  87. final theme = context.read<AppTheme>();
  88. final borderSide = BorderSide(color: theme.shader6, width: 1.0);
  89. return BoxDecoration(
  90. color: Colors.transparent,
  91. border: Border.fromBorderSide(borderSide),
  92. // boxShadow: const [
  93. // BoxShadow(
  94. // color: Colors.transparent,
  95. // spreadRadius: 0,
  96. // blurRadius: 5,
  97. // offset: Offset.zero,
  98. // )
  99. // ],
  100. borderRadius: const BorderRadius.all(Radius.circular(4)),
  101. );
  102. }
  103. class _CardEnterRegion extends StatelessWidget {
  104. final Widget child;
  105. final List<CardAccessory> accessories;
  106. const _CardEnterRegion(
  107. {required this.child, required this.accessories, Key? key})
  108. : super(key: key);
  109. @override
  110. Widget build(BuildContext context) {
  111. return Selector<_CardContainerNotifier, bool>(
  112. selector: (context, notifier) => notifier.onEnter,
  113. builder: (context, onEnter, _) {
  114. List<Widget> children = [child];
  115. if (onEnter) {
  116. children.add(CardAccessoryContainer(
  117. accessories: accessories,
  118. ).positioned(right: 0));
  119. }
  120. return MouseRegion(
  121. cursor: SystemMouseCursors.click,
  122. onEnter: (p) =>
  123. Provider.of<_CardContainerNotifier>(context, listen: false)
  124. .onEnter = true,
  125. onExit: (p) =>
  126. Provider.of<_CardContainerNotifier>(context, listen: false)
  127. .onEnter = false,
  128. child: IntrinsicHeight(
  129. child: Stack(
  130. alignment: AlignmentDirectional.topEnd,
  131. fit: StackFit.expand,
  132. children: children,
  133. )),
  134. );
  135. },
  136. );
  137. }
  138. }
  139. class _CardContainerNotifier extends ChangeNotifier {
  140. bool _onEnter = false;
  141. _CardContainerNotifier();
  142. set onEnter(bool value) {
  143. if (_onEnter != value) {
  144. _onEnter = value;
  145. notifyListeners();
  146. }
  147. }
  148. bool get onEnter => _onEnter;
  149. }