LCOV - code coverage report
Current view: top level - lib/src/pages - tab_pages.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 126 126 100.0 %
Date: 2021-04-04 17:06:32 Functions: 0 0 -

          Line data    Source code
       1             : part of '../../routemaster.dart';
       2             : 
       3             : class IndexedPage extends StatefulPage<void> with IndexedRouteMixIn {
       4             :   final Widget child;
       5             : 
       6             :   @override
       7             :   final List<String> paths;
       8             : 
       9           1 :   IndexedPage({
      10             :     required this.child,
      11             :     required this.paths,
      12             :   });
      13             : 
      14           1 :   @override
      15             :   PageState createState(Routemaster delegate, RouteInfo routeInfo) {
      16           1 :     return IndexedPageState(this, delegate, routeInfo);
      17             :   }
      18             : 
      19           1 :   static IndexedPageState of(BuildContext context) {
      20             :     return context
      21           1 :         .dependOnInheritedWidgetOfExactType<_IndexedPageStateProvider>()!
      22           1 :         .pageState;
      23             :   }
      24             : }
      25             : 
      26             : class _IndexedPageStateProvider extends InheritedNotifier {
      27             :   final IndexedPageState pageState;
      28             : 
      29           1 :   _IndexedPageStateProvider({
      30             :     required Widget child,
      31             :     required this.pageState,
      32           1 :   }) : super(
      33             :           child: child,
      34           1 :           notifier: pageState._delegate,
      35             :         );
      36             : 
      37           1 :   @override
      38             :   bool updateShouldNotify(covariant _IndexedPageStateProvider oldWidget) {
      39           3 :     return pageState != oldWidget.pageState;
      40             :   }
      41             : }
      42             : 
      43             : class IndexedPageState extends PageState
      44             :     with ChangeNotifier, IndexedPageStateMixIn {
      45             :   @override
      46             :   final IndexedPage _page;
      47             : 
      48             :   @override
      49             :   final Routemaster _delegate;
      50             : 
      51             :   @override
      52             :   final RouteInfo routeInfo;
      53             : 
      54           1 :   IndexedPageState(
      55             :     this._page,
      56             :     this._delegate,
      57             :     this.routeInfo,
      58             :   ) {
      59           5 :     _routes = List.filled(_page.paths.length, null);
      60             :   }
      61           1 :   @override
      62             :   Page createPage() {
      63           1 :     return MaterialPage<void>(
      64           1 :       child: _IndexedPageStateProvider(
      65             :         pageState: this,
      66           2 :         child: _page.child,
      67             :       ),
      68           2 :       key: ValueKey(routeInfo),
      69             :     );
      70             :   }
      71             : }
      72             : 
      73             : class TabPage extends StatefulPage<void> with IndexedRouteMixIn {
      74             :   // TODO: This should probably take a page and not a widget
      75             :   final Widget child;
      76             : 
      77             :   @override
      78             :   final List<String> paths;
      79             : 
      80           2 :   TabPage({
      81             :     required this.child,
      82             :     required this.paths,
      83             :   });
      84             : 
      85           2 :   @override
      86             :   PageState createState(Routemaster delegate, RouteInfo routeInfo) {
      87           2 :     return TabPageState(this, delegate, routeInfo);
      88             :   }
      89             : 
      90           2 :   static TabPageState of(BuildContext context) {
      91             :     return context
      92           2 :         .dependOnInheritedWidgetOfExactType<_TabPageStateProvider>()!
      93           2 :         .pageState;
      94             :   }
      95             : }
      96             : 
      97             : class _TabPageStateProvider extends InheritedNotifier {
      98             :   final TabPageState pageState;
      99             : 
     100           2 :   _TabPageStateProvider({
     101             :     required Widget child,
     102             :     required this.pageState,
     103           2 :   }) : super(
     104             :           child: child,
     105           2 :           notifier: pageState._delegate,
     106             :         );
     107             : 
     108           1 :   @override
     109             :   bool updateShouldNotify(covariant _TabPageStateProvider oldWidget) {
     110           3 :     return pageState != oldWidget.pageState;
     111             :   }
     112             : }
     113             : 
     114             : class TabPageState extends PageState
     115             :     with ChangeNotifier, IndexedPageStateMixIn {
     116             :   @override
     117             :   final TabPage _page;
     118             : 
     119             :   @override
     120             :   final Routemaster _delegate;
     121             : 
     122             :   @override
     123             :   final RouteInfo routeInfo;
     124             : 
     125           2 :   TabPageState(this._page, this._delegate, this.routeInfo) {
     126          10 :     _routes = List.filled(_page.paths.length, null);
     127             :   }
     128             : 
     129           2 :   @override
     130             :   set index(int value) {
     131           2 :     if (_tabController != null) {
     132           2 :       _tabController!.index = value;
     133             :     }
     134             : 
     135           2 :     super.index = value;
     136             :   }
     137             : 
     138           2 :   @override
     139             :   Page createPage() {
     140           2 :     return MaterialPage<void>(
     141           4 :       key: ValueKey(routeInfo),
     142           2 :       child: _TabControllerProvider(
     143             :         pageState: this,
     144           2 :         child: _TabPageStateProvider(
     145             :           pageState: this,
     146           8 :           child: Builder(builder: (_) => _page.child),
     147             :         ),
     148             :       ),
     149             :     );
     150             :   }
     151             : 
     152             :   TabController? _tabController;
     153           2 :   TabController get tabController => _tabController!;
     154             : }
     155             : 
     156             : /// Creates a [TabController] for [TabPageState]
     157             : class _TabControllerProvider extends StatefulWidget {
     158             :   final Widget child;
     159             :   final TabPageState pageState;
     160             : 
     161           2 :   _TabControllerProvider({
     162             :     required this.child,
     163             :     required this.pageState,
     164             :   });
     165             : 
     166           2 :   @override
     167           2 :   _TabControllerProviderState createState() => _TabControllerProviderState();
     168             : }
     169             : 
     170             : class _TabControllerProviderState extends State<_TabControllerProvider>
     171             :     with SingleTickerProviderStateMixin {
     172           2 :   @override
     173             :   void initState() {
     174           2 :     super.initState();
     175             : 
     176           2 :     final tabController = TabController(
     177           8 :       length: widget.pageState._routes.length,
     178           6 :       initialIndex: widget.pageState.index,
     179             :       vsync: this,
     180             :     );
     181             : 
     182           3 :     tabController.addListener(() {
     183           4 :       widget.pageState.index = tabController.index;
     184             :     });
     185             : 
     186           6 :     widget.pageState._tabController = tabController;
     187             :   }
     188             : 
     189           2 :   @override
     190             :   void dispose() {
     191           8 :     widget.pageState._tabController?.dispose();
     192           6 :     widget.pageState._tabController = null;
     193           2 :     super.dispose();
     194             :   }
     195             : 
     196           2 :   @override
     197           4 :   Widget build(BuildContext context) => widget.child;
     198             : }
     199             : 
     200             : class CupertinoTabPage extends StatefulPage<void> with IndexedRouteMixIn {
     201             :   final Widget child;
     202             : 
     203             :   @override
     204             :   final List<String> paths;
     205             : 
     206           1 :   CupertinoTabPage({
     207             :     required this.child,
     208             :     required this.paths,
     209             :   });
     210             : 
     211           1 :   @override
     212             :   PageState createState(Routemaster delegate, RouteInfo routeInfo) {
     213           1 :     return CupertinoTabPageState(this, delegate, routeInfo);
     214             :   }
     215             : 
     216           1 :   static CupertinoTabPageState of(BuildContext context) {
     217             :     return context
     218           1 :         .dependOnInheritedWidgetOfExactType<_CupertinoTabPageStateProvider>()!
     219           1 :         .pageState;
     220             :   }
     221             : }
     222             : 
     223             : class _CupertinoTabPageStateProvider extends InheritedNotifier {
     224             :   final CupertinoTabPageState pageState;
     225             : 
     226           1 :   _CupertinoTabPageStateProvider({
     227             :     required Widget child,
     228             :     required this.pageState,
     229           1 :   }) : super(
     230             :           child: child,
     231           1 :           notifier: pageState._delegate,
     232             :         );
     233             : 
     234           1 :   @override
     235             :   bool updateShouldNotify(covariant _CupertinoTabPageStateProvider oldWidget) {
     236           3 :     return pageState != oldWidget.pageState;
     237             :   }
     238             : }
     239             : 
     240             : class CupertinoTabPageState extends PageState
     241             :     with ChangeNotifier, IndexedPageStateMixIn {
     242             :   @override
     243             :   final CupertinoTabPage _page;
     244             : 
     245             :   @override
     246             :   final Routemaster _delegate;
     247             : 
     248             :   @override
     249             :   final RouteInfo routeInfo;
     250             : 
     251             :   final CupertinoTabController tabController = CupertinoTabController();
     252             : 
     253           1 :   CupertinoTabPageState(
     254             :     this._page,
     255             :     this._delegate,
     256             :     this.routeInfo,
     257             :   ) {
     258           5 :     _routes = List.filled(_page.paths.length, null);
     259             : 
     260           2 :     addListener(() {
     261           4 :       if (index != tabController.index) {
     262           3 :         tabController.index = index;
     263             :       }
     264             :     });
     265             : 
     266           3 :     tabController.addListener(() {
     267           3 :       index = tabController.index;
     268             :     });
     269             :   }
     270             : 
     271           1 :   @override
     272             :   Page createPage() {
     273           1 :     return MaterialPage<void>(
     274           1 :       child: _CupertinoTabPageStateProvider(
     275             :         pageState: this,
     276           2 :         child: _page.child,
     277             :       ),
     278           2 :       key: ValueKey(routeInfo),
     279             :     );
     280             :   }
     281             : 
     282           1 :   Widget tabBuilder(BuildContext context, int index) {
     283           1 :     final stack = _getStackForIndex(index);
     284           1 :     final pages = stack.createPages();
     285             : 
     286           1 :     assert(pages.isNotEmpty, 'Pages must not be empty');
     287             : 
     288           1 :     return Navigator(
     289           1 :       key: stack.navigatorKey,
     290           1 :       onPopPage: stack.onPopPage,
     291             :       pages: pages,
     292             :     );
     293             :   }
     294             : }
     295             : 
     296             : mixin IndexedRouteMixIn<T> on Page<T> {
     297             :   List<String> get paths;
     298             : }
     299             : 
     300             : mixin IndexedPageStateMixIn on PageWrapper, ChangeNotifier {
     301             :   late List<StackPageState?> _routes;
     302             : 
     303             :   Routemaster get _delegate;
     304             : 
     305             :   @override
     306             :   RouteInfo get routeInfo;
     307             : 
     308             :   IndexedRouteMixIn get _page;
     309             : 
     310             :   StackList? _stacks;
     311           6 :   StackList get stacks => _stacks ??= StackList(this);
     312             : 
     313           3 :   StackPageState get currentStack => _getStackForIndex(index);
     314             : 
     315             :   int _index = 0;
     316           8 :   int get index => _index;
     317           4 :   set index(int value) {
     318           8 :     if (value != _index) {
     319           3 :       _index = value;
     320             : 
     321           3 :       notifyListeners();
     322           6 :       _delegate._markNeedsUpdate();
     323             :     }
     324             :   }
     325             : 
     326           4 :   StackPageState _getStackForIndex(int index) {
     327           8 :     if (_routes[index] == null) {
     328          12 :       _routes[index] = _createInitialStackState(index);
     329             :     }
     330             : 
     331           8 :     return _routes[index]!;
     332             :   }
     333             : 
     334           4 :   StackPageState? _createInitialStackState(int index) {
     335          24 :     final path = join(routeInfo.path, _page.paths[index]);
     336           8 :     final route = _delegate._getPageWrapper(path);
     337          12 :     return StackPageState(delegate: _delegate, routes: [route]);
     338             :   }
     339             : 
     340             :   /// Attempts to handle a list of child pages.
     341             :   ///
     342             :   /// Checks if the first route matches one of the child paths of this tab page.
     343             :   /// If it does, it sets that stack's pages to the routes, and switches the
     344             :   /// current index to that tab.
     345           4 :   @override
     346             :   bool maybeSetChildPages(Iterable<PageWrapper> pages) {
     347             :     assert(
     348           4 :       pages.isNotEmpty,
     349             :       "Don't call maybeSetPageStates with an empty list",
     350             :     );
     351             : 
     352           8 :     final tabPagePath = routeInfo.path;
     353          12 :     final subPagePath = pages.first.routeInfo.path;
     354             : 
     355           4 :     if (!isWithin(tabPagePath, subPagePath)) {
     356             :       // subPagePath is not a path beneath the tab page's path.
     357             :       return false;
     358             :     }
     359             : 
     360           8 :     final index = _getIndexForPath(_stripPath(tabPagePath, subPagePath));
     361             :     if (index == null) {
     362             :       // First route didn't match any of our child paths, so this isn't a route
     363             :       // that we can handle.
     364             :       return false;
     365             :     }
     366             : 
     367             :     // Handle route
     368          12 :     _getStackForIndex(index).maybeSetChildPages(pages.toList());
     369           4 :     this.index = index;
     370             :     return true;
     371             :   }
     372             : 
     373           4 :   int? _getIndexForPath(String path) {
     374             :     var i = 0;
     375          12 :     for (final initialPath in _page.paths) {
     376           4 :       if (path.startsWith(initialPath)) {
     377             :         return i;
     378             :       }
     379           1 :       i++;
     380             :     }
     381             : 
     382             :     return null;
     383             :   }
     384             : 
     385           4 :   static String _stripPath(String parent, String child) {
     386           4 :     final splitParent = split(parent);
     387           4 :     final splitChild = split(child);
     388          12 :     return joinAll(splitChild.skip(splitParent.length));
     389             :   }
     390             : 
     391             :   // @override
     392             :   // bool maybePush(PageState route) {
     393             :   //   final index = _getIndexForPath(route.routeInfo.path);
     394             :   //   if (index == null) {
     395             :   //     return false;
     396             :   //   }
     397             : 
     398             :   //   return _getStackForIndex(index).maybePush(route);
     399             :   // }
     400             : 
     401           2 :   @override
     402             :   Future<bool> maybePop() {
     403           6 :     return _getStackForIndex(index).maybePop();
     404             :   }
     405             : 
     406             :   @override
     407             :   Iterable<PageWrapper> getCurrentPages() sync* {
     408             :     yield this;
     409             :     yield* _getStackForIndex(index)._getCurrentPages();
     410             :   }
     411             : }
     412             : 
     413             : class StackList {
     414             :   final IndexedPageStateMixIn _indexedPageState;
     415             : 
     416           2 :   StackList(this._indexedPageState);
     417             : 
     418           2 :   StackPageState operator [](int index) =>
     419           4 :       _indexedPageState._getStackForIndex(index);
     420             : }

Generated by: LCOV version 1.15