LCOV - code coverage report
Current view: top level - src/widgets - tabs_navigation_builder.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 1 98 1.0 %
Date: 2022-10-22 16:29:45 Functions: 0 0 -

          Line data    Source code
       1             : import 'package:flutter/gestures.dart';
       2             : import 'package:flutter/material.dart';
       3             : import 'package:flutter/services.dart';
       4             : 
       5             : import '../destination.dart';
       6             : import '../navigation_controller.dart';
       7             : import 'index.dart';
       8             : 
       9             : /// A [NavigatorBuilder] that allows to switch between destinations using
      10             : /// [TabBar].
      11             : ///
      12             : /// It builds a wrapper widget, which is a [Scaffold] with a [Scaffold.appbar] set
      13             : /// to the [TabBar] with provided [tabs], and a [Scaffold.body] set to the [TabBarView],
      14             : /// which display a content of corresponding destination.
      15             : ///
      16             : /// The [tabs] must correspond to the navigator's destinations.
      17             : ///
      18             : /// The tab bar can be customized using [parameters], which includes all parameters
      19             : /// supported by the [TabBar] widget.
      20             : ///
      21             : /// See also:
      22             : /// - [NavigatorBuilder]
      23             : /// - [AppBarParameters]
      24             : /// - [TabBarParameters]
      25             : /// - [NavigationController]
      26             : /// - [TabBar]
      27             : ///
      28             : class TabsNavigationBuilder implements NavigatorBuilder {
      29             :   /// Creates a [TabsNavigationBuilder] instance.
      30             :   ///
      31           0 :   const TabsNavigationBuilder({
      32             :     required this.tabs,
      33             :     this.parameters = const TabBarParameters(),
      34             :     this.appBarParametersBuilder,
      35             :     this.wrapInScaffold = true,
      36             :   });
      37             : 
      38             :   /// Typically a list of [Tab] widgets, that corresponds to the navigator's
      39             :   /// destination list.
      40             :   ///
      41             :   /// The list must contain the same number of widgets, following with the same order
      42             :   /// as a destination list specified for the navigator.
      43             :   ///
      44             :   final List<Widget> tabs;
      45             : 
      46             :   /// A set of [TabBar] parameters.
      47             :   ///
      48             :   /// Contains all supported parameters to customize [TabBar] widget.
      49             :   /// Doesn't include 'tabs', 'onTap' and 'controller', which are managed by
      50             :   /// [TabsNavigationBuilder].
      51             :   ///
      52             :   final TabBarParameters parameters;
      53             : 
      54             :   /// Return an instance of [AppBarParameters] for provided destination.
      55             :   ///
      56             :   /// Once this builder is specified, the navigation [TabBar] will appear as part of
      57             :   /// [AppBar] widget.
      58             :   /// When this function is called, the [destination] parameter is set to current
      59             :   /// destination (selected tab).
      60             :   ///
      61             :   /// The function should return an instance of [AppBarParameters],
      62             :   /// which is a set of all parameters available in the [AppBar] widget.
      63             :   /// So the app bar widget con be made to match the current destination.
      64             :   /// For example, you can set a title and actions, depending on the current destination.
      65             :   ///
      66             :   final AppBarParameters Function(BuildContext context, Destination destination)?
      67             :       appBarParametersBuilder;
      68             : 
      69             :   /// Controls if the [Scaffold] widget should be used around the tab bar and tab's content.
      70             :   ///
      71             :   /// This might be needed if you are using tabs as a top level navigation in your app.
      72             :   /// Defaults to 'true'.
      73             :   ///
      74             :   final bool wrapInScaffold;
      75             : 
      76           0 :   @override
      77             :   Widget build(BuildContext context, NavigationController navigator) {
      78           0 :     final currentDestination = navigator.currentDestination;
      79           0 :     return _TabsNavigationWrapper(
      80           0 :       tabs: tabs,
      81             :       // TODO: This implementation doesn't respect the possible parameters of destinations (excluding current destination).
      82             :       // How this could be resolved?
      83           0 :       tabContentBuilder: (tabIndex) =>
      84           0 :           navigator.destinations[tabIndex].build(context),
      85           0 :       parameters: parameters,
      86           0 :       onTabSelected: (index) => navigator.goTo(navigator.destinations[index]),
      87           0 :       selectedIndex: navigator.destinations.indexOf(currentDestination),
      88           0 :       appBarParameters: appBarParametersBuilder?.call(context, currentDestination),
      89           0 :       wrapInScaffold: wrapInScaffold,
      90             :     );
      91             :   }
      92             : }
      93             : 
      94             : class _TabsNavigationWrapper extends StatefulWidget {
      95           0 :   const _TabsNavigationWrapper({
      96             :     Key? key,
      97             :     required this.tabs,
      98             :     required this.tabContentBuilder,
      99             :     required this.onTabSelected,
     100             :     required this.selectedIndex,
     101             :     required this.parameters,
     102             :     this.appBarParameters,
     103             :     this.wrapInScaffold = false,
     104           0 :   }) : super(key: key);
     105             : 
     106             :   final List<Widget> tabs;
     107             : 
     108             :   final Widget Function(int tabIndex) tabContentBuilder;
     109             : 
     110             :   final void Function(int index) onTabSelected;
     111             : 
     112             :   final int selectedIndex;
     113             : 
     114             :   final TabBarParameters parameters;
     115             : 
     116             :   final AppBarParameters? appBarParameters;
     117             : 
     118             :   final bool wrapInScaffold;
     119             : 
     120           0 :   @override
     121           0 :   _TabsNavigationWrapperState createState() => _TabsNavigationWrapperState();
     122             : }
     123             : 
     124             : class _TabsNavigationWrapperState extends State<_TabsNavigationWrapper>
     125             :     with TickerProviderStateMixin {
     126             :   late final TabController _controller;
     127             : 
     128           0 :   @override
     129             :   void initState() {
     130           0 :     super.initState();
     131           0 :     _controller = TabController(length: widget.tabs.length, vsync: this);
     132           0 :     _controller.addListener(_onTabChanged);
     133           0 :     _controller.animateTo(widget.selectedIndex);
     134             :   }
     135             : 
     136           0 :   @override
     137             :   void didUpdateWidget(_TabsNavigationWrapper oldWidget) {
     138           0 :     super.didUpdateWidget(oldWidget);
     139           0 :     if (oldWidget.selectedIndex != widget.selectedIndex) {
     140           0 :       _controller.animateTo(widget.selectedIndex);
     141             :     }
     142             :   }
     143             : 
     144           0 :   @override
     145             :   void dispose() {
     146           0 :     _controller.dispose();
     147           0 :     super.dispose();
     148             :   }
     149             : 
     150           0 :   @override
     151             :   Widget build(BuildContext context) {
     152           0 :     final tabBar = TabBar(
     153           0 :       controller: _controller,
     154           0 :       tabs: widget.tabs,
     155           0 :       onTap: (value) => widget.onTabSelected(value),
     156           0 :       isScrollable: widget.parameters.isScrollable,
     157           0 :       padding: widget.parameters.padding,
     158           0 :       indicatorColor: widget.parameters.indicatorColor,
     159             :       automaticIndicatorColorAdjustment:
     160           0 :           widget.parameters.automaticIndicatorColorAdjustment,
     161           0 :       indicatorWeight: widget.parameters.indicatorWeight,
     162           0 :       indicatorPadding: widget.parameters.indicatorPadding,
     163           0 :       indicator: widget.parameters.indicator,
     164           0 :       indicatorSize: widget.parameters.indicatorSize,
     165           0 :       labelColor: widget.parameters.labelColor,
     166           0 :       labelStyle: widget.parameters.labelStyle,
     167           0 :       labelPadding: widget.parameters.labelPadding,
     168           0 :       unselectedLabelColor: widget.parameters.unselectedLabelColor,
     169           0 :       unselectedLabelStyle: widget.parameters.unselectedLabelStyle,
     170           0 :       dragStartBehavior: widget.parameters.dragStartBehavior,
     171           0 :       overlayColor: widget.parameters.overlayColor,
     172           0 :       mouseCursor: widget.parameters.mouseCursor,
     173           0 :       enableFeedback: widget.parameters.enableFeedback,
     174           0 :       physics: widget.parameters.physics,
     175           0 :       splashFactory: widget.parameters.splashFactory,
     176           0 :       splashBorderRadius: widget.parameters.splashBorderRadius,
     177             :     );
     178           0 :     final tabBarView = TabBarView(
     179           0 :       controller: _controller,
     180           0 :       children: List<Widget>.generate(
     181           0 :           widget.tabs.length, (index) => widget.tabContentBuilder(index)),
     182             :     );
     183             :     Widget result;
     184           0 :     if (widget.appBarParameters != null) {
     185           0 :       final appBar = AppBar(
     186             :         bottom: tabBar,
     187           0 :         leading: widget.appBarParameters?.leading,
     188             :         automaticallyImplyLeading:
     189           0 :             widget.appBarParameters?.automaticallyImplyLeading ?? true,
     190           0 :         title: widget.appBarParameters?.title,
     191           0 :         actions: widget.appBarParameters?.actions,
     192           0 :         flexibleSpace: widget.appBarParameters?.flexibleSpace,
     193           0 :         elevation: widget.appBarParameters?.elevation,
     194           0 :         scrolledUnderElevation: widget.appBarParameters?.scrolledUnderElevation,
     195           0 :         shadowColor: widget.appBarParameters?.shadowColor,
     196           0 :         surfaceTintColor: widget.appBarParameters?.surfaceTintColor,
     197           0 :         shape: widget.appBarParameters?.shape,
     198           0 :         backgroundColor: widget.appBarParameters?.backgroundColor,
     199           0 :         foregroundColor: widget.appBarParameters?.foregroundColor,
     200           0 :         iconTheme: widget.appBarParameters?.iconTheme,
     201           0 :         actionsIconTheme: widget.appBarParameters?.actionsIconTheme,
     202           0 :         primary: widget.appBarParameters?.primary ?? true,
     203           0 :         centerTitle: widget.appBarParameters?.centerTitle,
     204             :         excludeHeaderSemantics:
     205           0 :             widget.appBarParameters?.excludeHeaderSemantics ?? false,
     206           0 :         titleSpacing: widget.appBarParameters?.titleSpacing,
     207           0 :         toolbarOpacity: widget.appBarParameters?.toolbarOpacity ?? 1.0,
     208           0 :         bottomOpacity: widget.appBarParameters?.bottomOpacity ?? 1.0,
     209           0 :         toolbarHeight: widget.appBarParameters?.toolbarHeight,
     210           0 :         leadingWidth: widget.appBarParameters?.leadingWidth,
     211           0 :         toolbarTextStyle: widget.appBarParameters?.toolbarTextStyle,
     212           0 :         titleTextStyle: widget.appBarParameters?.titleTextStyle,
     213           0 :         systemOverlayStyle: widget.appBarParameters?.systemOverlayStyle,
     214             :       );
     215           0 :       if (widget.wrapInScaffold) {
     216           0 :         result = Scaffold(
     217             :           appBar: appBar,
     218             :           body: tabBarView,
     219             :         );
     220             :       } else {
     221           0 :         result = Column(
     222           0 :           children: [
     223             :             appBar,
     224           0 :             Expanded(
     225             :               child: tabBarView,
     226             :             ),
     227             :           ],
     228             :         );
     229             :       }
     230             :     } else {
     231           0 :       result = Column(
     232           0 :         children: [
     233             :           tabBar,
     234           0 :           Expanded(
     235             :             child: tabBarView,
     236             :           ),
     237             :         ],
     238             :       );
     239           0 :       if (widget.wrapInScaffold) {
     240           0 :         result = Scaffold(
     241             :           body: result,
     242             :         );
     243             :       }
     244             :     }
     245             :     return result;
     246             :   }
     247             : 
     248           0 :   void _onTabChanged() {
     249           0 :     widget.onTabSelected(_controller.index);
     250             :   }
     251             : }
     252             : 
     253             : /// Contains parameters to customize the [TabBar].
     254             : ///
     255             : /// It includes all the same arguments as the [TabBar()], excepting
     256             : /// the 'tabs', 'onTap' and 'controller', which are managed by the [TabsNavigationBuilder].
     257             : ///
     258             : /// See also:
     259             : /// - [TabsNavigationBuilder]
     260             : /// - [TabBar]
     261             : ///
     262             : class TabBarParameters {
     263             :   /// Create a [TabBarParameters] instance.
     264             :   ///
     265           9 :   const TabBarParameters({
     266             :     this.isScrollable = false,
     267             :     this.padding,
     268             :     this.indicatorColor,
     269             :     this.automaticIndicatorColorAdjustment = true,
     270             :     this.indicatorWeight = 2.0,
     271             :     this.indicatorPadding = EdgeInsets.zero,
     272             :     this.indicator,
     273             :     this.indicatorSize,
     274             :     this.labelColor,
     275             :     this.labelStyle,
     276             :     this.labelPadding,
     277             :     this.unselectedLabelColor,
     278             :     this.unselectedLabelStyle,
     279             :     this.dragStartBehavior = DragStartBehavior.start,
     280             :     this.overlayColor,
     281             :     this.mouseCursor,
     282             :     this.enableFeedback,
     283             :     this.physics,
     284             :     this.splashFactory,
     285             :     this.splashBorderRadius,
     286             :   });
     287             : 
     288             :   /// [TabBar.isScrollable]
     289             :   ///
     290             :   final bool isScrollable;
     291             : 
     292             :   /// [TabBar.padding]
     293             :   ///
     294             :   final EdgeInsetsGeometry? padding;
     295             : 
     296             :   /// [TabBar.indicatorColor]
     297             :   ///
     298             :   final Color? indicatorColor;
     299             : 
     300             :   /// [TabBar.automaticIndicatorColorAdjustment]
     301             :   ///
     302             :   final bool automaticIndicatorColorAdjustment;
     303             : 
     304             :   /// [TabBar.indicatorWeight]
     305             :   ///
     306             :   final double indicatorWeight;
     307             : 
     308             :   /// [TabBar.indicatorPadding]
     309             :   ///
     310             :   final EdgeInsetsGeometry indicatorPadding;
     311             : 
     312             :   /// [TabBar.indicator]
     313             :   ///
     314             :   final Decoration? indicator;
     315             : 
     316             :   /// [TabBar.indicatorSize]
     317             :   ///
     318             :   final TabBarIndicatorSize? indicatorSize;
     319             : 
     320             :   /// [TabBar.labelColor]
     321             :   ///
     322             :   final Color? labelColor;
     323             : 
     324             :   /// [TabBar.labelStyle]
     325             :   ///
     326             :   final TextStyle? labelStyle;
     327             : 
     328             :   /// [TabBar.labelPadding]
     329             :   ///
     330             :   final EdgeInsetsGeometry? labelPadding;
     331             : 
     332             :   /// [TabBar.unselectedLabelColor]
     333             :   ///
     334             :   final Color? unselectedLabelColor;
     335             : 
     336             :   /// [TabBar.unselectedLabelStyle]
     337             :   ///
     338             :   final TextStyle? unselectedLabelStyle;
     339             : 
     340             :   /// [TabBar.dragStartBehavior]
     341             :   ///
     342             :   final DragStartBehavior dragStartBehavior;
     343             : 
     344             :   /// [TabBar.overlayColor]
     345             :   ///
     346             :   final MaterialStateProperty<Color?>? overlayColor;
     347             : 
     348             :   /// [TabBar.mouseCursor]
     349             :   ///
     350             :   final MouseCursor? mouseCursor;
     351             : 
     352             :   /// [TabBar.enableFeedback]
     353             :   ///
     354             :   final bool? enableFeedback;
     355             : 
     356             :   /// [TabBar.physics]
     357             :   ///
     358             :   final ScrollPhysics? physics;
     359             : 
     360             :   /// [TabBar.splashFactory]
     361             :   ///
     362             :   final InteractiveInkFeatureFactory? splashFactory;
     363             : 
     364             :   /// [TabBar.splashBorderRadius]
     365             :   ///
     366             :   final BorderRadius? splashBorderRadius;
     367             : }
     368             : 
     369             : /// Contains parameters to customize the [AppBar].
     370             : ///
     371             : /// It includes all the same arguments as the [AppBar()], excepting
     372             : /// the 'bottom' which is managed by the [TabsNavigationBuilder].
     373             : ///
     374             : /// See also:
     375             : /// - [TabsNavigationBuilder]
     376             : /// - [AppBar]
     377             : ///
     378             : class AppBarParameters {
     379             :   /// Create a [AppBarParameters] instance.
     380             :   ///
     381           0 :   const AppBarParameters({
     382             :     this.leading,
     383             :     this.automaticallyImplyLeading = true,
     384             :     this.title,
     385             :     this.actions,
     386             :     this.flexibleSpace,
     387             :     this.elevation,
     388             :     this.scrolledUnderElevation,
     389             :     this.shadowColor,
     390             :     this.surfaceTintColor,
     391             :     this.shape,
     392             :     this.backgroundColor,
     393             :     this.foregroundColor,
     394             :     this.iconTheme,
     395             :     this.actionsIconTheme,
     396             :     this.primary = true,
     397             :     this.centerTitle,
     398             :     this.excludeHeaderSemantics = false,
     399             :     this.titleSpacing,
     400             :     this.toolbarOpacity = 1.0,
     401             :     this.bottomOpacity = 1.0,
     402             :     this.toolbarHeight,
     403             :     this.leadingWidth,
     404             :     this.toolbarTextStyle,
     405             :     this.titleTextStyle,
     406             :     this.systemOverlayStyle,
     407             :   });
     408             : 
     409             :   /// [AppBar.leading]
     410             :   ///
     411             :   final Widget? leading;
     412             : 
     413             :   /// [AppBar.leading]
     414             :   ///
     415             :   final bool automaticallyImplyLeading;
     416             : 
     417             :   /// [AppBar.title]
     418             :   ///
     419             :   final Widget? title;
     420             : 
     421             :   /// [AppBar.actions]
     422             :   ///
     423             :   final List<Widget>? actions;
     424             : 
     425             :   /// [AppBar.flexibleSpace]
     426             :   ///
     427             :   final Widget? flexibleSpace;
     428             : 
     429             :   /// [AppBar.elevation]
     430             :   ///
     431             :   final double? elevation;
     432             : 
     433             :   /// [AppBar.scrolledUnderElevation]
     434             :   ///
     435             :   final double? scrolledUnderElevation;
     436             : 
     437             :   /// [AppBar.shadowColor]
     438             :   ///
     439             :   final Color? shadowColor;
     440             : 
     441             :   /// [AppBar.surfaceTintColor]
     442             :   ///
     443             :   final Color? surfaceTintColor;
     444             : 
     445             :   /// [AppBar.shape]
     446             :   ///
     447             :   final ShapeBorder? shape;
     448             : 
     449             :   /// [AppBar.backgroundColor]
     450             :   ///
     451             :   final Color? backgroundColor;
     452             : 
     453             :   /// [AppBar.foregroundColor]
     454             :   ///
     455             :   final Color? foregroundColor;
     456             : 
     457             :   /// [AppBar.iconTheme]
     458             :   ///
     459             :   final IconThemeData? iconTheme;
     460             : 
     461             :   /// [AppBar.actionsIconTheme]
     462             :   ///
     463             :   final IconThemeData? actionsIconTheme;
     464             : 
     465             :   /// [AppBar.primary]
     466             :   ///
     467             :   final bool primary;
     468             : 
     469             :   /// [AppBar.centerTitle]
     470             :   ///
     471             :   final bool? centerTitle;
     472             : 
     473             :   /// [AppBar.excludeHeaderSemantics]
     474             :   ///
     475             :   final bool excludeHeaderSemantics;
     476             : 
     477             :   /// [AppBar.titleSpacing]
     478             :   ///
     479             :   final double? titleSpacing;
     480             : 
     481             :   /// [AppBar.toolbarOpacity]
     482             :   ///
     483             :   final double toolbarOpacity;
     484             : 
     485             :   /// [AppBar.bottomOpacity]
     486             :   ///
     487             :   final double bottomOpacity;
     488             : 
     489             :   /// [AppBar.toolbarHeight]
     490             :   ///
     491             :   final double? toolbarHeight;
     492             : 
     493             :   /// [AppBar.leadingWidth]
     494             :   ///
     495             :   final double? leadingWidth;
     496             : 
     497             :   /// [AppBar.toolbarTextStyle]
     498             :   ///
     499             :   final TextStyle? toolbarTextStyle;
     500             : 
     501             :   /// [AppBar.titleTextStyle]
     502             :   ///
     503             :   final TextStyle? titleTextStyle;
     504             : 
     505             :   /// [AppBar.systemOverlayStyle]
     506             :   ///
     507             :   final SystemUiOverlayStyle? systemOverlayStyle;
     508             : }

Generated by: LCOV version