navigation.dart 5.4 KB

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