Skip to content

Commit

Permalink
Refactor FBottomNavigationBar
Browse files Browse the repository at this point in the history
TODO: update CHANGELOG
TODO: Look through documentation and recommend people use `FBottomNavigationBar`
  • Loading branch information
Pante committed Aug 19, 2024
1 parent 38ba6b7 commit 5603754
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 104 deletions.
123 changes: 66 additions & 57 deletions forui/lib/src/widgets/bottom_navigation_bar/bottom_navigation_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,76 +7,76 @@ import 'package:meta/meta.dart';
import 'package:forui/forui.dart';
import 'package:forui/src/foundation/tappable.dart';

part 'bottom_navigation_bar_item.dart';

/// A bottom navigation bar.
///
/// A bottom navigation bar is usually present at the bottom of root pages. It is used to navigate between a small
/// number of views, typically between three and five.
///
/// See:
/// * https://forui.dev/docs/bottom-navigation-bar for working examples.
/// * [FBottomNavigationBarStyle] for customizing a card's appearance.
/// * [FBottomNavigationBarStyle] for customizing a bottom navigation bar's appearance.
class FBottomNavigationBar extends StatelessWidget {
/// Returns the [FBottomNavigationBarItemStyle] and currently selected index of the [FBottomNavigationBar] in the
/// given [context].
///
/// ## Contract
/// Throws [AssertionError] if there is no ancestor [FBottomNavigationBar] in the given [context].
static ({FBottomNavigationBarItemStyle itemStyle, bool current}) of(BuildContext context) {
final _InheritedData? result = context.dependOnInheritedWidgetOfExactType<_InheritedData>();
assert(result != null, 'No _InheritedData found in context');
return (itemStyle: result!.itemStyle, current: result.current);
}

/// The style.
final FBottomNavigationBarStyle? style;

/// The items.
final List<Widget> items;

/// A callback for when an item is selected.
final ValueChanged<int>? onChange;

/// The index.
final int? index;

/// The children.
final List<Widget> children;

/// Creates a [FBottomNavigationBar] with [FBottomNavigationBarItem]s.
FBottomNavigationBar({
required List<FBottomNavigationBarItem> items,
this.style,
this.onChange,
int index = -1,
super.key,
}) : items = items
.mapIndexed(
(currentIndex, item) => _FBottomNavigationBarItem(
item: item,
current: index == currentIndex,
style: style?.item,
),
)
.toList();

/// Creates a [FBottomNavigationBar] with [Widget]s.
const FBottomNavigationBar.raw({
required this.items,
const FBottomNavigationBar({
required this.children,
this.style,
this.onChange,
this.index,
super.key,
});

@override
Widget build(BuildContext context) {
final style = this.style ?? context.theme.bottomNavigationBarStyle;

return DecoratedBox(
decoration: style.decoration,
child: SafeArea(
top: false,
bottom: false,
child: Padding(
padding: style.padding.copyWith(
bottom: style.padding.bottom + (MediaQuery.of(context).viewPadding.bottom * 2 / 3),
bottom: style.padding.bottom + (MediaQuery.viewPaddingOf(context).bottom * 2 / 3),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: items
.mapIndexed(
(index, item) => Expanded(
child: FTappable.animated(
onPress: () => onChange?.call(index),
child: item,
children: [
for (final (i, child) in children.indexed)
Expanded(
child: FTappable.animated(
onPress: () {
onChange?.call(i);
},
child: _InheritedData(
itemStyle: style.itemStyle,
current: index == i,
child: child,
),
),
)
.toList(),
),
],
),
),
),
Expand All @@ -88,8 +88,30 @@ class FBottomNavigationBar extends StatelessWidget {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('style', style))
..add(IterableProperty('items', items))
..add(ObjectFlagProperty.has('onSelect', onChange));
..add(ObjectFlagProperty.has('onChange', onChange))
..add(IntProperty('initialIndex', index));
}
}

class _InheritedData extends InheritedWidget {
final FBottomNavigationBarItemStyle itemStyle;
final bool current;

const _InheritedData({
required this.itemStyle,
required this.current,
required super.child,
});

@override
bool updateShouldNotify(_InheritedData old) => old.itemStyle != itemStyle || old.current != current;

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('style', itemStyle))
..add(FlagProperty('current', value: current, ifTrue: 'current'));
}
}

Expand All @@ -102,12 +124,12 @@ class FBottomNavigationBarStyle with Diagnosticable {
final EdgeInsets padding;

/// The item's style.
final FBottomNavigationBarItemStyle item;
final FBottomNavigationBarItemStyle itemStyle;

/// Creates a [FBottomNavigationBarStyle].
FBottomNavigationBarStyle({
required this.decoration,
required this.item,
required this.itemStyle,
this.padding = const EdgeInsets.all(5),
});

Expand All @@ -118,25 +140,12 @@ class FBottomNavigationBarStyle with Diagnosticable {
color: colorScheme.background,
),
padding = const EdgeInsets.all(5),
item = FBottomNavigationBarItemStyle.inherit(
itemStyle = FBottomNavigationBarItemStyle.inherit(
colorScheme: colorScheme,
typography: typography,
);

/// Returns a copy of this [FBottomNavigationBarStyle] with the given properties replaced.
///
/// ```dart
/// final style = FBottomNavigationBarStyle(
/// decoration: ...,
/// padding: ...,
/// ...
/// );
///
/// final copy = style.copyWith(padding: ...);
///
/// print(style.decoration == copy.decoration); // true
/// print(style.padding == copy.padding); // false
/// ```
@useResult
FBottomNavigationBarStyle copyWith({
BoxDecoration? decoration,
Expand All @@ -146,7 +155,7 @@ class FBottomNavigationBarStyle with Diagnosticable {
FBottomNavigationBarStyle(
decoration: decoration ?? this.decoration,
padding: padding ?? this.padding,
item: item ?? this.item,
itemStyle: item ?? this.itemStyle,
);

@override
Expand All @@ -155,7 +164,7 @@ class FBottomNavigationBarStyle with Diagnosticable {
properties
..add(DiagnosticsProperty('decoration', decoration))
..add(DiagnosticsProperty('padding', padding))
..add(DiagnosticsProperty('item', item));
..add(DiagnosticsProperty('itemStyle', itemStyle));
}

@override
Expand All @@ -165,8 +174,8 @@ class FBottomNavigationBarStyle with Diagnosticable {
runtimeType == other.runtimeType &&
decoration == other.decoration &&
padding == other.padding &&
item == other.item;
itemStyle == other.itemStyle;

@override
int get hashCode => decoration.hashCode ^ padding.hashCode ^ item.hashCode;
int get hashCode => decoration.hashCode ^ padding.hashCode ^ itemStyle.hashCode;
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
part of 'bottom_navigation_bar.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:forui/forui.dart';
import 'package:meta/meta.dart';

class _FBottomNavigationBarItem extends StatelessWidget {
/// A [FBottomNavigationBar] item.
class FBottomNavigationBarItem extends StatelessWidget {
/// The style.
final FBottomNavigationBarItemStyle? style;

final FBottomNavigationBarItem item;
/// The icon.
final SvgAsset icon;

final bool current;
/// The label.
final String label;

const _FBottomNavigationBarItem({
required this.item,
required this.current,
/// Creates a [FBottomNavigationBarItem].
const FBottomNavigationBarItem({
required this.icon,
required this.label,
this.style,
super.key,
});

@override
Widget build(BuildContext context) {
final style = item.style ?? this.style ?? context.theme.bottomNavigationBarStyle.item;
final (:itemStyle, :current) = FBottomNavigationBar.of(context);
final style = this.style ?? itemStyle;

return MouseRegion(
cursor: SystemMouseCursors.click,
Expand All @@ -24,7 +34,7 @@ class _FBottomNavigationBarItem extends StatelessWidget {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
item.icon(
icon(
height: style.iconSize,
colorFilter: ColorFilter.mode(
current ? style.activeIconColor : style.inactiveIconColor,
Expand All @@ -33,7 +43,7 @@ class _FBottomNavigationBarItem extends StatelessWidget {
),
const SizedBox(height: 2),
Text(
item.label,
label,
overflow: TextOverflow.ellipsis,
style: current ? style.activeTextStyle : style.inactiveTextStyle,
),
Expand All @@ -48,30 +58,11 @@ class _FBottomNavigationBarItem extends StatelessWidget {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('style', style))
..add(DiagnosticsProperty('item', item))
..add(FlagProperty('active', value: current, ifTrue: 'active'));
..add(DiagnosticsProperty('icon', icon))
..add(StringProperty('label', label));
}
}

/// A [FBottomNavigationBar] item.
class FBottomNavigationBarItem {
/// The style.
final FBottomNavigationBarItemStyle? style;

/// The icon.
final SvgAsset icon;

/// The label.
final String label;

/// Creates a [FBottomNavigationBarItem].
const FBottomNavigationBarItem({
required this.icon,
required this.label,
this.style,
});
}

/// [FBottomNavigationBarItem]'s style.
class FBottomNavigationBarItemStyle with Diagnosticable {
/// The icon's size. Defaults to `28`.
Expand Down Expand Up @@ -118,21 +109,6 @@ class FBottomNavigationBarItemStyle with Diagnosticable {
padding = const EdgeInsets.all(5);

/// Returns a copy of this [FBottomNavigationBarItemStyle] with the given properties replaced.
///
/// ```dart
/// final style = FBottomNavigationBarItemStyle(
/// activeIconColor: Colors.black,
/// inactiveIconColor: Colors.white,
/// ...
/// );
///
/// final copy = style.copyWith(
/// inactiveIconColor: Colors.blue,
/// );
///
/// print(copy.activeIconColor); // black
/// print(copy.inactiveIconColor); // blue
/// ```
@useResult
FBottomNavigationBarItemStyle copyWith({
double? iconSize,
Expand Down
1 change: 1 addition & 0 deletions forui/lib/widgets/bottom_navigation_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
library forui.widgets.bottom_navigation_bar;

export '../src/widgets/bottom_navigation_bar/bottom_navigation_bar.dart';
export '../src/widgets/bottom_navigation_bar/bottom_navigation_bar_item.dart';
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ void main() {
background: background,
child: FBottomNavigationBar(
index: 2,
items: [
children: [
FBottomNavigationBarItem(
icon: FAssets.icons.home,
label: 'Home',
Expand Down

0 comments on commit 5603754

Please sign in to comment.