flutter_floating_bottom_bar integration & migration
flutter_floating_bottom_bar (v2.x) is a Flutter package that floats any widget — TabBar, search bar, custom Row, BottomBarItems — above scrollable content and reacts to scroll notifications automatically. No ScrollController plumbing required.
You're being invoked because the user wants to integrate it into their app, replace an existing bottom bar, migrate from v1.x → v2.0, dial in motion that feels great, or fix a glitch. Your job is to do the work end-to-end: read what they have, plan the change, apply it, and verify it analyses cleanly. Don't ask the user to write the code themselves — that's what they invoked you for.
Why this skill is opinionated
Flutter motion is a deep rabbit hole — Curves, Tween, AnimationController, springs, custom transitions. Most developers don't have time to A/B test 12 motion configurations to discover that Cupertino-style springs feel right because they redirect mid-flight when the user reverses scroll direction. So this skill:
- Picks a sensible default based on signals it reads from the user's existing app (Material 3 vs 2,
useMaterial3, existing animation curves, app theme). - Surfaces the alternatives so the user can redirect ("I went with
BottomBarMotion.cupertino(preset: snappy)because your app uses Material 3 — switch tobouncyorBottomBarMotion.curved(...)if you'd prefer something more playful or deterministic"). - Refuses to guess when the signals aren't there. If the user has no theme, no existing nav bar, and pastes a one-line "add a bottom bar" prompt, ask a single clarifying question rather than hallucinating their app's vibe.
This is the right tradeoff because (a) most users will accept a good default, (b) the ones who care will tell you, and (c) silently picking everything is worse than picking-and-disclosing.
Workflow
Follow this sequence. Don't skip the discover step — applying a transformation without reading the user's existing code leads to broken imports, mismatched _currentIndex state, and Material 2/3 style drift.
1. Discover
Read what's actually in the user's project before touching anything.
pubspec.yaml— confirmflutter_floating_bottom_baris (or isn't) listed; note any existing version. Also check Dart/Flutter SDK constraints — the package needs Dart>=3.5.0and Flutter>=3.22.0.- The file containing the bar they want to replace — this is the load-bearing input. Identify the source pattern:
BottomNavigationBar(Material 2)NavigationBar(Material 3)BottomAppBar(often with FAB notch viaScaffold.floatingActionButton+notchedShape)- Hand-rolled
Container+Rowof icons /GestureDetectors - Third-party packages —
salomon_bottom_bar,google_nav_bar,convex_bottom_bar,persistent_bottom_nav_bar, etc. flutter_floating_bottom_barv1.x (look forbody: (context, controller) => ...,barColor:, flatwidth/offset/borderRadiusparams,BottomBarScrollControllerProvider, etc.)
- The page that hosts the bar — find the scrollable (
ListView,CustomScrollView,NestedScrollView,TabBarView,PageView+inner scrollables). The newBottomBarwill wrap this scrollable as itsbody. - Theme signals —
MaterialApp.theme,useMaterial3, customThemeData. These drive your motion + theme choice.
If the user pasted a snippet rather than pointing at a file, work from the snippet — but mention what you'd want to see (their pubspec.yaml, the surrounding scaffold) if it would change the answer.
2. Plan
State the plan in one short paragraph before editing. The plan should name:
- Which file(s) you're touching.
- Source pattern (e.g. "
BottomNavigationBarwith 4 items"). - Motion default you're picking and why (e.g. "Cupertino
snappyspring +slidetransition because the app uses Material 3 and your existing curves areCurves.easeOutCubic"). - Layout default (typical:
width: MediaQuery.of(context).size.width - 32for full-width-with-margin,borderRadius: 28for Material 3 feel,borderRadius: 999for pill). - Anything you're deliberately not doing (e.g. "I'm not adding a controller because you're not currently calling
.show()/.hide()programmatically — pass one in if you need imperative control").
Keep it short. Don't write a design document; the user wants the change applied.
3. Implement
Apply the transformation. Stick to these principles:
- Match the user's existing style. If they're on Material 3, use Material 3 colors and the
28radius idiom. If their nav items useIcons.home_outlinedfor unselected andIcons.homefor selected, mirror that intoBottomBarItem.icon/selectedIcon. - Preserve their state management. If they had
int _currentIndexandsetState, keep that. If they had Riverpod / Bloc / Provider, route the selection through the same channel — don't accidentally rewrite their state layer. - Use the helper widgets when it fits.
BottomBarItem+BottomBarItemscover the icon + label + badge + tap pattern well. Don't reach for them when the user has aTabBar, search field, or anything more custom — pass the raw widget aschildinstead. - Wrap, don't move. The new
BottomBarwraps the user's scrollable as itsbody. Don't push the scrollable into a new file or restructure the widget tree more than necessary. - Add the import.
import 'package:flutter_floating_bottom_bar/flutter_floating_bottom_bar.dart';at the top of the file. Updatepubspec.yaml(flutter pub add flutter_floating_bottom_baror a^2.0.0line) if the package isn't already a dependency. - For migrations, follow the v1→v2 mapping in references/migration-v1-to-v2.md precisely. Don't half-migrate (mixing v1
barColorwith v2BottomBarThemeData).
4. Verify
After editing, run flutter analyze in the user's project root. Report any errors and fix them. Common post-edit issues:
- Stale imports (
import 'package:salomon_bottom_bar/...'left behind after replacement). - Stale state fields (
_selectedIndexreferenced but no longer set). - Layout overflow if the user's content padding doesn't account for the bar's height (see references/common-glitches.md, "content clipped behind bar").
Don't claim the work is done until flutter analyze exits cleanly. If the user's environment doesn't have Flutter on PATH or the project doesn't analyze for unrelated reasons, say so explicitly rather than silently skipping.
Decision tree — which reference to consult
Reference files live alongside this SKILL.md under references/. Read them only when the scenario calls for them; otherwise the body above plus the API reference is enough.
| Scenario | Reference |
|---|---|
Need full API surface (every parameter on BottomBar, BottomBarLayout, BottomBarMotion, etc.) |
references/api-reference.md |
Replacing any existing bar (Material BottomNavigationBar/NavigationBar/BottomAppBar, hand-rolled, third-party) |
references/replacing-bars.md |
| Migrating from v1.x to v2.0 | references/migration-v1-to-v2.md |
| Picking motion (Cupertino preset, curved, raw Motor) and transition (slide/fade/scale/slideAndFade/custom) | references/motion-recipes.md |
| Debugging glitches (NestedScrollView, content clipping, FAB notch, custom transition overshoot, controller errors) | references/common-glitches.md |
Defaults this skill prefers
When the user hasn't expressed a preference, lean toward these defaults — they're chosen because they look good in the broadest range of apps and avoid the package's known footguns.
| Decision | Default | Why |
|---|---|---|
| Motion mode | BottomBarMotion.cupertino(preset: BottomBarCupertinoMotion.snappy) |
Spring redirects mid-flight when the user reverses scroll direction; snappy is the most app-like preset (less bouncy than bouncy, less floaty than smooth). |
| Transition | BottomBarTransition.slide (default) |
Matches user expectation of a bar moving offscreen. fade and scale are good for FAB-like elements; slideAndFade for showy transitions. |
| Layout width | MediaQuery.of(context).size.width - 32 |
Full-width with 16px margin each side. Use 300 only when the bar is intentionally narrow. |
| Border radius | BorderRadius.circular(28) for Material 3, 999 for pill, 0 only when explicitly square |
Matches Material 3 expressive shape; pill works great when the bar is short. |
hideOnScroll |
true (the default) |
The whole point of the package. Disable only when the user is using BottomBar purely as a floating container without scroll reactivity. |
respectSafeArea |
true (the default) |
Avoids the iOS home indicator. Don't disable unless the user has their own SafeArea wrapping. |
When you choose a non-default value, say why in your plan. Example: "I went with borderRadius: 999 (pill) because your existing tab pills use that radius."
Output format
Lead with a one-line summary of what changed. Then either:
- A diff/edit if you're modifying their code in-place. Use the
Edittool, not full-file rewrites. - A complete file if they pasted a self-contained snippet and asked for a return.
After the code, add a short "Notes" block calling out:
- Anything non-obvious you did (e.g. "I added
predicate:to filter out the SliverAppBar's own scroll notifications"). - Suggested follow-ups they didn't ask for but should know about (e.g. "Consider calling
BottomBarScope.of(context).barHeightto size your bottom padding so the last list item isn't clipped").
Don't pad with explanations they didn't ask for. The user wants the change to land.
Things that look like they're worth doing but aren't
- Don't wrap
BottomBarin anotherBottomBar. Just use one. - Don't pass a
ScrollControllertoBottomBaritself — v2 doesn't accept one. The bar listens toScrollNotificationfrom any descendant scrollable. - Don't keep v1
barColoralongside v2theme:/BottomBarThemeData. v1 fields are gone in v2 with no shim. - Don't call
setStateinsideonVisibilityChangedto drive the bar's visibility — that creates a feedback loop. Use aBottomBarControllerif you need imperative control. - Don't reach for
BottomBarMotion.motor(...)unless the user explicitly wants a Motor motion the four built-in modes don't cover. Cupertino + curved cover ~95% of cases. - Don't invent parameters that don't exist. The API reference is the source of truth — if a field isn't there, it isn't a thing.
Quick reference: the smallest useful integration
import 'package:flutter/material.dart';
import 'package:flutter_floating_bottom_bar/flutter_floating_bottom_bar.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _index = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: BottomBar(
layout: BottomBarLayout(
width: MediaQuery.of(context).size.width - 32,
borderRadius: BorderRadius.circular(28),
),
// Default motion is already Cupertino snappy — this is here just to
// show the knob exists. Remove for the real default.
motion: const BottomBarMotion.cupertino(
preset: BottomBarCupertinoMotion.snappy,
),
body: ListView.builder(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 96),
itemCount: 200,
itemBuilder: (_, i) => ListTile(title: Text('Row $i')),
),
child: Padding(
padding: const EdgeInsets.all(8),
child: BottomBarItems(
children: [
BottomBarItem(
icon: const Icon(Icons.home_outlined),
selectedIcon: const Icon(Icons.home),
label: const Text('Home'),
selected: _index == 0,
onTap: () => setState(() => _index = 0),
),
BottomBarItem(
icon: const Icon(Icons.search_outlined),
selectedIcon: const Icon(Icons.search),
label: const Text('Search'),
selected: _index == 1,
onTap: () => setState(() => _index = 1),
),
BottomBarItem(
icon: const Icon(Icons.person_outline),
selectedIcon: const Icon(Icons.person),
label: const Text('Profile'),
selected: _index == 2,
onTap: () => setState(() => _index = 2),
),
],
),
),
),
);
}
}
That's the canonical shape. Most replacements / migrations end up looking like a variation of this.
Source: codenameakshay/flutter-floating-bottom-bar — distributed by TomeVault.