Add avatar-menu navigation style and inline Android search bar

- New Settings > Appearance > "Navigation menu" toggle
  (SettingsController.navMenuStyle, NooNavMenuStyle.drawer/avatarMenu):
  the hamburger-opens-a-Drawer pattern stays the default, or the avatar
  button opens a full-width dropdown (showAvatarMenu) instead, holding
  the same hidden-tabs + Settings content the drawer does. Removes one
  of the two top-corner targets the shell asks a thumb to reach, with
  no permanent chrome added.
- NooTopBar gains androidTitleReplacement so Android's top bar can swap
  its title for an inline search-field-styled launcher
  (ShellSearchLauncher) when search isn't in the bottom bar, instead of
  a bare search icon beside it.
- ShellAvatarButton's onTap/label are now overridable so AppTopBar can
  repoint it per the new setting without this file depending on
  avatar_menu.dart (which already depends on it for shared helpers).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-30 22:25:04 -04:00
co-authored by Claude Sonnet 5
parent 840ec4c301
commit e6d96a6f9f
11 changed files with 550 additions and 25 deletions
+14
View File
@@ -32,6 +32,20 @@ enum NooNavStyle {
/// trailing padding to clear the bar (see `NooBottomBar.rowHeight`).
enum NooBottomBarStyle { attached, floating }
/// Which widget opens the shell's navigation menu (hidden tabs + Settings)
/// on mobile - user-configurable in Settings (Appearance), same precedent
/// as [NooBottomBarStyle]. [drawer] is the original pattern: a `menu`
/// icon, top-left of the top bar, opens a left-edge `Drawer`. [avatarMenu]
/// drops that icon entirely and repurposes the avatar button - already
/// sitting top-right, already a tap target every user already knows - as
/// the one entry point instead, opening a dropdown anchored below it (see
/// `showAvatarMenu` in `widgets/avatar_menu.dart`) rather than a drawer
/// sliding from the opposite edge. Applies on both platforms, since
/// [NooTopBar]'s `onMenu`/avatar wiring is shared chrome, not
/// Android-specific - only [AppDrawer]'s left-`Drawer` convention is being
/// offered an alternative, not anything platform-only.
enum NooNavMenuStyle { drawer, avatarMenu }
/// One destination in a [NooBottomBar]. Data-agnostic on purpose - the app
/// maps its own tab model (e.g. `AppTab`) onto these.
@immutable
+18 -6
View File
@@ -56,6 +56,15 @@ class NooTopBar extends StatelessWidget implements PreferredSizeWidget {
/// iOS only: the search field shown below the large title.
final Widget? search;
/// Android only: replaces the plain title text with an arbitrary widget
/// (e.g. a search-field-styled launcher) in that same flexible middle
/// slot, between [leading]/the menu button and [actions]. iOS already
/// has a dedicated [search] slot below its large title for this; Android
/// has no large title to put a second row under, so a caller that wants
/// an inline search bar there replaces the title with one instead of
/// adding a row.
final Widget? androidTitleReplacement;
/// Defaults to `bg` - the top bar sits on the screen background.
final Color? backgroundColor;
@@ -67,6 +76,7 @@ class NooTopBar extends StatelessWidget implements PreferredSizeWidget {
this.onMenu,
this.actions = const [],
this.search,
this.androidTitleReplacement,
this.backgroundColor,
});
@@ -179,12 +189,14 @@ class NooTopBar extends StatelessWidget implements PreferredSizeWidget {
children: [
?lead,
Expanded(
child: Text(
title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: NooText.title.copyWith(height: 1, color: colors.fg1),
),
child:
androidTitleReplacement ??
Text(
title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: NooText.title.copyWith(height: 1, color: colors.fg1),
),
),
...actions,
],