navigation.dart 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
  2. import 'package:flowy_infra_ui/style_widget/text_button.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:provider/provider.dart';
  5. import 'package:styled_widget/styled_widget.dart';
  6. typedef NaviAction = void Function();
  7. class NavigationNotifier with ChangeNotifier {
  8. HomeStackNotifier homeStackNotifier;
  9. NavigationNotifier(this.homeStackNotifier);
  10. void update(HomeStackNotifier notifier) {
  11. homeStackNotifier = notifier;
  12. notifyListeners();
  13. }
  14. List<NavigationItem> get naviItems => homeStackNotifier.context.navigationItems;
  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: (_) => NavigationNotifier(
  42. Provider.of<HomeStackNotifier>(context, listen: false),
  43. ),
  44. update: (_, notifier, controller) => controller!..update(notifier),
  45. child: Consumer(builder: (ctx, NavigationNotifier notifier, child) {
  46. return Row(children: _renderChildren(notifier.naviItems));
  47. }),
  48. );
  49. }
  50. List<Widget> _renderChildren(List<NavigationItem> items) {
  51. if (items.isEmpty) {
  52. return [];
  53. }
  54. List<NavigationItem> newItems = _filter(items);
  55. Widget last = NaviItemWidget(newItems.removeLast());
  56. List<Widget> widgets = List.empty(growable: true);
  57. widgets.addAll(newItems.map((item) => NaviItemDivider(child: NaviItemWidget(item))).toList());
  58. widgets.add(last);
  59. return widgets;
  60. }
  61. List<NavigationItem> _filter(List<NavigationItem> items) {
  62. final length = items.length;
  63. if (length > 4) {
  64. final first = items[0];
  65. final ellipsisItems = items.getRange(1, length - 2).toList();
  66. final last = items.getRange(length - 2, length).toList();
  67. return [
  68. first,
  69. EllipsisNaviItem(items: ellipsisItems),
  70. ...last,
  71. ];
  72. } else {
  73. return items;
  74. }
  75. }
  76. }
  77. class IconNaviItemWidget extends StatelessWidget {
  78. final NavigationItem item;
  79. const IconNaviItemWidget(this.item, {Key? key}) : super(key: key);
  80. @override
  81. Widget build(BuildContext context) {
  82. return SizedBox(
  83. height: 24,
  84. child: FlowyTextButton(
  85. item.title,
  86. padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
  87. fontSize: 12,
  88. onPressed: () {
  89. debugPrint('show app document');
  90. },
  91. ),
  92. );
  93. }
  94. }
  95. class NaviItemWidget extends StatelessWidget {
  96. final NavigationItem item;
  97. const NaviItemWidget(this.item, {Key? key}) : super(key: key);
  98. @override
  99. Widget build(BuildContext context) {
  100. return SizedBox(
  101. height: 24,
  102. child: FlowyTextButton(
  103. item.title,
  104. padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
  105. fontSize: 12,
  106. onPressed: () {
  107. debugPrint('show app document');
  108. },
  109. ),
  110. );
  111. }
  112. }
  113. class NaviItemDivider extends StatelessWidget {
  114. final Widget child;
  115. const NaviItemDivider({Key? key, required this.child}) : super(key: key);
  116. @override
  117. Widget build(BuildContext context) {
  118. return Row(
  119. children: [child, const Text('/').padding(horizontal: 2)],
  120. );
  121. }
  122. }
  123. class EllipsisNaviItem extends NavigationItem {
  124. final List<NavigationItem> items;
  125. EllipsisNaviItem({
  126. required this.items,
  127. });
  128. @override
  129. String get title => "...";
  130. @override
  131. NavigationCallback get action => (id) {};
  132. @override
  133. String get identifier => "Ellipsis";
  134. }