feat(ui): on a phone, the floating button sits just above the navigation bar, its screen's fade under it

Each tab's screen paints the bar's fade beneath its own button
(FrameFade as its bottomNavigationBar, extendBody), so the button stands
16 pixels above the bar, as Material's, instead of above the fade; the
frame keeps its own fade for screens without one, and gets it back while
a page is pushed over the screen.

Whats-New: On a phone, the + button now sits just above the navigation bar.
Whats-New-De: Auf dem Handy sitzt der +-Knopf jetzt direkt über der Navigationsleiste.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-11-10 00:18:44 +01:00
commit c5eceda383
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
3 changed files with 466 additions and 122 deletions

View file

@ -105,21 +105,20 @@ class _HomeState() extends State<_Home> {
// The keyboard covers the screen rather than shrinking it while the
// assistant's field is open, which lifts itself above it (D69).
resizeToAvoidBottomInset: !composerOpen(context),
// The sections scroll on beneath the Ask field and the navigation bar.
// No field, no slot: an empty one would take the room the body keeps
// for the navigation bar.
// The sections scroll on beneath the Ask field, the fade the screen
// paints (FrameFade, in the bar's slot) and the navigation bar.
extendBody: true,
// Folded into its button at the lower left, as in the Library (decided
// 2026-11-01), clear of the navigation bar and its fade at the foot.
floatingActionButtonLocation: FloatingActionButtonLocation.startFloat,
// 2026-11-01), over the fade above the navigation bar, which the screen
// paints beneath it.
floatingActionButtonLocation: FrameFade.buttonLocation(
context,
FloatingActionButtonLocation.startFloat,
),
floatingActionButton: _arranging || !assistantOn(context)
? null
: Padding(
padding: EdgeInsets.only(
bottom: FlushNavigationBar.buttonLiftOf(context),
),
child: const AssistantButton(topic: AssistantTopicLearning()),
),
: const AssistantButton(topic: AssistantTopicLearning()),
bottomNavigationBar: const FrameFade(),
appBar: _arranging
? AppBar(
backgroundColor: Colors.transparent,

View file

@ -261,20 +261,19 @@ class _LibraryState() extends State<_Library> {
),
],
),
floatingActionButtonLocation: switch ((asks, create)) {
(true, _?) => FloatingActionButtonLocation.centerFloat,
(true, null) => FloatingActionButtonLocation.startFloat,
(false, _) => FloatingActionButtonLocation.endFloat,
},
// Clear of the navigation bar and its fade at the foot on a phone.
floatingActionButton: fab == null
? null
: Padding(
padding: EdgeInsets.only(
bottom: FlushNavigationBar.buttonLiftOf(context),
),
child: fab,
),
// Over the fade above the navigation bar on a phone, which the
// screen paints beneath it, its cards running on beneath both.
extendBody: true,
floatingActionButtonLocation: FrameFade.buttonLocation(
context,
switch ((asks, create)) {
(true, _?) => FloatingActionButtonLocation.centerFloat,
(true, null) => FloatingActionButtonLocation.startFloat,
(false, _) => FloatingActionButtonLocation.endFloat,
},
),
floatingActionButton: fab,
bottomNavigationBar: const FrameFade(),
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [

View file

@ -261,10 +261,12 @@ void main() {
final pill = tester.getRect(find.byKey(FlushNavigationBar.fadeKey));
expect(tester.getBottomLeft(find.byType(LibraryScreen)).dy, 900);
// Before scrolling, which slides it away.
// Over the fade, which the Library paints beneath it, 16 above the bar
// (the developer's review, 9 Nov 2026).
expect(
tester.getRect(find.byTooltip('Create studyset')).bottom,
lessThan(pill.top),
reason: "the Library's button stands clear of the bar",
tester.getRect(find.byKey(FlushNavigationBar.barKey)).top - 16,
reason: "the Library's button stands 16 above the bar",
);
await tester.fling(
find.byType(CustomScrollView),