navigation.dart 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
  2. import 'package:flowy_infra_ui/style_widget/text.dart';
  3. import 'package:flowy_infra_ui/style_widget/button.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:provider/provider.dart';
  6. import 'package:styled_widget/styled_widget.dart';
  7. typedef NaviAction = void Function();
  8. class NavigationNotifier with ChangeNotifier {
  9. List<NavigationItem> navigationItems;
  10. NavigationNotifier({required this.navigationItems});
  11. void update(HomeStackNotifier notifier) {
  12. navigationItems = notifier.context.navigationItems;
  13. notifyListeners();
  14. }
  15. }
  16. // [[diagram: HomeStack navigation flow]]
  17. // ┌───────────────────────┐
  18. // 2.notify listeners ┌──────│DefaultHomeStackContext│
  19. // ┌────────────────┐ ┌───────────┐ ┌────────────────┐ │ └───────────────────────┘
  20. // │HomeStackNotifie│◀──────────│ HomeStack │◀──│HomeStackContext│◀─ impl
  21. // └────────────────┘ └───────────┘ └────────────────┘ │ ┌───────────────────┐
  22. // │ ▲ └───────│ DocStackContext │
  23. // │ │ └───────────────────┘
  24. // 3.notify change 1.set context
  25. // │ │
  26. // ▼ │
  27. // ┌───────────────────┐ ┌──────────────────┐
  28. // │NavigationNotifier │ │ ViewSectionItem │
  29. // └───────────────────┘ └──────────────────┘
  30. // │
  31. // │
  32. // ▼
  33. // ┌─────────────────┐
  34. // │ FlowyNavigation │ 4.render navigation items
  35. // └─────────────────┘
  36. class FlowyNavigation extends StatelessWidget {
  37. const FlowyNavigation({Key? key}) : super(key: key);
  38. @override
  39. Widget build(BuildContext context) {
  40. return ChangeNotifierProxyProvider<HomeStackNotifier, NavigationNotifier>(
  41. create: (_) {
  42. final notifier = Provider.of<HomeStackNotifier>(context, listen: false);
  43. return NavigationNotifier(
  44. navigationItems: notifier.context.navigationItems,
  45. );
  46. },
  47. update: (_, notifier, controller) => controller!..update(notifier),
  48. child: Consumer(builder: (ctx, NavigationNotifier notifier, child) {
  49. return Row(children: _renderChildren(notifier.navigationItems));
  50. }),
  51. );
  52. }
  53. List<Widget> _renderChildren(List<NavigationItem> items) {
  54. if (items.isEmpty) {
  55. return [];
  56. }
  57. List<NavigationItem> newItems = _filter(items);
  58. Widget last = NaviItemWidget(newItems.removeLast());
  59. List<Widget> widgets = List.empty(growable: true);
  60. widgets.addAll(newItems.map((item) => NaviItemDivider(child: NaviItemWidget(item))).toList());
  61. widgets.add(last);
  62. return widgets;
  63. }
  64. List<NavigationItem> _filter(List<NavigationItem> items) {
  65. final length = items.length;
  66. if (length > 4) {
  67. final first = items[0];
  68. final ellipsisItems = items.getRange(1, length - 2).toList();
  69. final last = items.getRange(length - 2, length).toList();
  70. return [
  71. first,
  72. EllipsisNaviItem(items: ellipsisItems),
  73. ...last,
  74. ];
  75. } else {
  76. return items;
  77. }
  78. }
  79. }
  80. class IconNaviItemWidget extends StatelessWidget {
  81. final NavigationItem item;
  82. const IconNaviItemWidget(this.item, {Key? key}) : super(key: key);
  83. @override
  84. Widget build(BuildContext context) {
  85. return SizedBox(
  86. height: 24,
  87. child: InkWell(
  88. child: item.naviTitle,
  89. onTap: () {
  90. debugPrint('show app document');
  91. },
  92. ).padding(horizontal: 8, vertical: 2),
  93. );
  94. }
  95. }
  96. class NaviItemWidget extends StatelessWidget {
  97. final NavigationItem item;
  98. const NaviItemWidget(this.item, {Key? key}) : super(key: key);
  99. @override
  100. Widget build(BuildContext context) {
  101. return SizedBox(
  102. height: 24,
  103. child: InkWell(
  104. child: item.naviTitle,
  105. onTap: () {
  106. debugPrint('show app document');
  107. },
  108. ).padding(horizontal: 8, vertical: 2),
  109. );
  110. }
  111. }
  112. class NaviItemDivider extends StatelessWidget {
  113. final Widget child;
  114. const NaviItemDivider({Key? key, required this.child}) : super(key: key);
  115. @override
  116. Widget build(BuildContext context) {
  117. return Row(
  118. children: [child, const Text('/').padding(horizontal: 2)],
  119. );
  120. }
  121. }
  122. class EllipsisNaviItem extends NavigationItem {
  123. final List<NavigationItem> items;
  124. EllipsisNaviItem({
  125. required this.items,
  126. });
  127. @override
  128. Widget get naviTitle => const FlowyText.medium('...');
  129. @override
  130. NavigationCallback get action => (id) {};
  131. @override
  132. String get identifier => "Ellipsis";
  133. }