card_container.dart 4.8 KB

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