card_container.dart 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  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. const BoardCardContainer({
  10. required this.child,
  11. this.accessoryBuilder,
  12. Key? key,
  13. }) : super(key: key);
  14. @override
  15. Widget build(BuildContext context) {
  16. return ChangeNotifierProvider(
  17. create: (_) => _CardContainerNotifier(),
  18. child: Consumer<_CardContainerNotifier>(
  19. builder: (context, notifier, _) {
  20. Widget container = Center(child: child);
  21. if (accessoryBuilder != null) {
  22. final accessories = accessoryBuilder!(context);
  23. if (accessories.isNotEmpty) {
  24. container = _CardEnterRegion(
  25. child: container,
  26. accessories: accessories,
  27. );
  28. }
  29. }
  30. return Padding(
  31. padding: const EdgeInsets.all(8),
  32. child: ConstrainedBox(
  33. constraints: const BoxConstraints(minHeight: 30),
  34. child: container,
  35. ),
  36. );
  37. },
  38. ),
  39. );
  40. }
  41. }
  42. abstract class CardAccessory implements Widget {
  43. void onTap(BuildContext context);
  44. }
  45. typedef CardAccessoryBuilder = List<CardAccessory> Function(
  46. BuildContext buildContext,
  47. );
  48. class CardAccessoryContainer extends StatelessWidget {
  49. final List<CardAccessory> accessories;
  50. const CardAccessoryContainer({required this.accessories, Key? key})
  51. : super(key: key);
  52. @override
  53. Widget build(BuildContext context) {
  54. final theme = context.read<AppTheme>();
  55. final children = accessories.map((accessory) {
  56. final hover = FlowyHover(
  57. style: HoverStyle(
  58. hoverColor: theme.hover,
  59. backgroundColor: theme.surface,
  60. ),
  61. builder: (_, onHover) => Container(
  62. width: 26,
  63. height: 26,
  64. padding: const EdgeInsets.all(3),
  65. child: accessory,
  66. ),
  67. );
  68. return GestureDetector(
  69. child: hover,
  70. behavior: HitTestBehavior.opaque,
  71. onTap: () => accessory.onTap(context),
  72. );
  73. }).toList();
  74. return Wrap(children: children, spacing: 6);
  75. }
  76. }
  77. class _CardEnterRegion extends StatelessWidget {
  78. final Widget child;
  79. final List<CardAccessory> accessories;
  80. const _CardEnterRegion(
  81. {required this.child, required this.accessories, Key? key})
  82. : super(key: key);
  83. @override
  84. Widget build(BuildContext context) {
  85. return Selector<_CardContainerNotifier, bool>(
  86. selector: (context, notifier) => notifier.onEnter,
  87. builder: (context, onEnter, _) {
  88. List<Widget> children = [child];
  89. if (onEnter) {
  90. children.add(CardAccessoryContainer(accessories: accessories)
  91. .positioned(right: 0));
  92. }
  93. return MouseRegion(
  94. cursor: SystemMouseCursors.click,
  95. onEnter: (p) =>
  96. Provider.of<_CardContainerNotifier>(context, listen: false)
  97. .onEnter = true,
  98. onExit: (p) =>
  99. Provider.of<_CardContainerNotifier>(context, listen: false)
  100. .onEnter = false,
  101. child: IntrinsicHeight(
  102. child: Stack(
  103. alignment: AlignmentDirectional.center,
  104. fit: StackFit.expand,
  105. children: children,
  106. )),
  107. );
  108. },
  109. );
  110. }
  111. }
  112. class _CardContainerNotifier extends ChangeNotifier {
  113. bool _onEnter = false;
  114. _CardContainerNotifier();
  115. set onEnter(bool value) {
  116. if (_onEnter != value) {
  117. _onEnter = value;
  118. notifyListeners();
  119. }
  120. }
  121. bool get onEnter => _onEnter;
  122. }