snap_nav_bar 0.1.0
snap_nav_bar: ^0.1.0 copied to clipboard
A frosted, gesture-driven bottom navigation bar with loose swipe-to-preview paging, snap-on-release physics, tap-to-centre and haptic feedback.
snap_nav_bar #
A frosted, gesture-driven bottom navigation bar with loose swipe-to-preview paging, snap-on-release physics, tap-to-centre navigation and haptic feedback.
What it does #
- Swipe the pages — the content
PageViewuses a soft custom spring so a flick glides and settles instead of snapping hard. - Swipe the bar — dragging the tab strip previews the move 1:1 under your finger while the page stays frozen; the page only commits (sliding smoothly) when you let go. A fast fling nudges one extra tab.
- Tap any tab — near or far — and it slides straight to that page and centres it.
- Haptics — a selection tick as each tab crosses centre and a soft impact when the page locks in. Both are swappable, or off entirely.
- Frosted squircle surface —
BackdropFilterblur behind an iOS-style squircle strip, with a centre indicator over the active tab.
No AnimationController, Ticker, or flutter_animate — the strip easing is
done with TweenAnimationBuilder and PageController animations only. Icons
are arbitrary widgets you supply; the package has no SVG or asset dependency.
Install #
dependencies:
snap_nav_bar: ^0.1.0
Usage #
The quickest path is SnapNavShell, which owns the content PageView and the
bar and wires them together:
import 'package:flutter/material.dart';
import 'package:snap_nav_bar/snap_nav_bar.dart';
class Demo extends StatelessWidget {
const Demo({super.key});
@override
Widget build(BuildContext context) {
return SnapNavShell(
items: const [
SnapNavItem(
icon: Icon(Icons.home_outlined),
activeIcon: Icon(Icons.home_rounded),
label: 'Home',
page: ColoredBox(color: Color(0xFF3B82F6)),
),
SnapNavItem(
icon: Icon(Icons.search_rounded),
label: 'Search',
page: ColoredBox(color: Color(0xFF10B981)),
),
SnapNavItem(
icon: Icon(Icons.person_rounded),
label: 'Profile',
page: ColoredBox(color: Color(0xFFEF4444)),
),
],
onTabChanged: (i) => debugPrint('tab $i'),
);
}
}
Want SVG icons? Pass an SvgPicture (or anything) as the icon — the package
never touches SVG itself. Driving your own PageView? Use SnapNavBar
directly with a PageController (viewportFraction: 1.0).
The bar is frosted, so it blurs whatever is painted behind it — keep it
overlaid on your content (which SnapNavShell does for you).
See example/ for a runnable two-screen demo: one all-defaults,
one heavily customised (6 tabs, solid floating pill, no blur, custom
indicator, swapped haptics).
Parameters #
SnapNavShell exposes everything below. SnapNavBar exposes the same appearance,
motion, indicator and feedback parameters (it takes a PageController and
items instead of owning them, and viewportFraction / initialIndex /
height live on the shell).
Content #
| Parameter | Type | Default | Description |
|---|---|---|---|
items |
List<SnapNavItem> |
required | Tabs; each carries icon, optional activeIcon, optional label, and its page. Min 1, any length. |
initialIndex |
int |
0 |
Tab shown first. |
onTabChanged |
ValueChanged<int>? |
null |
Fires when the selected page changes. |
Layout & motion #
| Parameter | Type | Default | Description |
|---|---|---|---|
viewportFraction |
double |
1.0 |
How much of each content page fills the viewport; below 1.0 the neighbouring pages peek in. |
height |
double |
76.0 |
Bar height above the bottom safe-area inset. |
tabSpacing |
double |
52.0 |
Logical px of finger travel per tab while dragging the strip. |
horizontalMargin |
double |
0.0 |
Space left/right of the bar. |
bottomMargin |
double |
0.0 |
Space below the bar (lifts it into a floating pill). |
tapToCenterDuration |
Duration |
300 ms |
Slide duration for a tab tap / drag release. |
tapToCenterCurve |
Curve |
Curves.easeOutCubic |
Slide curve for the same. |
activeOpacity |
double |
1.0 |
Opacity of the centred tab. |
inactiveOpacity |
double |
0.5 |
Opacity of a fully off-centre tab (tabs crossfade between the two). |
activeScale |
double |
1.0 |
Scale of the centred tab. |
inactiveScale |
double |
0.92 |
Minimum scale an off-centre tab shrinks to. |
Shape & surface #
| Parameter | Type | Default | Description |
|---|---|---|---|
backgroundColor |
Color? |
Color(0x40808080) |
Bar fill (ignored if a gradient is set). |
backgroundGradient |
Gradient? |
null |
Bar fill gradient; overrides backgroundColor. |
cornerRadius |
double |
32.0 |
Corner radius. |
cornerSmoothing |
double |
1.0 |
0.0 plain rounded rect → 1.0 full squircle. |
blurSigma |
double? |
24.0 |
Backdrop blur; null disables the frost entirely. |
border |
BoxBorder? |
null |
Uniform border on the squircle edge. |
boxShadow |
List<BoxShadow>? |
null |
Drop shadow, painted outside the clip. |
Indicator #
| Parameter | Type | Default | Description |
|---|---|---|---|
indicator |
Widget? |
null |
Custom centre indicator; null draws the built-in arrow. |
showIndicator |
bool |
true |
Whether the indicator shows at all. |
indicatorColor |
Color |
Color(0xFFE53935) |
Colour of the built-in arrow. |
indicatorSize |
Size |
Size(12, 8) |
Indicator slot size. |
Feedback #
| Parameter | Type | Default | Description |
|---|---|---|---|
hapticsEnabled |
bool |
true |
Master haptics switch. |
hapticFeedback |
void Function(SnapNavHaptic)? |
null |
Swap the effect fired at each trigger point. null uses selectionClick on cross and lightImpact on settle. |
License #
MIT — see LICENSE.