From 03d852c9fee7abfacf777f8c56575c329afa11f3 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka <45696119+bdlukaa@users.noreply.github.com> Date: Fri, 25 Mar 2022 16:59:44 -0300 Subject: [PATCH 01/16] Do not display indicator builder on pane --- .../navigation/navigation_view/pane.dart | 277 ++++++++---------- 1 file changed, 130 insertions(+), 147 deletions(-) diff --git a/lib/src/controls/navigation/navigation_view/pane.dart b/lib/src/controls/navigation/navigation_view/pane.dart index a056afae6..f8fec24b4 100644 --- a/lib/src/controls/navigation/navigation_view/pane.dart +++ b/lib/src/controls/navigation/navigation_view/pane.dart @@ -364,54 +364,48 @@ class _TopNavigationPane extends StatelessWidget { assert(debugCheckHasFluentTheme(context)); Widget topBar = SizedBox( height: pane.size?.topHeight ?? kOneLineTileHeight, - child: pane.indicatorBuilder( - context: context, - pane: pane, - axis: Axis.vertical, - child: Row(key: pane.paneKey, children: [ - Expanded( - child: Row(children: [ - if (appBar != null) - NavigationAppBar.buildLeading(context, appBar!), - if (pane.header != null) - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 6.0, - ), - child: pane.header!, - ), - // TODO: A listview shouldn't be used here. Instead, if there are - // more items than space, show a dropdown button with the other - // items - Expanded( - child: SingleChildScrollView( - key: listKey, - primary: true, - scrollDirection: Axis.horizontal, - child: Row( - children: pane.items.map((item) { - return _buildItem(context, item); - }).toList(), - ), + child: Row(key: pane.paneKey, children: [ + Expanded( + child: Row(children: [ + if (appBar != null) NavigationAppBar.buildLeading(context, appBar!), + if (pane.header != null) + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, ), + child: pane.header!, ), - ]), - ), - if (pane.autoSuggestBox != null) - Container( - margin: const EdgeInsets.only(left: 30.0), - constraints: const BoxConstraints( - minWidth: 100.0, - maxWidth: 215.0, + // TODO: A listview shouldn't be used here. Instead, if there are + // more items than space, show a dropdown button with the other + // items + Expanded( + child: SingleChildScrollView( + key: listKey, + primary: true, + scrollDirection: Axis.horizontal, + child: Row( + children: pane.items.map((item) { + return _buildItem(context, item); + }).toList(), + ), ), - child: pane.autoSuggestBox!, ), - ...pane.footerItems.map((item) { - return _buildItem(context, item); - }), - ]), - ), + ]), + ), + if (pane.autoSuggestBox != null) + Container( + margin: const EdgeInsets.only(left: 30.0), + constraints: const BoxConstraints( + minWidth: 100.0, + maxWidth: 215.0, + ), + child: pane.autoSuggestBox!, + ), + ...pane.footerItems.map((item) { + return _buildItem(context, item); + }), + ]), ); return topBar; } @@ -467,61 +461,55 @@ class _CompactNavigationPane extends StatelessWidget { duration: theme.animationDuration ?? Duration.zero, curve: theme.animationCurve ?? Curves.linear, width: pane.size?.compactWidth ?? _kCompactNavigationPanelWidth, - child: pane.indicatorBuilder( - context: context, - pane: pane, - axis: Axis.horizontal, - child: Align( - key: pane.paneKey, - alignment: Alignment.topCenter, - child: - Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - () { - if (pane.menuButton != null) return pane.menuButton!; - if (onToggle != null) { - return NavigationPane.buildMenuButton( - context, - Text(FluentLocalizations.of(context).openNavigationTooltip), - pane, - onPressed: () { - onToggle?.call(); - }, - padding: showReplacement ? EdgeInsets.zero : topPadding, - ); - } - return const SizedBox.shrink(); - }(), - if (showReplacement) - Padding( - padding: topPadding, - child: PaneItem( - title: Text(FluentLocalizations.of(context).clickToSearch), - icon: pane.autoSuggestBoxReplacement!, - ).build( - context, - false, - () { - onToggle?.call(); - }, - ), - ), - Expanded( - child: Scrollbar( - key: scrollbarKey, - controller: pane.scrollController, - isAlwaysShown: false, - child: ListView(key: listKey, primary: true, children: [ - ...pane.items.map((item) { - return _buildItem(context, item); - }), - ]), + child: Align( + key: pane.paneKey, + alignment: Alignment.topCenter, + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + () { + if (pane.menuButton != null) return pane.menuButton!; + if (onToggle != null) { + return NavigationPane.buildMenuButton( + context, + Text(FluentLocalizations.of(context).openNavigationTooltip), + pane, + onPressed: () { + onToggle?.call(); + }, + padding: showReplacement ? EdgeInsets.zero : topPadding, + ); + } + return const SizedBox.shrink(); + }(), + if (showReplacement) + Padding( + padding: topPadding, + child: PaneItem( + title: Text(FluentLocalizations.of(context).clickToSearch), + icon: pane.autoSuggestBoxReplacement!, + ).build( + context, + false, + () { + onToggle?.call(); + }, ), ), - ...pane.footerItems.map((item) { - return _buildItem(context, item); - }), - ]), - ), + Expanded( + child: Scrollbar( + key: scrollbarKey, + controller: pane.scrollController, + isAlwaysShown: false, + child: ListView(key: listKey, primary: true, children: [ + ...pane.items.map((item) { + return _buildItem(context, item); + }), + ]), + ), + ), + ...pane.footerItems.map((item) { + return _buildItem(context, item); + }), + ]), ), ); } @@ -640,59 +628,54 @@ class _OpenNavigationPaneState extends State<_OpenNavigationPane> duration: Duration.zero, curve: Curves.linear, width: paneWidth, - child: widget.pane.indicatorBuilder( - context: context, - pane: widget.pane, - axis: Axis.horizontal, - child: Column(key: widget.pane.paneKey, children: [ - Container( - margin: widget.pane.autoSuggestBox != null - ? EdgeInsets.zero - : topPadding, - height: kOneLineTileHeight, - child: () { - if (widget.pane.header != null) { - return Row(children: [ - menuButton, - Expanded( - child: Align( - child: widget.pane.header!, - alignment: Alignment.centerLeft, - ), + child: Column(key: widget.pane.paneKey, children: [ + Container( + margin: widget.pane.autoSuggestBox != null + ? EdgeInsets.zero + : topPadding, + height: kOneLineTileHeight, + child: () { + if (widget.pane.header != null) { + return Row(children: [ + menuButton, + Expanded( + child: Align( + child: widget.pane.header!, + alignment: Alignment.centerLeft, ), - ]); - } else { - return menuButton; - } - }(), + ), + ]); + } else { + return menuButton; + } + }(), + ), + if (widget.pane.autoSuggestBox != null) + Container( + padding: theme.iconPadding ?? EdgeInsets.zero, + height: 41.0, + alignment: Alignment.center, + margin: topPadding, + child: widget.pane.autoSuggestBox!, ), - if (widget.pane.autoSuggestBox != null) - Container( - padding: theme.iconPadding ?? EdgeInsets.zero, - height: 41.0, - alignment: Alignment.center, - margin: topPadding, - child: widget.pane.autoSuggestBox!, - ), - Expanded( - child: Scrollbar( - key: widget.scrollbarKey, - controller: widget.pane.scrollController, - isAlwaysShown: false, - child: ListView(key: widget.listKey, primary: true, children: [ - ...widget.pane.items.map((item) { - return _OpenNavigationPane.buildItem( - context, widget.pane, item, widget.onItemSelected); - }), - ]), - ), + Expanded( + child: Scrollbar( + key: widget.scrollbarKey, + controller: widget.pane.scrollController, + isAlwaysShown: false, + child: ListView(key: widget.listKey, primary: true, children: [ + ...widget.pane.items.map((item) { + return _OpenNavigationPane.buildItem( + context, widget.pane, item, widget.onItemSelected); + }), + ]), ), - ...widget.pane.footerItems.map((item) { - return _OpenNavigationPane.buildItem( - context, widget.pane, item, widget.onItemSelected); - }), - ]), - ), + ), + ...widget.pane.footerItems.map((item) { + return _OpenNavigationPane.buildItem( + context, widget.pane, item, widget.onItemSelected); + }), + ]), ), ); } From 9d4952ff156b8376cf3808a26037abfb25a321fd Mon Sep 17 00:00:00 2001 From: Bruno D'Luka <45696119+bdlukaa@users.noreply.github.com> Date: Fri, 25 Mar 2022 17:23:28 -0300 Subject: [PATCH 02/16] Remove indicator builder functions --- example/lib/main.dart | 18 ++-- .../navigation/navigation_view/body.dart | 3 + .../navigation_view/indicators.dart | 89 ++++++++----------- .../navigation/navigation_view/pane.dart | 5 +- .../navigation_view/pane_items.dart | 17 +++- .../navigation/navigation_view/view.dart | 1 + 6 files changed, 66 insertions(+), 67 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 1626b00d0..08cdba2bd 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -176,15 +176,15 @@ class _MyHomePageState extends State with WindowListener { ), ), displayMode: appTheme.displayMode, - indicatorBuilder: () { - switch (appTheme.indicator) { - case NavigationIndicators.end: - return NavigationIndicator.end; - case NavigationIndicators.sticky: - default: - return NavigationIndicator.sticky; - } - }(), + // indicatorBuilder: () { + // switch (appTheme.indicator) { + // case NavigationIndicators.end: + // return NavigationIndicator.end; + // case NavigationIndicators.sticky: + // default: + // return NavigationIndicator.sticky; + // } + // }(), items: [ // It doesn't look good when resizing from compact to open // PaneItemHeader(header: Text('User Interaction')), diff --git a/lib/src/controls/navigation/navigation_view/body.dart b/lib/src/controls/navigation/navigation_view/body.dart index b7ad961b7..7192fa939 100644 --- a/lib/src/controls/navigation/navigation_view/body.dart +++ b/lib/src/controls/navigation/navigation_view/body.dart @@ -171,11 +171,14 @@ class _NavigationBody extends InheritedWidget { required Widget child, required this.displayMode, required this.minimalPaneOpen, + this.pane, }) : super(key: key, child: child); final PaneDisplayMode? displayMode; final bool minimalPaneOpen; + final NavigationPane? pane; + static _NavigationBody? maybeOf(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<_NavigationBody>(); } diff --git a/lib/src/controls/navigation/navigation_view/indicators.dart b/lib/src/controls/navigation/navigation_view/indicators.dart index fa056aa42..fa333e2fb 100644 --- a/lib/src/controls/navigation/navigation_view/indicators.dart +++ b/lib/src/controls/navigation/navigation_view/indicators.dart @@ -1,17 +1,5 @@ part of 'view.dart'; -/// Creates a navigation indicator from a function. -/// -/// [pane] is the current NavigationPane used -/// -/// [axis], if null, defaults to [Axis.horinzontal] -typedef NavigationIndicatorBuilder = Widget Function({ - required BuildContext context, - required NavigationPane pane, - required Axis axis, - required Widget child, -}); - /// A indicator used by [NavigationPane] to render the selected /// indicator. class NavigationIndicator extends StatefulWidget { @@ -22,34 +10,35 @@ class NavigationIndicator extends StatefulWidget { required this.index, required this.child, required this.pane, - required this.axis, this.curve = Curves.linear, this.color, }) : super(key: key); /// Creates a [StickyNavigationIndicator] - static Widget sticky({ - required BuildContext context, - required NavigationPane pane, - required Axis axis, - required Widget child, - }) { - if (pane.selected == null) return child; - assert(debugCheckHasFluentTheme(context)); - final theme = NavigationPaneTheme.of(context); + static Widget sticky({Widget child = const SizedBox.shrink()}) { + return Builder(builder: (context) { + final body = _NavigationBody.maybeOf(context); - final left = theme.iconPadding?.left ?? theme.labelPadding?.left ?? 0; - final right = theme.labelPadding?.right ?? theme.iconPadding?.right ?? 0; + if (body?.pane == null) return child; - return StickyNavigationIndicator( - index: pane.selected!, - pane: pane, - child: child, - color: theme.highlightColor, - curve: Curves.easeIn, - axis: axis, - topPadding: EdgeInsets.only(left: left, right: right), - ); + final pane = body!.pane!; + + if (pane.selected == null) return child; + assert(debugCheckHasFluentTheme(context)); + final theme = NavigationPaneTheme.of(context); + + final left = theme.iconPadding?.left ?? theme.labelPadding?.left ?? 0; + final right = theme.labelPadding?.right ?? theme.iconPadding?.right ?? 0; + + return StickyNavigationIndicator( + index: pane.selected!, + pane: pane, + child: child, + color: theme.highlightColor, + curve: Curves.easeIn, + topPadding: EdgeInsets.only(left: left, right: right), + ); + }); } /// Creates an [EndNavigationIndicator] @@ -69,7 +58,6 @@ class NavigationIndicator extends StatefulWidget { child: child, color: theme.highlightColor, curve: theme.animationCurve ?? Curves.linear, - axis: axis, ); } @@ -82,11 +70,6 @@ class NavigationIndicator extends StatefulWidget { /// The navigation pane final NavigationPane pane; - /// The axis corresponding to the current navigation pane. If it's - /// a top pane, [Axis.vertical] will be provided, otherwise - /// [Axis.horizontal]. - final Axis axis; - /// The curve used on the animation, if any /// /// For sticky navigation indicator, [Curves.easeIn] is recommended @@ -99,7 +82,6 @@ class NavigationIndicator extends StatefulWidget { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(IntProperty('index', index)); - properties.add(EnumProperty('axis', axis)); properties .add(DiagnosticsProperty('curve', curve, defaultValue: Curves.linear)); properties.add(ColorProperty('highlight color', color)); @@ -135,6 +117,13 @@ class NavigationIndicatorState extends State { }); } + Axis get axis { + if (_NavigationBody.maybeOf(context)?.displayMode == PaneDisplayMode.top) { + return Axis.vertical; + } + return Axis.horizontal; + } + @override Widget build(BuildContext context) { return widget.child; @@ -148,12 +137,10 @@ class EndNavigationIndicator extends NavigationIndicator { required NavigationPane pane, required int index, required Widget child, - required Axis axis, Curve curve = Curves.easeInOut, Color? color, }) : super( key: key, - axis: axis, pane: pane, child: child, index: index, @@ -174,7 +161,7 @@ class _EndNavigationIndicatorState return Stack(clipBehavior: Clip.none, children: [ widget.child, ...List.generate(offsets!.length, (index) { - final isTop = widget.axis == Axis.vertical; + final isTop = axis == Axis.vertical; final offset = offsets![index]; final size = sizes![index]; @@ -236,13 +223,11 @@ class StickyNavigationIndicator extends NavigationIndicator { required NavigationPane pane, required int index, required Widget child, - required Axis axis, this.topPadding = EdgeInsets.zero, Curve curve = Curves.easeIn, Color? color, }) : super( key: key, - axis: axis, pane: pane, child: child, index: index, @@ -313,7 +298,7 @@ class _StickyNavigationIndicatorState fetch(); final double hFactor = () { - if (widget.axis == Axis.horizontal) { + if (axis == Axis.horizontal) { return sizes![widget.index].height * 0.9; } else { // 6.0 of padding @@ -322,10 +307,10 @@ class _StickyNavigationIndicatorState } }(); - final minOffsetAxis = offsets![minIndex].fromAxis(widget.axis); - final maxOffsetAxis = offsets![maxIndex].fromAxis(widget.axis); + final minOffsetAxis = offsets![minIndex].fromAxis(axis); + final maxOffsetAxis = offsets![maxIndex].fromAxis(axis); - if (widget.axis == Axis.horizontal) { + if (axis == Axis.horizontal) { p1Start = minOffsetAxis - (hFactor / 2); p1End = maxOffsetAxis - (hFactor / 2); @@ -381,10 +366,10 @@ class _StickyNavigationIndicatorState update(widget.index); return CustomPaint( foregroundPainter: _StickyPainter( - y: widget.axis == Axis.horizontal + y: axis == Axis.horizontal ? sizes!.first.height / 1.4 : sizes!.first.height - (indicatorPadding / 2), - padding: widget.axis == Axis.horizontal + padding: axis == Axis.horizontal ? indicatorPadding : widget.topPadding.left + 4.0, p1: p1, @@ -396,7 +381,7 @@ class _StickyNavigationIndicatorState color: widget.color ?? FluentTheme.maybeOf(context)?.accentColor.light ?? Colors.transparent, - axis: widget.axis, + axis: axis, ), child: child, ); diff --git a/lib/src/controls/navigation/navigation_view/pane.dart b/lib/src/controls/navigation/navigation_view/pane.dart index f8fec24b4..a066a67fb 100644 --- a/lib/src/controls/navigation/navigation_view/pane.dart +++ b/lib/src/controls/navigation/navigation_view/pane.dart @@ -81,7 +81,7 @@ class NavigationPane with Diagnosticable { this.customPane, this.menuButton, this.scrollController, - this.indicatorBuilder = NavigationIndicator.sticky, + this.indicatorBuilder, }) : assert(selected == null || selected >= 0); final Key? key; @@ -165,7 +165,7 @@ class NavigationPane with Diagnosticable { final ScrollController? scrollController; /// A function called when building the navigation indicator - final NavigationIndicatorBuilder indicatorBuilder; + final Widget? indicatorBuilder; @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { @@ -351,6 +351,7 @@ class _TopNavigationPane extends StatelessWidget { pane.onChanged?.call(pane.effectiveIndexOf(item)); }, showTextOnTop: !pane.footerItems.contains(item), + displayMode: PaneDisplayMode.top, ); } else { throw UnsupportedError( diff --git a/lib/src/controls/navigation/navigation_view/pane_items.dart b/lib/src/controls/navigation/navigation_view/pane_items.dart index a882ce608..7297dc09f 100644 --- a/lib/src/controls/navigation/navigation_view/pane_items.dart +++ b/lib/src/controls/navigation/navigation_view/pane_items.dart @@ -155,9 +155,9 @@ class PaneItem extends NavigationPaneItem { bool showTextOnTop = true, bool? autofocus, }) { - final PaneDisplayMode mode = displayMode ?? - _NavigationBody.maybeOf(context)?.displayMode ?? - PaneDisplayMode.minimal; + final maybeBody = _NavigationBody.maybeOf(context); + final PaneDisplayMode mode = + displayMode ?? maybeBody?.displayMode ?? PaneDisplayMode.minimal; assert(mode != PaneDisplayMode.auto); final NavigationPaneThemeData theme = NavigationPaneTheme.of(context); @@ -169,7 +169,7 @@ class PaneItem extends NavigationPaneItem { final bool isTop = mode == PaneDisplayMode.top; final bool isCompact = mode == PaneDisplayMode.compact; - return HoverButton( + final button = HoverButton( autofocus: autofocus ?? this.autofocus, focusNode: focusNode, onPressed: onPressed, @@ -357,6 +357,15 @@ class PaneItem extends NavigationPaneItem { ); }, ); + + if (maybeBody?.pane != null) { + return Stack(children: [ + button, + maybeBody!.pane!.indicatorBuilder ?? NavigationIndicator.sticky(), + ]); + } + + return button; } } diff --git a/lib/src/controls/navigation/navigation_view/view.dart b/lib/src/controls/navigation/navigation_view/view.dart index 370338ec0..19b10b251 100644 --- a/lib/src/controls/navigation/navigation_view/view.dart +++ b/lib/src/controls/navigation/navigation_view/view.dart @@ -497,6 +497,7 @@ class NavigationViewState extends State { ? PaneDisplayMode.open : widget.pane?.displayMode, minimalPaneOpen: _minimalPaneOpen, + pane: widget.pane, child: paneResult, ), ); From fbfbd2e579935976926b94b90d7427879eb30519 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka <45696119+bdlukaa@users.noreply.github.com> Date: Fri, 25 Mar 2022 17:35:03 -0300 Subject: [PATCH 03/16] Remove unused parameters --- .../navigation/navigation_view/body.dart | 7 +- .../navigation_view/indicators.dart | 302 ++---------------- .../navigation/navigation_view/pane.dart | 39 +++ .../navigation_view/pane_items.dart | 2 +- 4 files changed, 64 insertions(+), 286 deletions(-) diff --git a/lib/src/controls/navigation/navigation_view/body.dart b/lib/src/controls/navigation/navigation_view/body.dart index 7192fa939..ae21c9094 100644 --- a/lib/src/controls/navigation/navigation_view/body.dart +++ b/lib/src/controls/navigation/navigation_view/body.dart @@ -183,9 +183,14 @@ class _NavigationBody extends InheritedWidget { return context.dependOnInheritedWidgetOfExactType<_NavigationBody>(); } + static _NavigationBody of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType<_NavigationBody>()!; + } + @override bool updateShouldNotify(_NavigationBody oldWidget) { return oldWidget.displayMode != displayMode || - oldWidget.minimalPaneOpen != minimalPaneOpen; + oldWidget.minimalPaneOpen != minimalPaneOpen || + oldWidget.pane != pane; } } diff --git a/lib/src/controls/navigation/navigation_view/indicators.dart b/lib/src/controls/navigation/navigation_view/indicators.dart index fa333e2fb..4ead52c42 100644 --- a/lib/src/controls/navigation/navigation_view/indicators.dart +++ b/lib/src/controls/navigation/navigation_view/indicators.dart @@ -7,69 +7,10 @@ class NavigationIndicator extends StatefulWidget { /// to render the selected indicator. const NavigationIndicator({ Key? key, - required this.index, - required this.child, - required this.pane, this.curve = Curves.linear, this.color, }) : super(key: key); - /// Creates a [StickyNavigationIndicator] - static Widget sticky({Widget child = const SizedBox.shrink()}) { - return Builder(builder: (context) { - final body = _NavigationBody.maybeOf(context); - - if (body?.pane == null) return child; - - final pane = body!.pane!; - - if (pane.selected == null) return child; - assert(debugCheckHasFluentTheme(context)); - final theme = NavigationPaneTheme.of(context); - - final left = theme.iconPadding?.left ?? theme.labelPadding?.left ?? 0; - final right = theme.labelPadding?.right ?? theme.iconPadding?.right ?? 0; - - return StickyNavigationIndicator( - index: pane.selected!, - pane: pane, - child: child, - color: theme.highlightColor, - curve: Curves.easeIn, - topPadding: EdgeInsets.only(left: left, right: right), - ); - }); - } - - /// Creates an [EndNavigationIndicator] - static Widget end({ - required BuildContext context, - required NavigationPane pane, - required Axis axis, - required Widget child, - }) { - if (pane.selected == null) return child; - assert(debugCheckHasFluentTheme(context)); - final theme = NavigationPaneTheme.of(context); - - return EndNavigationIndicator( - index: pane.selected!, - pane: pane, - child: child, - color: theme.highlightColor, - curve: theme.animationCurve ?? Curves.linear, - ); - } - - /// The [NavigationPane]. It can be open, compact, closed or top. - final Widget child; - - /// The current selected index; - final int index; - - /// The navigation pane - final NavigationPane pane; - /// The curve used on the animation, if any /// /// For sticky navigation indicator, [Curves.easeIn] is recommended @@ -81,7 +22,6 @@ class NavigationIndicator extends StatefulWidget { @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties.add(IntProperty('index', index)); properties .add(DiagnosticsProperty('curve', curve, defaultValue: Curves.linear)); properties.add(ColorProperty('highlight color', color)); @@ -106,10 +46,10 @@ class NavigationIndicatorState extends State { void fetch() { WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { - final _offsets = widget.pane.effectiveItems.getPaneItemsOffsets( - widget.pane.paneKey, + final _offsets = pane.effectiveItems.getPaneItemsOffsets( + pane.paneKey, ); - final _sizes = widget.pane.effectiveItems.getPaneItemsSizes(); + final _sizes = pane.effectiveItems.getPaneItemsSizes(); if (mounted && (offsets != _offsets || _sizes != sizes)) { offsets = _offsets; sizes = _sizes; @@ -117,6 +57,14 @@ class NavigationIndicatorState extends State { }); } + NavigationPane get pane { + return _NavigationBody.of(context).pane!; + } + + int get index { + return pane.selected ?? 0; + } + Axis get axis { if (_NavigationBody.maybeOf(context)?.displayMode == PaneDisplayMode.top) { return Axis.vertical; @@ -126,7 +74,7 @@ class NavigationIndicatorState extends State { @override Widget build(BuildContext context) { - return widget.child; + return const SizedBox.shrink(); } } @@ -134,19 +82,9 @@ class NavigationIndicatorState extends State { class EndNavigationIndicator extends NavigationIndicator { const EndNavigationIndicator({ Key? key, - required NavigationPane pane, - required int index, - required Widget child, Curve curve = Curves.easeInOut, Color? color, - }) : super( - key: key, - pane: pane, - child: child, - index: index, - curve: curve, - color: color, - ); + }) : super(key: key, curve: curve, color: color); @override _EndNavigationIndicatorState createState() => _EndNavigationIndicatorState(); @@ -156,10 +94,9 @@ class _EndNavigationIndicatorState extends NavigationIndicatorState { @override Widget build(BuildContext context) { - if (offsets == null || sizes == null) return widget.child; + if (offsets == null || sizes == null) const SizedBox.shrink(); fetch(); return Stack(clipBehavior: Clip.none, children: [ - widget.child, ...List.generate(offsets!.length, (index) { final isTop = axis == Axis.vertical; final offset = offsets![index]; @@ -173,15 +110,14 @@ class _EndNavigationIndicatorState duration: const Duration(milliseconds: 75), reverseDuration: Duration.zero, child: Container( - key: ValueKey(widget.index), + key: ValueKey(this.index), margin: EdgeInsets.symmetric( vertical: isTop ? 0.0 : 0, horizontal: isTop ? 10.0 : 0.0, ), width: isTop ? 20.0 : 6.0, height: isTop ? 4.5 : 20.0, - color: - widget.index != index ? Colors.transparent : widget.color, + color: this.index != index ? Colors.transparent : widget.color, ), ), ), @@ -220,17 +156,11 @@ class StickyNavigationIndicator extends NavigationIndicator { /// Creates a sticky navigation indicator. const StickyNavigationIndicator({ Key? key, - required NavigationPane pane, - required int index, - required Widget child, this.topPadding = EdgeInsets.zero, Curve curve = Curves.easeIn, Color? color, }) : super( key: key, - pane: pane, - child: child, - index: index, curve: curve, color: color, ); @@ -251,30 +181,13 @@ class _StickyNavigationIndicatorState late int oldIndex; late int newIndex; - static const double step = 0.5; - static const double startDelay = 8; - static const double indicatorPadding = 8.0; - - double p1Start = 0.0; - double p2Start = 0.0; - double p1End = 0.0; - double p2End = 0.0; - - double p1 = 0; // percentage of 1st point (0..1) - double p2 = 0; // percentage of 2st point (0..1) - - double delay = 0; - @override void initState() { super.initState(); - newIndex = widget.index; - oldIndex = widget.index; controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 1), ); - controller.repeat(); } @override @@ -283,192 +196,13 @@ class _StickyNavigationIndicatorState super.dispose(); } - void update(int index) { - if (!mounted) return; - if (index != newIndex) { - oldIndex = newIndex; - newIndex = index; - p1 = 0; - p2 = 0; - delay = startDelay; - } - final minIndex = oldIndex; - final maxIndex = newIndex; - - fetch(); - - final double hFactor = () { - if (axis == Axis.horizontal) { - return sizes![widget.index].height * 0.9; - } else { - // 6.0 of padding - // return sizes![widget.index].width - widget.topPadding.horizontal - 6.0; - return 12.5; - } - }(); - - final minOffsetAxis = offsets![minIndex].fromAxis(axis); - final maxOffsetAxis = offsets![maxIndex].fromAxis(axis); - - if (axis == Axis.horizontal) { - p1Start = minOffsetAxis - (hFactor / 2); - p1End = maxOffsetAxis - (hFactor / 2); - - p2Start = minOffsetAxis; - p2End = maxOffsetAxis; - } else { - double horizontalPadding(index) { - final w = sizes![index].width; - return (w / 2.5) - hFactor; - } - - p1Start = minOffsetAxis + horizontalPadding(minIndex); - p1End = maxOffsetAxis + horizontalPadding(maxIndex); - - p2Start = minOffsetAxis + horizontalPadding(minIndex) + hFactor; - p2End = maxOffsetAxis + horizontalPadding(maxIndex) + hFactor; - } - - /// Calculates the velocity the line will move according to a curve. - /// - /// By default, [Curves.easeIn] is used - double calcVelocity(double p) { - return widget.curve.transform(p) + 0.05; - } - - if (p2Start > p2End) { - // move up - final v1 = calcVelocity(p1); - p1 = min(p1 + step * v1, 1); - if (delay == 0) { - final v2 = calcVelocity(p2); - p2 = min(p2 + step * v2, 1); - } - } else { - // move down - final v2 = calcVelocity(p2); - p2 = min(p2 + step * v2, 1); - if (delay == 0) { - final v1 = calcVelocity(p1); - p1 = min(p1 + step * v1, 1); - } - } - if (delay > 0) delay -= 1; - } - @override Widget build(BuildContext context) { - if (offsets == null || sizes == null) return widget.child; - return AnimatedBuilder( - animation: controller, - child: widget.child, - builder: (context, child) { - update(widget.index); - return CustomPaint( - foregroundPainter: _StickyPainter( - y: axis == Axis.horizontal - ? sizes!.first.height / 1.4 - : sizes!.first.height - (indicatorPadding / 2), - padding: axis == Axis.horizontal - ? indicatorPadding - : widget.topPadding.left + 4.0, - p1: p1, - p1Start: p1Start, - p1End: p1End, - p2: p2, - p2Start: p2Start, - p2End: p2End, - color: widget.color ?? - FluentTheme.maybeOf(context)?.accentColor.light ?? - Colors.transparent, - axis: axis, - ), - child: child, - ); - }, - ); + if (offsets == null || sizes == null) return const SizedBox.shrink(); + return const SizedBox.shrink(); } } -class _StickyPainter extends CustomPainter { - final double y; - final double padding; - final double p1; - final double p1Start; - final double p1End; - final double p2; - final double p2Start; - final double p2End; - - final Color color; - - final Axis axis; - - final double strokeWidth; - - const _StickyPainter({ - this.y = 0, - required this.padding, - required this.p1, - required this.p1Start, - required this.p1End, - required this.p2, - required this.p2Start, - required this.p2End, - required this.color, - required this.axis, - this.strokeWidth = 3, - }); - - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = color - ..strokeJoin = StrokeJoin.round - ..strokeCap = StrokeCap.round - ..strokeWidth = strokeWidth; - final double first = p1Start + (p1End - p1Start) * p1; - final double second = p2Start + (p2End - p2Start) * p2; - - if (first.isNegative || second.isNegative) return; - - // debugPrint('from $first to $second within $size'); - - switch (axis) { - case Axis.horizontal: - canvas.drawLine( - Offset(padding, y + first), - Offset(padding, y + second), - paint, - ); - break; - case Axis.vertical: - canvas.drawLine( - Offset(padding + first, y), - Offset(padding + second, y), - paint, - ); - break; - } - } - - @override - bool shouldRepaint(_StickyPainter oldDelegate) { - return y != oldDelegate.y || - padding != oldDelegate.padding || - p1 != oldDelegate.p1 || - p1Start != oldDelegate.p1Start || - p1End != oldDelegate.p1End || - p2 != oldDelegate.p2 || - p2Start != oldDelegate.p2Start || - p2End != oldDelegate.p2End || - color != oldDelegate.color; - } - - @override - bool shouldRebuildSemantics(_StickyPainter oldDelegate) => false; -} - extension _OffsetExtension on Offset { /// Gets the value based on [axis] /// diff --git a/lib/src/controls/navigation/navigation_view/pane.dart b/lib/src/controls/navigation/navigation_view/pane.dart index a066a67fb..79e61f6cc 100644 --- a/lib/src/controls/navigation/navigation_view/pane.dart +++ b/lib/src/controls/navigation/navigation_view/pane.dart @@ -224,6 +224,45 @@ class NavigationPane with Diagnosticable { ), ); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is NavigationPane && + other.key == key && + other.displayMode == displayMode && + other.customPane == customPane && + other.menuButton == menuButton && + other.size == size && + other.header == header && + listEquals(other.items, items) && + listEquals(other.footerItems, footerItems) && + other.autoSuggestBox == autoSuggestBox && + other.autoSuggestBoxReplacement == autoSuggestBoxReplacement && + other.selected == selected && + other.onChanged == onChanged && + other.scrollController == scrollController && + other.indicatorBuilder == indicatorBuilder; + } + + @override + int get hashCode { + return key.hashCode ^ + displayMode.hashCode ^ + customPane.hashCode ^ + menuButton.hashCode ^ + size.hashCode ^ + header.hashCode ^ + items.hashCode ^ + footerItems.hashCode ^ + autoSuggestBox.hashCode ^ + autoSuggestBoxReplacement.hashCode ^ + selected.hashCode ^ + onChanged.hashCode ^ + scrollController.hashCode ^ + indicatorBuilder.hashCode; + } } /// Configure the size of the pane in its various mode. diff --git a/lib/src/controls/navigation/navigation_view/pane_items.dart b/lib/src/controls/navigation/navigation_view/pane_items.dart index 7297dc09f..efdc7e175 100644 --- a/lib/src/controls/navigation/navigation_view/pane_items.dart +++ b/lib/src/controls/navigation/navigation_view/pane_items.dart @@ -361,7 +361,7 @@ class PaneItem extends NavigationPaneItem { if (maybeBody?.pane != null) { return Stack(children: [ button, - maybeBody!.pane!.indicatorBuilder ?? NavigationIndicator.sticky(), + maybeBody?.pane?.indicatorBuilder ?? const StickyNavigationIndicator(), ]); } From 1f50c851f01be868c085eefb224109a5851b617b Mon Sep 17 00:00:00 2001 From: Bruno D'Luka <45696119+bdlukaa@users.noreply.github.com> Date: Sat, 26 Mar 2022 09:35:32 -0300 Subject: [PATCH 04/16] New sticky indicator --- example/lib/main.dart | 2 + .../navigation_view/indicators.dart | 106 +++++++++++++++--- .../navigation/navigation_view/pane.dart | 3 + .../navigation_view/pane_items.dart | 5 +- .../navigation/navigation_view/view.dart | 2 - 5 files changed, 102 insertions(+), 16 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 08cdba2bd..8d78167de 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -337,6 +337,7 @@ class _LinkPaneItemAction extends PaneItem { PaneDisplayMode? displayMode, bool showTextOnTop = true, bool? autofocus, + int index = -1, }) { return Link( uri: Uri.parse(link), @@ -347,6 +348,7 @@ class _LinkPaneItemAction extends PaneItem { displayMode: displayMode, showTextOnTop: showTextOnTop, autofocus: autofocus, + index: index, ), ); } diff --git a/lib/src/controls/navigation/navigation_view/indicators.dart b/lib/src/controls/navigation/navigation_view/indicators.dart index 4ead52c42..38c4cc770 100644 --- a/lib/src/controls/navigation/navigation_view/indicators.dart +++ b/lib/src/controls/navigation/navigation_view/indicators.dart @@ -65,6 +65,10 @@ class NavigationIndicatorState extends State { return pane.selected ?? 0; } + bool get isSelected { + return pane.isSelected(pane.effectiveItems[index]); + } + Axis get axis { if (_NavigationBody.maybeOf(context)?.displayMode == PaneDisplayMode.top) { return Axis.vertical; @@ -149,25 +153,23 @@ class _EndNavigationIndicatorState } /// A sticky navigation indicator. -/// -/// Made by [@raitonubero](/~https://github.com/raitonoberu). Make -/// sure to [check him out](https://gist.github.com/raitonoberu/af76d9b5813b7879e8db940bafa0f325). class StickyNavigationIndicator extends NavigationIndicator { /// Creates a sticky navigation indicator. const StickyNavigationIndicator({ Key? key, + required this.indexValue, this.topPadding = EdgeInsets.zero, Curve curve = Curves.easeIn, Color? color, - }) : super( - key: key, - curve: curve, - color: color, - ); + }) : super(key: key, curve: curve, color: color); + + final int indexValue; /// The padding applied to the indicator if [axis] is [Axis.vertical] final EdgeInsets topPadding; + static const Duration duration = Duration(seconds: 1); + @override _StickyNavigationIndicatorState createState() => _StickyNavigationIndicatorState(); @@ -178,15 +180,15 @@ class _StickyNavigationIndicatorState with SingleTickerProviderStateMixin { late AnimationController controller; - late int oldIndex; - late int newIndex; + NavigationPane? _pane; + int oldIndex = -1; @override void initState() { super.initState(); controller = AnimationController( vsync: this, - duration: const Duration(milliseconds: 1), + duration: StickyNavigationIndicator.duration, ); } @@ -196,10 +198,88 @@ class _StickyNavigationIndicatorState super.dispose(); } + bool get isShowing => + !widget.indexValue.isNegative && + (widget.indexValue == oldIndex || widget.indexValue == index); + + bool get isAbove => oldIndex < index; + bool get isBelow => oldIndex > index; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + if (_pane == null) { + _pane = pane; + } else if (_pane!.selected == pane.selected) { + return; + } + + if (oldIndex == -1 || widget.indexValue != index) { + oldIndex = index; + } + + if (isShowing) { + if (isBelow) { + if (isSelected) { + controller.forward( + from: 0.0, + ); + } else { + controller.reverse( + from: 1.0, + ); + } + } else if (isAbove) { + if (isSelected) { + controller.forward( + from: 0.0, + ); + } else { + controller.reverse( + from: 1.0, + ); + } + } + } + } + @override Widget build(BuildContext context) { - if (offsets == null || sizes == null) return const SizedBox.shrink(); - return const SizedBox.shrink(); + if (offsets == null || sizes == null || !isShowing) { + return const SizedBox.shrink(); + } + assert(debugCheckHasFluentTheme(context)); + + final theme = NavigationPaneTheme.of(context); + + return SizedBox( + height: sizes![widget.indexValue].height, + child: AnimatedBuilder( + animation: CurvedAnimation( + parent: controller, + curve: widget.curve, + ), + child: Container( + width: 2.5, + decoration: BoxDecoration( + color: widget.color ?? theme.highlightColor, + borderRadius: BorderRadius.circular(100), + ), + ), + builder: (context, child) { + return Padding( + padding: EdgeInsets.only( + left: offsets![widget.indexValue].dx, + top: 10.0 * (isAbove ? controller.value : 1.0), + bottom: 10.0 * (isBelow ? controller.value : 1.0), + ), + // child: Text('$oldIndex - ${widget.indexValue}'), + child: child, + ); + }, + ), + ); } } diff --git a/lib/src/controls/navigation/navigation_view/pane.dart b/lib/src/controls/navigation/navigation_view/pane.dart index 79e61f6cc..8ac7d2dd6 100644 --- a/lib/src/controls/navigation/navigation_view/pane.dart +++ b/lib/src/controls/navigation/navigation_view/pane.dart @@ -391,6 +391,7 @@ class _TopNavigationPane extends StatelessWidget { }, showTextOnTop: !pane.footerItems.contains(item), displayMode: PaneDisplayMode.top, + index: pane.effectiveIndexOf(item), ); } else { throw UnsupportedError( @@ -481,6 +482,7 @@ class _CompactNavigationPane extends StatelessWidget { () { pane.onChanged?.call(pane.effectiveIndexOf(item)); }, + index: pane.effectiveIndexOf(item), ); } else { throw UnsupportedError( @@ -596,6 +598,7 @@ class _OpenNavigationPane extends StatefulWidget { onChanged?.call(); }, autofocus: autofocus, + index: pane.effectiveIndexOf(item), ); } else { throw UnsupportedError( diff --git a/lib/src/controls/navigation/navigation_view/pane_items.dart b/lib/src/controls/navigation/navigation_view/pane_items.dart index efdc7e175..4b6eab13a 100644 --- a/lib/src/controls/navigation/navigation_view/pane_items.dart +++ b/lib/src/controls/navigation/navigation_view/pane_items.dart @@ -154,6 +154,7 @@ class PaneItem extends NavigationPaneItem { PaneDisplayMode? displayMode, bool showTextOnTop = true, bool? autofocus, + int index = -1, }) { final maybeBody = _NavigationBody.maybeOf(context); final PaneDisplayMode mode = @@ -361,7 +362,8 @@ class PaneItem extends NavigationPaneItem { if (maybeBody?.pane != null) { return Stack(children: [ button, - maybeBody?.pane?.indicatorBuilder ?? const StickyNavigationIndicator(), + maybeBody?.pane?.indicatorBuilder ?? + StickyNavigationIndicator(indexValue: index), ]); } @@ -483,6 +485,7 @@ class PaneItemAction extends PaneItem implements NavigationPaneItem { PaneDisplayMode? displayMode, bool showTextOnTop = true, bool? autofocus, + int index = -1, }) { return super.build( context, diff --git a/lib/src/controls/navigation/navigation_view/view.dart b/lib/src/controls/navigation/navigation_view/view.dart index 19b10b251..c76c5b516 100644 --- a/lib/src/controls/navigation/navigation_view/view.dart +++ b/lib/src/controls/navigation/navigation_view/view.dart @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:fluent_ui/fluent_ui.dart'; import 'package:flutter/foundation.dart'; From b16651fc168995378ac5488e3efbd870bdf564d6 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka <45696119+bdlukaa@users.noreply.github.com> Date: Sat, 26 Mar 2022 11:51:46 -0300 Subject: [PATCH 05/16] Fix sticky indicator animation --- README.md | 2 +- example/lib/main.dart | 4 +- .../navigation/navigation_view/body.dart | 69 +++++++-- .../navigation_view/indicators.dart | 135 +++++++++++------- .../navigation/navigation_view/pane.dart | 11 +- .../navigation_view/pane_items.dart | 14 +- .../navigation/navigation_view/view.dart | 15 +- 7 files changed, 166 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index 8b1ede820..190baaebe 100644 --- a/README.md +++ b/README.md @@ -471,7 +471,7 @@ You can customize the selected indicator. By default `StickyNavigationIndicator` ```dart pane: NavigationPane( - indicatorBuilder: ({ + indicator: ({ required BuildContext context, /// The navigation pane corresponding to this indicator required NavigationPane pane, diff --git a/example/lib/main.dart b/example/lib/main.dart index 8d78167de..0d82a9481 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -176,7 +176,7 @@ class _MyHomePageState extends State with WindowListener { ), ), displayMode: appTheme.displayMode, - // indicatorBuilder: () { + // indicator: () { // switch (appTheme.indicator) { // case NavigationIndicators.end: // return NavigationIndicator.end; @@ -337,7 +337,6 @@ class _LinkPaneItemAction extends PaneItem { PaneDisplayMode? displayMode, bool showTextOnTop = true, bool? autofocus, - int index = -1, }) { return Link( uri: Uri.parse(link), @@ -348,7 +347,6 @@ class _LinkPaneItemAction extends PaneItem { displayMode: displayMode, showTextOnTop: showTextOnTop, autofocus: autofocus, - index: index, ), ); } diff --git a/lib/src/controls/navigation/navigation_view/body.dart b/lib/src/controls/navigation/navigation_view/body.dart index ae21c9094..d616abbba 100644 --- a/lib/src/controls/navigation/navigation_view/body.dart +++ b/lib/src/controls/navigation/navigation_view/body.dart @@ -116,7 +116,7 @@ class _NavigationBodyState extends State { @override Widget build(BuildContext context) { assert(debugCheckHasFluentTheme(context)); - final _body = _NavigationBody.maybeOf(context); + final _body = InheritedNavigationView.maybeOf(context); final theme = FluentTheme.of(context); final NavigationPaneThemeData paneTheme = NavigationPaneTheme.of(context); return Container( @@ -163,34 +163,79 @@ class _NavigationBodyState extends State { } } -/// A widget that tells [NavigationBody] what's the panel display -/// mode of the parent [NavigationView], if any. -class _NavigationBody extends InheritedWidget { - const _NavigationBody({ +/// A widget that tells what's the the current state of the parent +/// [NavigationView] +/// +/// See also: +/// +/// * [NavigationView], which provides the information for this +/// * [NavigationBody], which is used to display the content +class InheritedNavigationView extends InheritedWidget { + /// Creates an inherited navigation view. + const InheritedNavigationView({ Key? key, required Widget child, required this.displayMode, - required this.minimalPaneOpen, + this.minimalPaneOpen = false, this.pane, + this.oldIndex = 0, + this.itemIndex = -1, }) : super(key: key, child: child); + /// The current pane display mode. final PaneDisplayMode? displayMode; + + /// Whether the minimal pane is open or not final bool minimalPaneOpen; + /// The current navigation pane final NavigationPane? pane; - static _NavigationBody? maybeOf(BuildContext context) { - return context.dependOnInheritedWidgetOfExactType<_NavigationBody>(); + /// The old index + final int oldIndex; + + /// Used by [NavigationIndicator] to know what's the current index of the + /// item + final int itemIndex; + + static InheritedNavigationView? maybeOf(BuildContext context) { + return context + .dependOnInheritedWidgetOfExactType(); + } + + static InheritedNavigationView of(BuildContext context) { + return context + .dependOnInheritedWidgetOfExactType()!; } - static _NavigationBody of(BuildContext context) { - return context.dependOnInheritedWidgetOfExactType<_NavigationBody>()!; + static Widget merge({ + Key? key, + required Widget child, + int? itemIndex, + NavigationPane? pane, + PaneDisplayMode? displayMode, + bool? minimalPaneOpen, + int? oldIndex, + }) { + return Builder(builder: (context) { + final current = InheritedNavigationView.maybeOf(context); + return InheritedNavigationView( + key: key, + child: child, + displayMode: displayMode ?? current?.displayMode, + minimalPaneOpen: minimalPaneOpen ?? current?.minimalPaneOpen ?? false, + itemIndex: itemIndex ?? current?.itemIndex ?? -1, + pane: pane ?? current?.pane, + oldIndex: oldIndex ?? current?.oldIndex ?? 0, + ); + }); } @override - bool updateShouldNotify(_NavigationBody oldWidget) { + bool updateShouldNotify(InheritedNavigationView oldWidget) { return oldWidget.displayMode != displayMode || oldWidget.minimalPaneOpen != minimalPaneOpen || - oldWidget.pane != pane; + oldWidget.pane != pane || + oldWidget.itemIndex != oldWidget.itemIndex; } } diff --git a/lib/src/controls/navigation/navigation_view/indicators.dart b/lib/src/controls/navigation/navigation_view/indicators.dart index 38c4cc770..f396997a6 100644 --- a/lib/src/controls/navigation/navigation_view/indicators.dart +++ b/lib/src/controls/navigation/navigation_view/indicators.dart @@ -58,7 +58,7 @@ class NavigationIndicatorState extends State { } NavigationPane get pane { - return _NavigationBody.of(context).pane!; + return InheritedNavigationView.of(context).pane!; } int get index { @@ -66,16 +66,25 @@ class NavigationIndicatorState extends State { } bool get isSelected { - return pane.isSelected(pane.effectiveItems[index]); + return pane.isSelected(pane.effectiveItems[itemIndex]); } Axis get axis { - if (_NavigationBody.maybeOf(context)?.displayMode == PaneDisplayMode.top) { + if (InheritedNavigationView.maybeOf(context)?.displayMode == + PaneDisplayMode.top) { return Axis.vertical; } return Axis.horizontal; } + int get itemIndex { + return InheritedNavigationView.maybeOf(context)?.itemIndex ?? -1; + } + + int get oldIndex { + return InheritedNavigationView.maybeOf(context)?.oldIndex ?? 0; + } + @override Widget build(BuildContext context) { return const SizedBox.shrink(); @@ -157,18 +166,15 @@ class StickyNavigationIndicator extends NavigationIndicator { /// Creates a sticky navigation indicator. const StickyNavigationIndicator({ Key? key, - required this.indexValue, this.topPadding = EdgeInsets.zero, Curve curve = Curves.easeIn, Color? color, }) : super(key: key, curve: curve, color: color); - final int indexValue; - /// The padding applied to the indicator if [axis] is [Axis.vertical] final EdgeInsets topPadding; - static const Duration duration = Duration(seconds: 1); + static const Duration duration = Duration(milliseconds: 500); @override _StickyNavigationIndicatorState createState() => @@ -177,30 +183,39 @@ class StickyNavigationIndicator extends NavigationIndicator { class _StickyNavigationIndicatorState extends NavigationIndicatorState - with SingleTickerProviderStateMixin { - late AnimationController controller; - - NavigationPane? _pane; - int oldIndex = -1; + with TickerProviderStateMixin { + late AnimationController upController; + late AnimationController downController; @override void initState() { super.initState(); - controller = AnimationController( + upController = AnimationController( vsync: this, duration: StickyNavigationIndicator.duration, - ); + value: 1.0, + )..addListener(_updateListener); + downController = AnimationController( + vsync: this, + duration: StickyNavigationIndicator.duration, + value: 1.0, + )..addListener(_updateListener); } + void _updateListener() => setState(() {}); + + Animation? upAnimation; + Animation? downAnimation; + @override void dispose() { - controller.dispose(); + upController.dispose(); + downController.dispose(); super.dispose(); } bool get isShowing => - !widget.indexValue.isNegative && - (widget.indexValue == oldIndex || widget.indexValue == index); + !itemIndex.isNegative && (itemIndex == oldIndex || itemIndex == index); bool get isAbove => oldIndex < index; bool get isBelow => oldIndex > index; @@ -209,36 +224,47 @@ class _StickyNavigationIndicatorState void didChangeDependencies() { super.didChangeDependencies(); - if (_pane == null) { - _pane = pane; - } else if (_pane!.selected == pane.selected) { - return; - } + downController.value = 1.0; + upController.value = 1.0; - if (oldIndex == -1 || widget.indexValue != index) { - oldIndex = index; - } + animate(); + } + + void animate() async { + // await Future.delayed(StickyNavigationIndicator.duration); + + if (!mounted) return; if (isShowing) { if (isBelow) { if (isSelected) { - controller.forward( - from: 0.0, + downAnimation = Tween(begin: 0, end: 1.0).animate( + CurvedAnimation( + curve: Interval(0.5, 1.0, curve: widget.curve), + parent: downController, + ), ); + downController.forward(from: 0.0); } else { - controller.reverse( - from: 1.0, + upAnimation = Tween(begin: 0, end: 1.0).animate( + CurvedAnimation(curve: widget.curve, parent: upController), ); + upController.reverse(from: 1.0); } } else if (isAbove) { if (isSelected) { - controller.forward( - from: 0.0, + upAnimation = Tween(begin: 0, end: 1.0).animate( + CurvedAnimation( + curve: Interval(0.5, 1.0, curve: widget.curve), + parent: upController, + ), ); + upController.forward(from: 0.0); } else { - controller.reverse( - from: 1.0, + downAnimation = Tween(begin: 0, end: 1.0).animate( + CurvedAnimation(curve: widget.curve, parent: downController), ); + downController.reverse(from: 1.0); } } } @@ -254,30 +280,35 @@ class _StickyNavigationIndicatorState final theme = NavigationPaneTheme.of(context); return SizedBox( - height: sizes![widget.indexValue].height, - child: AnimatedBuilder( - animation: CurvedAnimation( - parent: controller, - curve: widget.curve, - ), - child: Container( - width: 2.5, - decoration: BoxDecoration( - color: widget.color ?? theme.highlightColor, - borderRadius: BorderRadius.circular(100), - ), - ), - builder: (context, child) { + height: sizes![itemIndex].height, + child: IgnorePointer( + child: Builder(builder: (context) { + final child = Container( + width: 2.5, + decoration: BoxDecoration( + color: widget.color ?? theme.highlightColor, + borderRadius: BorderRadius.circular(100), + ), + ); + if (!isSelected) { + if (upController.status == AnimationStatus.dismissed || + downController.status == AnimationStatus.dismissed) { + return const SizedBox.shrink(); + } + } else { + if (upAnimation?.value == 0.0 || downAnimation?.value == 0.0) { + return const SizedBox.shrink(); + } + } return Padding( padding: EdgeInsets.only( - left: offsets![widget.indexValue].dx, - top: 10.0 * (isAbove ? controller.value : 1.0), - bottom: 10.0 * (isBelow ? controller.value : 1.0), + left: offsets![itemIndex].dx, + top: 10.0 * (upAnimation?.value ?? 1.0), + bottom: 10.0 * (downAnimation?.value ?? 1.0), ), - // child: Text('$oldIndex - ${widget.indexValue}'), child: child, ); - }, + }), ), ); } diff --git a/lib/src/controls/navigation/navigation_view/pane.dart b/lib/src/controls/navigation/navigation_view/pane.dart index 8ac7d2dd6..df2e713de 100644 --- a/lib/src/controls/navigation/navigation_view/pane.dart +++ b/lib/src/controls/navigation/navigation_view/pane.dart @@ -81,7 +81,7 @@ class NavigationPane with Diagnosticable { this.customPane, this.menuButton, this.scrollController, - this.indicatorBuilder, + this.indicator = const StickyNavigationIndicator(), }) : assert(selected == null || selected >= 0); final Key? key; @@ -165,7 +165,7 @@ class NavigationPane with Diagnosticable { final ScrollController? scrollController; /// A function called when building the navigation indicator - final Widget? indicatorBuilder; + final Widget? indicator; @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { @@ -243,7 +243,7 @@ class NavigationPane with Diagnosticable { other.selected == selected && other.onChanged == onChanged && other.scrollController == scrollController && - other.indicatorBuilder == indicatorBuilder; + other.indicator == indicator; } @override @@ -261,7 +261,7 @@ class NavigationPane with Diagnosticable { selected.hashCode ^ onChanged.hashCode ^ scrollController.hashCode ^ - indicatorBuilder.hashCode; + indicator.hashCode; } } @@ -391,7 +391,6 @@ class _TopNavigationPane extends StatelessWidget { }, showTextOnTop: !pane.footerItems.contains(item), displayMode: PaneDisplayMode.top, - index: pane.effectiveIndexOf(item), ); } else { throw UnsupportedError( @@ -482,7 +481,6 @@ class _CompactNavigationPane extends StatelessWidget { () { pane.onChanged?.call(pane.effectiveIndexOf(item)); }, - index: pane.effectiveIndexOf(item), ); } else { throw UnsupportedError( @@ -598,7 +596,6 @@ class _OpenNavigationPane extends StatefulWidget { onChanged?.call(); }, autofocus: autofocus, - index: pane.effectiveIndexOf(item), ); } else { throw UnsupportedError( diff --git a/lib/src/controls/navigation/navigation_view/pane_items.dart b/lib/src/controls/navigation/navigation_view/pane_items.dart index 4b6eab13a..e41801440 100644 --- a/lib/src/controls/navigation/navigation_view/pane_items.dart +++ b/lib/src/controls/navigation/navigation_view/pane_items.dart @@ -154,9 +154,8 @@ class PaneItem extends NavigationPaneItem { PaneDisplayMode? displayMode, bool showTextOnTop = true, bool? autofocus, - int index = -1, }) { - final maybeBody = _NavigationBody.maybeOf(context); + final maybeBody = InheritedNavigationView.maybeOf(context); final PaneDisplayMode mode = displayMode ?? maybeBody?.displayMode ?? PaneDisplayMode.minimal; assert(mode != PaneDisplayMode.auto); @@ -359,11 +358,16 @@ class PaneItem extends NavigationPaneItem { }, ); - if (maybeBody?.pane != null) { + if (maybeBody?.pane?.indicator != null) { + final index = maybeBody!.pane!.effectiveIndexOf(this); + if (index == -1) return button; + return Stack(children: [ button, - maybeBody?.pane?.indicatorBuilder ?? - StickyNavigationIndicator(indexValue: index), + InheritedNavigationView.merge( + itemIndex: index, + child: maybeBody.pane!.indicator!, + ), ]); } diff --git a/lib/src/controls/navigation/navigation_view/view.dart b/lib/src/controls/navigation/navigation_view/view.dart index c76c5b516..65e4e3d59 100644 --- a/lib/src/controls/navigation/navigation_view/view.dart +++ b/lib/src/controls/navigation/navigation_view/view.dart @@ -103,6 +103,8 @@ class NavigationViewState extends State { bool _minimalPaneOpen = false; bool _compactOverlayOpen = false; + int oldIndex = 0; + @override void initState() { super.initState(); @@ -121,6 +123,10 @@ class NavigationViewState extends State { if (widget.pane?.scrollController != scrollController) { scrollController = widget.pane?.scrollController ?? scrollController; } + + if (oldWidget.pane?.selected != widget.pane?.selected) { + oldIndex = oldWidget.pane?.selected ?? 0; + } } @override @@ -233,7 +239,7 @@ class NavigationViewState extends State { selected: pane.selected, menuButton: pane.menuButton, scrollController: pane.scrollController, - indicatorBuilder: pane.indicatorBuilder, + indicator: pane.indicator, ), ); } else { @@ -490,12 +496,13 @@ class NavigationViewState extends State { ); return Mica( backgroundColor: theme.backgroundColor, - child: _NavigationBody( + child: InheritedNavigationView( displayMode: _compactOverlayOpen ? PaneDisplayMode.open : widget.pane?.displayMode, minimalPaneOpen: _minimalPaneOpen, pane: widget.pane, + oldIndex: oldIndex, child: paneResult, ), ); @@ -640,7 +647,7 @@ class _NavigationAppBar extends StatelessWidget { Widget build(BuildContext context) { final direction = Directionality.of(context); final PaneDisplayMode displayMode = this.displayMode ?? - _NavigationBody.maybeOf(context)?.displayMode ?? + InheritedNavigationView.maybeOf(context)?.displayMode ?? PaneDisplayMode.top; final leading = NavigationAppBar.buildLeading( context, @@ -685,7 +692,7 @@ class _NavigationAppBar extends StatelessWidget { case PaneDisplayMode.open: case PaneDisplayMode.compact: final isMinimalPaneOpen = - _NavigationBody.maybeOf(context)?.minimalPaneOpen ?? false; + InheritedNavigationView.maybeOf(context)?.minimalPaneOpen ?? false; final double width = displayMode == PaneDisplayMode.minimal && !isMinimalPaneOpen ? 0.0 From f34fc2ef92bc042947718844bdcec88c65b1efa6 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka <45696119+bdlukaa@users.noreply.github.com> Date: Sat, 26 Mar 2022 12:06:58 -0300 Subject: [PATCH 06/16] Fix EndNavigationIndicator --- example/lib/main.dart | 18 +-- .../navigation_view/indicators.dart | 116 +++++++----------- .../navigation_view/pane_items.dart | 35 +++--- 3 files changed, 72 insertions(+), 97 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 0d82a9481..400819af0 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -176,15 +176,15 @@ class _MyHomePageState extends State with WindowListener { ), ), displayMode: appTheme.displayMode, - // indicator: () { - // switch (appTheme.indicator) { - // case NavigationIndicators.end: - // return NavigationIndicator.end; - // case NavigationIndicators.sticky: - // default: - // return NavigationIndicator.sticky; - // } - // }(), + indicator: () { + switch (appTheme.indicator) { + case NavigationIndicators.end: + return const EndNavigationIndicator(); + case NavigationIndicators.sticky: + default: + return const StickyNavigationIndicator(); + } + }(), items: [ // It doesn't look good when resizing from compact to open // PaneItemHeader(header: Text('User Interaction')), diff --git a/lib/src/controls/navigation/navigation_view/indicators.dart b/lib/src/controls/navigation/navigation_view/indicators.dart index f396997a6..3ade93cb8 100644 --- a/lib/src/controls/navigation/navigation_view/indicators.dart +++ b/lib/src/controls/navigation/navigation_view/indicators.dart @@ -33,7 +33,6 @@ class NavigationIndicator extends StatefulWidget { class NavigationIndicatorState extends State { List? offsets; - List? sizes; @override void initState() { @@ -49,10 +48,8 @@ class NavigationIndicatorState extends State { final _offsets = pane.effectiveItems.getPaneItemsOffsets( pane.paneKey, ); - final _sizes = pane.effectiveItems.getPaneItemsSizes(); - if (mounted && (offsets != _offsets || _sizes != sizes)) { + if (mounted && (offsets != _offsets)) { offsets = _offsets; - sizes = _sizes; } }); } @@ -107,57 +104,39 @@ class _EndNavigationIndicatorState extends NavigationIndicatorState { @override Widget build(BuildContext context) { - if (offsets == null || sizes == null) const SizedBox.shrink(); - fetch(); - return Stack(clipBehavior: Clip.none, children: [ - ...List.generate(offsets!.length, (index) { - final isTop = axis == Axis.vertical; - final offset = offsets![index]; - - final size = sizes![index]; - - final indicator = IgnorePointer( - child: Align( - alignment: isTop ? Alignment.bottomCenter : Alignment.centerRight, - child: AnimatedSwitcher( - duration: const Duration(milliseconds: 75), - reverseDuration: Duration.zero, - child: Container( - key: ValueKey(this.index), - margin: EdgeInsets.symmetric( - vertical: isTop ? 0.0 : 0, - horizontal: isTop ? 10.0 : 0.0, - ), - width: isTop ? 20.0 : 6.0, - height: isTop ? 4.5 : 20.0, - color: this.index != index ? Colors.transparent : widget.color, - ), + final isTop = axis == Axis.vertical; + + final theme = NavigationPaneTheme.of(context); + + final indicator = IgnorePointer( + child: Align( + alignment: isTop + ? AlignmentDirectional.bottomCenter + : AlignmentDirectional.centerStart, + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 75), + reverseDuration: Duration.zero, + child: Container( + key: ValueKey(itemIndex), + margin: EdgeInsets.symmetric( + vertical: isTop ? 0.0 : 10.0, + horizontal: isTop ? 10.0 : 0.0, ), + width: isTop ? 20.0 : 6.0, + height: isTop ? 4.5 : double.infinity, + color: itemIndex != index + ? Colors.transparent + : widget.color ?? theme.highlightColor, ), - ); - - // debugPrint('at $offset with $size'); - - if (isTop) { - return Positioned( - top: offset.dy, - left: offset.dx, - width: size.width, - height: size.height, - child: Align( - alignment: Alignment.bottomCenter, - child: indicator, - ), - ); - } else { - return Positioned( - top: offset.dy, - height: size.height, - child: indicator, - ); - } - }), - ]); + ), + ), + ); + + if (isTop) { + return indicator; + } else { + return indicator; + } } } @@ -272,7 +251,7 @@ class _StickyNavigationIndicatorState @override Widget build(BuildContext context) { - if (offsets == null || sizes == null || !isShowing) { + if (offsets == null || !isShowing) { return const SizedBox.shrink(); } assert(debugCheckHasFluentTheme(context)); @@ -280,14 +259,17 @@ class _StickyNavigationIndicatorState final theme = NavigationPaneTheme.of(context); return SizedBox( - height: sizes![itemIndex].height, + height: double.infinity, child: IgnorePointer( child: Builder(builder: (context) { - final child = Container( - width: 2.5, - decoration: BoxDecoration( - color: widget.color ?? theme.highlightColor, - borderRadius: BorderRadius.circular(100), + final child = Align( + alignment: AlignmentDirectional.centerStart, + child: Container( + width: 2.5, + decoration: BoxDecoration( + color: widget.color ?? theme.highlightColor, + borderRadius: BorderRadius.circular(100), + ), ), ); if (!isSelected) { @@ -313,17 +295,3 @@ class _StickyNavigationIndicatorState ); } } - -extension _OffsetExtension on Offset { - /// Gets the value based on [axis] - /// - /// If [Axis.horizontal], [dy] is going to be returned. Otherwise, [dx] is - /// returned. - double fromAxis(Axis axis) { - if (axis == Axis.horizontal) { - return dy; - } else { - return dx; - } - } -} diff --git a/lib/src/controls/navigation/navigation_view/pane_items.dart b/lib/src/controls/navigation/navigation_view/pane_items.dart index e41801440..96a3e23e7 100644 --- a/lib/src/controls/navigation/navigation_view/pane_items.dart +++ b/lib/src/controls/navigation/navigation_view/pane_items.dart @@ -312,7 +312,7 @@ class PaneItem extends NavigationPaneItem { child: AnimatedContainer( duration: theme.animationDuration ?? Duration.zero, curve: theme.animationCurve ?? standardCurve, - margin: const EdgeInsets.only(right: 6.0, left: 6.0, bottom: 4.0), + margin: const EdgeInsets.only(right: 6.0, left: 6.0), decoration: BoxDecoration( color: () { final ButtonState tileColor = this.tileColor ?? @@ -358,20 +358,27 @@ class PaneItem extends NavigationPaneItem { }, ); - if (maybeBody?.pane?.indicator != null) { - final index = maybeBody!.pane!.effectiveIndexOf(this); - if (index == -1) return button; - - return Stack(children: [ - button, - InheritedNavigationView.merge( - itemIndex: index, - child: maybeBody.pane!.indicator!, - ), - ]); - } + return Padding( + padding: const EdgeInsets.only(bottom: 4.0), + child: () { + if (maybeBody?.pane?.indicator != null) { + final index = maybeBody!.pane!.effectiveIndexOf(this); + if (index == -1) return button; + + return Stack(children: [ + button, + Positioned.fill( + child: InheritedNavigationView.merge( + itemIndex: index, + child: maybeBody.pane!.indicator!, + ), + ), + ]); + } - return button; + return button; + }(), + ); } } From 88055d45d48f45ca66e6bb1a7ad61b9b9e1a2f74 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka <45696119+bdlukaa@users.noreply.github.com> Date: Sun, 27 Mar 2022 11:25:40 -0300 Subject: [PATCH 07/16] Hold state --- .../navigation_view/indicators.dart | 25 ++++++++++++------- .../navigation_view/pane_items.dart | 6 +++-- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/src/controls/navigation/navigation_view/indicators.dart b/lib/src/controls/navigation/navigation_view/indicators.dart index 3ade93cb8..3e2558c7e 100644 --- a/lib/src/controls/navigation/navigation_view/indicators.dart +++ b/lib/src/controls/navigation/navigation_view/indicators.dart @@ -186,6 +186,8 @@ class _StickyNavigationIndicatorState Animation? upAnimation; Animation? downAnimation; + int _old = 0; + @override void dispose() { upController.dispose(); @@ -193,8 +195,13 @@ class _StickyNavigationIndicatorState super.dispose(); } - bool get isShowing => - !itemIndex.isNegative && (itemIndex == oldIndex || itemIndex == index); + bool get isShowing { + if (itemIndex.isNegative) return false; + if (itemIndex == oldIndex && _old != oldIndex) { + return true; + } + return itemIndex == index; + } bool get isAbove => oldIndex < index; bool get isBelow => oldIndex > index; @@ -210,11 +217,9 @@ class _StickyNavigationIndicatorState } void animate() async { - // await Future.delayed(StickyNavigationIndicator.duration); - if (!mounted) return; - if (isShowing) { + if (isShowing && _old != oldIndex) { if (isBelow) { if (isSelected) { downAnimation = Tween(begin: 0, end: 1.0).animate( @@ -223,12 +228,12 @@ class _StickyNavigationIndicatorState parent: downController, ), ); - downController.forward(from: 0.0); + await downController.forward(from: 0.0); } else { upAnimation = Tween(begin: 0, end: 1.0).animate( CurvedAnimation(curve: widget.curve, parent: upController), ); - upController.reverse(from: 1.0); + await upController.reverse(from: 1.0); } } else if (isAbove) { if (isSelected) { @@ -238,15 +243,17 @@ class _StickyNavigationIndicatorState parent: upController, ), ); - upController.forward(from: 0.0); + await upController.forward(from: 0.0); } else { downAnimation = Tween(begin: 0, end: 1.0).animate( CurvedAnimation(curve: widget.curve, parent: downController), ); - downController.reverse(from: 1.0); + await downController.reverse(from: 1.0); } } } + + _old = oldIndex; } @override diff --git a/lib/src/controls/navigation/navigation_view/pane_items.dart b/lib/src/controls/navigation/navigation_view/pane_items.dart index 96a3e23e7..ebfb0725c 100644 --- a/lib/src/controls/navigation/navigation_view/pane_items.dart +++ b/lib/src/controls/navigation/navigation_view/pane_items.dart @@ -156,8 +156,10 @@ class PaneItem extends NavigationPaneItem { bool? autofocus, }) { final maybeBody = InheritedNavigationView.maybeOf(context); - final PaneDisplayMode mode = - displayMode ?? maybeBody?.displayMode ?? PaneDisplayMode.minimal; + final PaneDisplayMode mode = displayMode ?? + maybeBody?.displayMode ?? + maybeBody?.pane?.displayMode ?? + PaneDisplayMode.minimal; assert(mode != PaneDisplayMode.auto); final NavigationPaneThemeData theme = NavigationPaneTheme.of(context); From f93caa702326259ec1ed3f0916bf05ab8a44e736 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka <45696119+bdlukaa@users.noreply.github.com> Date: Sun, 27 Mar 2022 11:38:42 -0300 Subject: [PATCH 08/16] Update changelog --- CHANGELOG.md | 41 ++++++++++++++++++- .../navigation_view/indicators.dart | 4 -- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2927edac4..a188ea484 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ Date format: DD/MM/YYYY -## [3.9.2] +## [3.10.0] - Indicators and Command Bar - Improves `icons.dart` formatting and its generation. - Fix: [#207](/~https://github.com/bdlukaa/fluent_ui/pull/207) FilledButton disabled foreground @@ -15,6 +15,45 @@ Date format: DD/MM/YYYY - Add `HorizontalScrollView` helper widget, with mouse wheel horizontal scrolling - Long `content` widget no longer overflow in `ContentDialog` ([#242](/~https://github.com/bdlukaa/fluent_ui/issues/242)) - Content no longer loses state when the pane display mode is changed ([#250](/~https://github.com/bdlukaa/fluent_ui/pull/250)) +- **BREAKING** Update indicators ([#248](/~https://github.com/bdlukaa/fluent_ui/pull/248)): + - Added `InheritedNavigationView` + - Updated sticky indicator to match the latest Win 11 UI ([#173](/~https://github.com/bdlukaa/fluent_ui/issues/173)) + - **BREAKING** Renamed `NavigationPane.indicatorBuilder` to `NavigationPane.indicator` + - **BREAKING** Indicators are no longer built with functions + Before: + ```dart + indicatorBuilder: ({ + required BuildContext context, + required NavigationPane pane, + required Axis axis, + required Widget child, + }) { + if (pane.selected == null) return child; + assert(debugCheckHasFluentTheme(context)); + final theme = NavigationPaneTheme.of(context); + + final left = theme.iconPadding?.left ?? theme.labelPadding?.left ?? 0; + final right = theme.labelPadding?.right ?? theme.iconPadding?.right ?? 0; + + return StickyNavigationIndicator( + index: pane.selected!, + pane: pane, + child: child, + color: theme.highlightColor, + curve: Curves.easeIn, + axis: axis, + topPadding: EdgeInsets.only(left: left, right: right), + ); + } + ``` + + Now: + + ```dart + indicator: StickyNavigationIndicator( + color: Colors.blue.lighter, // optional + ), + ``` ## [3.9.1] - Input Update - [25/02/2022] diff --git a/lib/src/controls/navigation/navigation_view/indicators.dart b/lib/src/controls/navigation/navigation_view/indicators.dart index 3e2558c7e..6199a2fd9 100644 --- a/lib/src/controls/navigation/navigation_view/indicators.dart +++ b/lib/src/controls/navigation/navigation_view/indicators.dart @@ -145,14 +145,10 @@ class StickyNavigationIndicator extends NavigationIndicator { /// Creates a sticky navigation indicator. const StickyNavigationIndicator({ Key? key, - this.topPadding = EdgeInsets.zero, Curve curve = Curves.easeIn, Color? color, }) : super(key: key, curve: curve, color: color); - /// The padding applied to the indicator if [axis] is [Axis.vertical] - final EdgeInsets topPadding; - static const Duration duration = Duration(milliseconds: 500); @override From 87f7878fa348fbb24456e24a13bcefcab4ab0c6d Mon Sep 17 00:00:00 2001 From: Bruno D'Luka <45696119+bdlukaa@users.noreply.github.com> Date: Sun, 27 Mar 2022 11:48:06 -0300 Subject: [PATCH 09/16] Provide more customization options --- README.md | 28 +----------- .../navigation_view/indicators.dart | 45 ++++++++++++------- 2 files changed, 30 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 190baaebe..534337796 100644 --- a/README.md +++ b/README.md @@ -471,33 +471,7 @@ You can customize the selected indicator. By default `StickyNavigationIndicator` ```dart pane: NavigationPane( - indicator: ({ - required BuildContext context, - /// The navigation pane corresponding to this indicator - required NavigationPane pane, - /// Corresponds to the current display mode. If top, Axis.vertical - /// is passed, otherwise Axis.vertical - Axis? axis, - /// Corresponds to the pane itself as a widget. The indicator is - /// rendered over the whole pane. - required Widget child, - }) { - if (pane.selected == null) return child; - assert(debugCheckHasFluentTheme(context)); - final theme = NavigationPaneThemeData.of(context); - - axis??= Axis.horizontal; - - return EndNavigationIndicator( - index: pane.selected, - offsets: () => pane.effectiveItems.getPaneItemsOffsets(pane.paneKey), - sizes: pane.effectiveItems.getPaneItemsSizes, - child: child, - color: theme.highlightColor, - curve: theme.animationCurve ?? Curves.linear, - axis: axis, - ); - }, + indicator: const EndNavigationIndicator(), ) ``` diff --git a/lib/src/controls/navigation/navigation_view/indicators.dart b/lib/src/controls/navigation/navigation_view/indicators.dart index 6199a2fd9..5a5903bfb 100644 --- a/lib/src/controls/navigation/navigation_view/indicators.dart +++ b/lib/src/controls/navigation/navigation_view/indicators.dart @@ -1,5 +1,7 @@ part of 'view.dart'; +const kIndicatorAnimationDuration = Duration(milliseconds: 500); + /// A indicator used by [NavigationPane] to render the selected /// indicator. class NavigationIndicator extends StatefulWidget { @@ -9,6 +11,7 @@ class NavigationIndicator extends StatefulWidget { Key? key, this.curve = Curves.linear, this.color, + this.duration = kIndicatorAnimationDuration, }) : super(key: key); /// The curve used on the animation, if any @@ -16,6 +19,11 @@ class NavigationIndicator extends StatefulWidget { /// For sticky navigation indicator, [Curves.easeIn] is recommended final Curve curve; + /// The duration used on the animation, if any + /// + /// 500 milliseconds is used by default + final Duration duration; + /// The highlight color final Color? color; @@ -92,9 +100,12 @@ class NavigationIndicatorState extends State { class EndNavigationIndicator extends NavigationIndicator { const EndNavigationIndicator({ Key? key, - Curve curve = Curves.easeInOut, Color? color, - }) : super(key: key, curve: curve, color: color); + this.unselectedColor = Colors.transparent, + }) : super(key: key, color: color); + + /// The color of the indicator when the item is not selected + final Color unselectedColor; @override _EndNavigationIndicatorState createState() => _EndNavigationIndicatorState(); @@ -104,11 +115,12 @@ class _EndNavigationIndicatorState extends NavigationIndicatorState { @override Widget build(BuildContext context) { - final isTop = axis == Axis.vertical; + assert(debugCheckHasFluentTheme(context)); + final bool isTop = axis == Axis.vertical; final theme = NavigationPaneTheme.of(context); - final indicator = IgnorePointer( + return IgnorePointer( child: Align( alignment: isTop ? AlignmentDirectional.bottomCenter @@ -125,18 +137,12 @@ class _EndNavigationIndicatorState width: isTop ? 20.0 : 6.0, height: isTop ? 4.5 : double.infinity, color: itemIndex != index - ? Colors.transparent + ? widget.unselectedColor : widget.color ?? theme.highlightColor, ), ), ), ); - - if (isTop) { - return indicator; - } else { - return indicator; - } } } @@ -147,9 +153,8 @@ class StickyNavigationIndicator extends NavigationIndicator { Key? key, Curve curve = Curves.easeIn, Color? color, - }) : super(key: key, curve: curve, color: color); - - static const Duration duration = Duration(milliseconds: 500); + Duration duration = kIndicatorAnimationDuration, + }) : super(key: key, curve: curve, color: color, duration: duration); @override _StickyNavigationIndicatorState createState() => @@ -167,12 +172,12 @@ class _StickyNavigationIndicatorState super.initState(); upController = AnimationController( vsync: this, - duration: StickyNavigationIndicator.duration, + duration: widget.duration, value: 1.0, )..addListener(_updateListener); downController = AnimationController( vsync: this, - duration: StickyNavigationIndicator.duration, + duration: widget.duration, value: 1.0, )..addListener(_updateListener); } @@ -191,6 +196,14 @@ class _StickyNavigationIndicatorState super.dispose(); } + @override + void didUpdateWidget(StickyNavigationIndicator oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.duration != oldWidget.duration) { + upController.duration = downController.duration = widget.duration; + } + } + bool get isShowing { if (itemIndex.isNegative) return false; if (itemIndex == oldIndex && _old != oldIndex) { From a097c250f33d2160d4f305195258a5ebd4cd0fa8 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka <45696119+bdlukaa@users.noreply.github.com> Date: Sun, 27 Mar 2022 11:48:45 -0300 Subject: [PATCH 10/16] Fix tests --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 6b2b3bbf3..2b0817542 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fluent_ui description: Implements Windows UI in Flutter. Based on the official documentation -version: 3.9.2 +version: 3.10.0 homepage: /~https://github.com/bdlukaa/fluent_ui environment: From 2aaf0f3f0154830f343785fb872661142f6c1304 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka <45696119+bdlukaa@users.noreply.github.com> Date: Mon, 28 Mar 2022 15:14:35 -0300 Subject: [PATCH 11/16] Support top mode for sticky indicator --- .../navigation_view/indicators.dart | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/lib/src/controls/navigation/navigation_view/indicators.dart b/lib/src/controls/navigation/navigation_view/indicators.dart index 5a5903bfb..73fc77c1d 100644 --- a/lib/src/controls/navigation/navigation_view/indicators.dart +++ b/lib/src/controls/navigation/navigation_view/indicators.dart @@ -218,10 +218,6 @@ class _StickyNavigationIndicatorState @override void didChangeDependencies() { super.didChangeDependencies(); - - downController.value = 1.0; - upController.value = 1.0; - animate(); } @@ -274,20 +270,25 @@ class _StickyNavigationIndicatorState final theme = NavigationPaneTheme.of(context); + final isHorizontal = axis == Axis.horizontal; + return SizedBox( height: double.infinity, child: IgnorePointer( child: Builder(builder: (context) { - final child = Align( - alignment: AlignmentDirectional.centerStart, - child: Container( - width: 2.5, - decoration: BoxDecoration( - color: widget.color ?? theme.highlightColor, - borderRadius: BorderRadius.circular(100), - ), - ), + final decoration = BoxDecoration( + color: widget.color ?? theme.highlightColor, + borderRadius: BorderRadius.circular(100), ); + final child = isHorizontal + ? Align( + alignment: AlignmentDirectional.centerStart, + child: Container(width: 2.5, decoration: decoration), + ) + : Align( + alignment: Alignment.bottomCenter, + child: Container(height: 2.5, decoration: decoration), + ); if (!isSelected) { if (upController.status == AnimationStatus.dismissed || downController.status == AnimationStatus.dismissed) { @@ -299,11 +300,16 @@ class _StickyNavigationIndicatorState } } return Padding( - padding: EdgeInsets.only( - left: offsets![itemIndex].dx, - top: 10.0 * (upAnimation?.value ?? 1.0), - bottom: 10.0 * (downAnimation?.value ?? 1.0), - ), + padding: isHorizontal + ? EdgeInsets.only( + left: offsets![itemIndex].dx, + top: 10.0 * (upAnimation?.value ?? 1.0), + bottom: 10.0 * (downAnimation?.value ?? 1.0), + ) + : EdgeInsetsDirectional.only( + start: 12.0 * (upAnimation?.value ?? 1.0), + end: 12.0 * (downAnimation?.value ?? 1.0), + ), child: child, ); }), From 035de500aa8ea8ec6c058954c33d7ddccb121e00 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka <45696119+bdlukaa@users.noreply.github.com> Date: Fri, 1 Apr 2022 14:42:41 -0300 Subject: [PATCH 12/16] Ensure the state is kept with keys and AutomaticKeepAliveClientMixin --- example/lib/main.dart | 5 +- .../navigation_view/indicators.dart | 6 +- .../navigation/navigation_view/pane.dart | 76 +++++++++++++------ .../navigation_view/pane_items.dart | 17 +++-- .../navigation/navigation_view/view.dart | 5 ++ 5 files changed, 79 insertions(+), 30 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 400819af0..648f0e12f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -72,8 +72,7 @@ class MyApp extends StatelessWidget { title: appTitle, themeMode: appTheme.mode, debugShowCheckedModeBanner: false, - initialRoute: '/', - routes: {'/': (_) => const MyHomePage()}, + home: const MyHomePage(), color: appTheme.color, darkTheme: ThemeData( brightness: Brightness.dark, @@ -123,6 +122,7 @@ class _MyHomePageState extends State with WindowListener { int index = 0; final settingsController = ScrollController(); + final viewKey = GlobalKey(); @override void initState() { @@ -141,6 +141,7 @@ class _MyHomePageState extends State with WindowListener { Widget build(BuildContext context) { final appTheme = context.watch(); return NavigationView( + key: viewKey, appBar: NavigationAppBar( title: () { if (kIsWeb) return const Text(appTitle); diff --git a/lib/src/controls/navigation/navigation_view/indicators.dart b/lib/src/controls/navigation/navigation_view/indicators.dart index 73fc77c1d..d537a145f 100644 --- a/lib/src/controls/navigation/navigation_view/indicators.dart +++ b/lib/src/controls/navigation/navigation_view/indicators.dart @@ -163,7 +163,7 @@ class StickyNavigationIndicator extends NavigationIndicator { class _StickyNavigationIndicatorState extends NavigationIndicatorState - with TickerProviderStateMixin { + with TickerProviderStateMixin, AutomaticKeepAliveClientMixin { late AnimationController upController; late AnimationController downController; @@ -266,6 +266,7 @@ class _StickyNavigationIndicatorState if (offsets == null || !isShowing) { return const SizedBox.shrink(); } + super.build(context); assert(debugCheckHasFluentTheme(context)); final theme = NavigationPaneTheme.of(context); @@ -316,4 +317,7 @@ class _StickyNavigationIndicatorState ), ); } + + @override + bool get wantKeepAlive => true; } diff --git a/lib/src/controls/navigation/navigation_view/pane.dart b/lib/src/controls/navigation/navigation_view/pane.dart index df2e713de..806860607 100644 --- a/lib/src/controls/navigation/navigation_view/pane.dart +++ b/lib/src/controls/navigation/navigation_view/pane.dart @@ -182,6 +182,11 @@ class NavigationPane with Diagnosticable { )); } + void _changeTo(NavigationPaneItem item) { + final index = effectiveIndexOf(item); + if (selected != index) onChanged?.call(index); + } + /// A list of all of the items displayed on this pane. List get allItems { return items + footerItems; @@ -387,7 +392,7 @@ class _TopNavigationPane extends StatelessWidget { context, selected, () { - pane.onChanged?.call(pane.effectiveIndexOf(item)); + pane._changeTo(item); }, showTextOnTop: !pane.footerItems.contains(item), displayMode: PaneDisplayMode.top, @@ -442,9 +447,14 @@ class _TopNavigationPane extends StatelessWidget { ), child: pane.autoSuggestBox!, ), - ...pane.footerItems.map((item) { - return _buildItem(context, item); - }), + ListView( + primary: false, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + children: pane.footerItems.map((item) { + return _buildItem(context, item); + }).toList(), + ), ]), ); return topBar; @@ -479,7 +489,7 @@ class _CompactNavigationPane extends StatelessWidget { context, selected, () { - pane.onChanged?.call(pane.effectiveIndexOf(item)); + pane._changeTo(item); }, ); } else { @@ -539,16 +549,23 @@ class _CompactNavigationPane extends StatelessWidget { key: scrollbarKey, controller: pane.scrollController, isAlwaysShown: false, - child: ListView(key: listKey, primary: true, children: [ - ...pane.items.map((item) { + child: ListView( + key: listKey, + primary: true, + children: pane.items.map((item) { return _buildItem(context, item); - }), - ]), + }).toList(), + ), ), ), - ...pane.footerItems.map((item) { - return _buildItem(context, item); - }), + ListView( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + primary: false, + children: pane.footerItems.map((item) { + return _buildItem(context, item); + }).toList(), + ), ]), ), ); @@ -592,7 +609,7 @@ class _OpenNavigationPane extends StatefulWidget { context, selected, () { - pane.onChanged?.call(pane.effectiveIndexOf(item)); + pane._changeTo(item); onChanged?.call(); }, autofocus: autofocus, @@ -703,18 +720,33 @@ class _OpenNavigationPaneState extends State<_OpenNavigationPane> key: widget.scrollbarKey, controller: widget.pane.scrollController, isAlwaysShown: false, - child: ListView(key: widget.listKey, primary: true, children: [ - ...widget.pane.items.map((item) { + child: ListView( + key: widget.listKey, + primary: true, + children: widget.pane.items.map((item) { return _OpenNavigationPane.buildItem( - context, widget.pane, item, widget.onItemSelected); - }), - ]), + context, + widget.pane, + item, + widget.onItemSelected, + ); + }).toList(), + ), ), ), - ...widget.pane.footerItems.map((item) { - return _OpenNavigationPane.buildItem( - context, widget.pane, item, widget.onItemSelected); - }), + ListView( + primary: false, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + children: widget.pane.footerItems.map((item) { + return _OpenNavigationPane.buildItem( + context, + widget.pane, + item, + widget.onItemSelected, + ); + }).toList(), + ), ]), ), ); diff --git a/lib/src/controls/navigation/navigation_view/pane_items.dart b/lib/src/controls/navigation/navigation_view/pane_items.dart index ebfb0725c..ad5b381be 100644 --- a/lib/src/controls/navigation/navigation_view/pane_items.dart +++ b/lib/src/controls/navigation/navigation_view/pane_items.dart @@ -360,19 +360,26 @@ class PaneItem extends NavigationPaneItem { }, ); + final int? index = () { + if (maybeBody?.pane?.indicator != null) { + return maybeBody!.pane!.effectiveIndexOf(this); + } + }(); + return Padding( padding: const EdgeInsets.only(bottom: 4.0), child: () { - if (maybeBody?.pane?.indicator != null) { - final index = maybeBody!.pane!.effectiveIndexOf(this); - if (index == -1) return button; - + // If there is an indicator and the item is an effective item + if (maybeBody?.pane?.indicator != null && index != -1) { return Stack(children: [ button, Positioned.fill( child: InheritedNavigationView.merge( itemIndex: index, - child: maybeBody.pane!.indicator!, + child: KeyedSubtree( + key: index != null ? ValueKey(index) : null, + child: maybeBody!.pane!.indicator!, + ), ), ), ]); diff --git a/lib/src/controls/navigation/navigation_view/view.dart b/lib/src/controls/navigation/navigation_view/view.dart index 977fd4e05..71b762c4c 100644 --- a/lib/src/controls/navigation/navigation_view/view.dart +++ b/lib/src/controls/navigation/navigation_view/view.dart @@ -97,6 +97,7 @@ class NavigationViewState extends State { final _listKey = GlobalKey(); final _scrollbarKey = GlobalKey(); final _contentKey = GlobalKey(); + final _overlayKey = GlobalKey(); /// The overlay entry used for minimal pane OverlayEntry? minimalOverlayEntry; @@ -344,6 +345,7 @@ class NavigationViewState extends State { child: () { if (openedWithoutOverlay) { return Mica( + key: _overlayKey, backgroundColor: theme.backgroundColor, child: Container( margin: const EdgeInsets.symmetric(vertical: 1.0), @@ -361,6 +363,7 @@ class NavigationViewState extends State { ); } else if (_compactOverlayOpen) { return Mica( + key: _overlayKey, backgroundColor: _overlayBackgroundColor(), elevation: 10.0, child: Container( @@ -388,6 +391,7 @@ class NavigationViewState extends State { return Padding( padding: appBarPadding, child: Mica( + key: _overlayKey, backgroundColor: theme.backgroundColor, child: _CompactNavigationPane( pane: pane, @@ -448,6 +452,7 @@ class NavigationViewState extends State { ), ), AnimatedPositionedDirectional( + key: _overlayKey, duration: theme.animationDuration ?? Duration.zero, curve: theme.animationCurve ?? Curves.linear, start: _minimalPaneOpen ? 0.0 : -_kOpenNavigationPanelWidth, From 5ed8cbf4afe96a00e9d9329d9f4df7ec7d74b912 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka <45696119+bdlukaa@users.noreply.github.com> Date: Fri, 1 Apr 2022 14:43:20 -0300 Subject: [PATCH 13/16] Keys for indicators are not allowed --- .../navigation/navigation_view/indicators.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/src/controls/navigation/navigation_view/indicators.dart b/lib/src/controls/navigation/navigation_view/indicators.dart index d537a145f..dd9438e60 100644 --- a/lib/src/controls/navigation/navigation_view/indicators.dart +++ b/lib/src/controls/navigation/navigation_view/indicators.dart @@ -1,3 +1,5 @@ +// ignore_for_file: use_key_in_widget_constructors + part of 'view.dart'; const kIndicatorAnimationDuration = Duration(milliseconds: 500); @@ -8,11 +10,10 @@ class NavigationIndicator extends StatefulWidget { /// Creates a navigation indicator used by [NavigationPane] /// to render the selected indicator. const NavigationIndicator({ - Key? key, this.curve = Curves.linear, this.color, this.duration = kIndicatorAnimationDuration, - }) : super(key: key); + }) : super(); /// The curve used on the animation, if any /// @@ -99,10 +100,9 @@ class NavigationIndicatorState extends State { /// The end navigation indicator class EndNavigationIndicator extends NavigationIndicator { const EndNavigationIndicator({ - Key? key, Color? color, this.unselectedColor = Colors.transparent, - }) : super(key: key, color: color); + }) : super(color: color); /// The color of the indicator when the item is not selected final Color unselectedColor; @@ -150,11 +150,10 @@ class _EndNavigationIndicatorState class StickyNavigationIndicator extends NavigationIndicator { /// Creates a sticky navigation indicator. const StickyNavigationIndicator({ - Key? key, Curve curve = Curves.easeIn, Color? color, Duration duration = kIndicatorAnimationDuration, - }) : super(key: key, curve: curve, color: color, duration: duration); + }) : super(curve: curve, color: color, duration: duration); @override _StickyNavigationIndicatorState createState() => From e22b2e469c3b7997b2b987941a5230622c87e852 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka <45696119+bdlukaa@users.noreply.github.com> Date: Fri, 1 Apr 2022 14:52:34 -0300 Subject: [PATCH 14/16] Code updates --- .../navigation/navigation_view/indicators.dart | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/src/controls/navigation/navigation_view/indicators.dart b/lib/src/controls/navigation/navigation_view/indicators.dart index dd9438e60..69811d8aa 100644 --- a/lib/src/controls/navigation/navigation_view/indicators.dart +++ b/lib/src/controls/navigation/navigation_view/indicators.dart @@ -221,7 +221,9 @@ class _StickyNavigationIndicatorState } void animate() async { - if (!mounted) return; + if (!mounted) { + return; + } if (isShowing && _old != oldIndex) { if (isBelow) { @@ -265,12 +267,14 @@ class _StickyNavigationIndicatorState if (offsets == null || !isShowing) { return const SizedBox.shrink(); } + + // Ensure it is only kept alive after if it's showing and after the offets + // are fetched super.build(context); assert(debugCheckHasFluentTheme(context)); - final theme = NavigationPaneTheme.of(context); - - final isHorizontal = axis == Axis.horizontal; + final NavigationPaneThemeData theme = NavigationPaneTheme.of(context); + final bool isHorizontal = axis == Axis.horizontal; return SizedBox( height: double.infinity, From 730f669587f7036c133fed72c33c60a0faa04739 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka <45696119+bdlukaa@users.noreply.github.com> Date: Fri, 1 Apr 2022 16:10:14 -0300 Subject: [PATCH 15/16] topPadding and leftPadding --- example/pubspec.lock | 2 +- .../navigation_view/indicators.dart | 22 +++++++++++++++---- .../navigation/navigation_view/pane.dart | 11 +++------- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 3337998e3..5bd7e8434 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -91,7 +91,7 @@ packages: path: ".." relative: true source: path - version: "3.9.2" + version: "3.10.0" flutter: dependency: "direct main" description: flutter diff --git a/lib/src/controls/navigation/navigation_view/indicators.dart b/lib/src/controls/navigation/navigation_view/indicators.dart index 69811d8aa..f5b0c158a 100644 --- a/lib/src/controls/navigation/navigation_view/indicators.dart +++ b/lib/src/controls/navigation/navigation_view/indicators.dart @@ -153,8 +153,22 @@ class StickyNavigationIndicator extends NavigationIndicator { Curve curve = Curves.easeIn, Color? color, Duration duration = kIndicatorAnimationDuration, + this.topPadding = 12.0, + this.leftPadding = 10.0, }) : super(curve: curve, color: color, duration: duration); + /// The padding used on both horizontal sides of the indicator when the + /// current display mode is top. + /// + /// Defaults to 12.0 + final double topPadding; + + /// The padding used on both vertical sides of the indicator when the current + /// display mode is not top. + /// + /// Defaults to 10.0 + final double leftPadding; + @override _StickyNavigationIndicatorState createState() => _StickyNavigationIndicatorState(); @@ -307,12 +321,12 @@ class _StickyNavigationIndicatorState padding: isHorizontal ? EdgeInsets.only( left: offsets![itemIndex].dx, - top: 10.0 * (upAnimation?.value ?? 1.0), - bottom: 10.0 * (downAnimation?.value ?? 1.0), + top: widget.leftPadding * (upAnimation?.value ?? 1.0), + bottom: widget.leftPadding * (downAnimation?.value ?? 1.0), ) : EdgeInsetsDirectional.only( - start: 12.0 * (upAnimation?.value ?? 1.0), - end: 12.0 * (downAnimation?.value ?? 1.0), + start: widget.topPadding * (upAnimation?.value ?? 1.0), + end: widget.topPadding * (downAnimation?.value ?? 1.0), ), child: child, ); diff --git a/lib/src/controls/navigation/navigation_view/pane.dart b/lib/src/controls/navigation/navigation_view/pane.dart index 806860607..b0b917caa 100644 --- a/lib/src/controls/navigation/navigation_view/pane.dart +++ b/lib/src/controls/navigation/navigation_view/pane.dart @@ -447,14 +447,9 @@ class _TopNavigationPane extends StatelessWidget { ), child: pane.autoSuggestBox!, ), - ListView( - primary: false, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - children: pane.footerItems.map((item) { - return _buildItem(context, item); - }).toList(), - ), + ...pane.footerItems.map((item) { + return _buildItem(context, item); + }).toList(), ]), ); return topBar; From 6a181e8fd49d663d3b630495bb9701e1033af767 Mon Sep 17 00:00:00 2001 From: Bruno D'Luka <45696119+bdlukaa@users.noreply.github.com> Date: Sat, 2 Apr 2022 12:08:31 -0300 Subject: [PATCH 16/16] Animate first item --- lib/src/controls/navigation/navigation_view/indicators.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/controls/navigation/navigation_view/indicators.dart b/lib/src/controls/navigation/navigation_view/indicators.dart index f5b0c158a..03bb86f9d 100644 --- a/lib/src/controls/navigation/navigation_view/indicators.dart +++ b/lib/src/controls/navigation/navigation_view/indicators.dart @@ -88,7 +88,7 @@ class NavigationIndicatorState extends State { } int get oldIndex { - return InheritedNavigationView.maybeOf(context)?.oldIndex ?? 0; + return InheritedNavigationView.maybeOf(context)?.oldIndex ?? -1; } @override @@ -200,7 +200,7 @@ class _StickyNavigationIndicatorState Animation? upAnimation; Animation? downAnimation; - int _old = 0; + int _old = -1; @override void dispose() {