App Bar
A bar across the top of a page with an optional leading button, a title heading, actions, and bottom content
An app bar is a header row across the top of a page or pane. It provides slots
for an optional leading widget, page title, header actions, and optional bottom
content such as tabs. RemixAppBar manages toolbar layout, title heading
semantics, and top safe-area insets. The enclosing application manages
navigation, outer page structure, and scrolling.
When to use this
- Page headers: Display the page title and primary actions.
- Detail views: Pair a custom back button with a record title.
- Tabbed pages: Place a tab bar below the title using the
bottomslot. - Pane headers: Title a nested pane or split view with
safeAreadisabled.
RemixAppBar does not provide default navigation buttons or inspect Navigator
or route state. Pass navigation controls explicitly using leading. The app bar
remains static and does not change on scroll.
Basic implementation
Basic implementation
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
class OrdersHeader extends StatelessWidget {
const OrdersHeader({super.key, required this.onBack});
final VoidCallback onBack;
@override
Widget build(BuildContext context) {
return RemixAppBar(
leading: RemixIconButton(
icon: Icons.arrow_back,
semanticLabel: 'Back',
onPressed: onBack,
),
title: 'Orders',
actions: [
RemixIconButton(
icon: Icons.search,
semanticLabel: 'Search orders',
onPressed: () {},
),
],
style: AppBarStyler()
.color(const Color(0xFFFFFFFF))
.toolbarHeight(56)
.spacing(12)
.toolbar(BoxStyler().padding(.horizontal(16)))
.title(TextStyler().fontSize(18).fontWeight(FontWeight.w600)),
);
}
}Placing the bar in a page
Place the bar at the top of a Column with the page body below it. The bar
requires a bounded horizontal width, which Column provides.
By default, safeArea is enabled (true). The container surface extends behind
the top safe-area inset (such as a status bar or display cutout), while toolbar
content renders below it. Wrap the page body in MediaQuery.removePadding with
removeTop: true so scroll views do not apply the top inset twice.
Page frame
import 'package:flutter/widgets.dart';
import 'package:remix/remix.dart';
class OrdersPage extends StatelessWidget {
const OrdersPage({super.key});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const RemixAppBar(title: 'Orders'),
Expanded(
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: ListView(
children: const [Text('Order 1042'), Text('Order 1043')],
),
),
),
],
);
}
}Disable safeArea (safeArea: false) when embedding the bar inside a container
that does not touch the top window edge, such as a sheet or nested pane.
Tabs and other bottom content
The bottom slot displays secondary content below the toolbar, inside the app
bar container surface. To display tabs, wrap both the app bar and page body in
RemixTabs, then place RemixTabBar in bottom.
Tabbed page
import 'package:flutter/widgets.dart';
import 'package:remix/remix.dart';
class AccountPage extends StatefulWidget {
const AccountPage({super.key});
@override
State<AccountPage> createState() => _AccountPageState();
}
class _AccountPageState extends State<AccountPage> {
String _tab = 'profile';
@override
Widget build(BuildContext context) {
return RemixTabs(
selectedTabId: _tab,
onChanged: (id) => setState(() => _tab = id),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
RemixAppBar(
title: 'Account',
bottom: RemixTabBar(
child: Row(
children: [
RemixTab(tabId: 'profile', label: 'Profile'),
RemixTab(tabId: 'billing', label: 'Billing'),
],
),
),
),
Expanded(
child: Column(
children: [
RemixTabView(tabId: 'profile', child: Text('Profile')),
RemixTabView(tabId: 'billing', child: Text('Billing')),
],
),
),
],
),
);
}
}Title and semantics
- Heading node: The title renders as an accessibility heading.
titleprovides both visible text and the accessible label. - Custom titles:
titleChildrenders custom widgets in the title slot and inherits the title text style. Whentitleisnull, assistive technologies preserve the semantics oftitleChild, including interactive controls. - Semantics override: Setting
titleoverrides all child semantics. OmittitlewhentitleChildcontains interactive controls. - Route naming: On Android, Fuchsia, Linux, and Windows, the title names the enclosing route. Screen readers announce this title on page entry. Use
namesRouteto override platform defaults. - Web heading level:
headingLevelconfigures the HTML heading tag level from 1 to 6 on the web. - Accessibility traversal: Screen readers navigate leading, title, and actions in sequential order, including when
centerTitleistrue. - Text truncation: Titles truncate to a single line with an ellipsis unless the style defines custom line limits.
Flutter does not provide a banner or toolbar semantic role, so RemixAppBar
publishes no landmark semantics.
Styling
AppBarStyler exposes these slots:
| Slot | Description |
|---|---|
container | Surface behind the entire bar, including the top window inset |
toolbar | Layout and decoration of the toolbar row, including horizontal content padding |
title | Title typography and default text style for titleChild |
actions | Spacing and alignment for the actions row |
bottom | Layout and decoration for bottom content |
toolbarHeight | Height of the toolbar row in logical pixels. Defaults to 56 |
spacing | Horizontal spacing between leading, title, and action slots |
Styler box methods such as color, padding, and border apply to
container. The actions row lays out with intrinsic width
(MainAxisSize.min). A border on container adds to total bar height, whereas
a border on toolbar renders inside toolbarHeight.
Fluent styling
import 'package:flutter/widgets.dart';
import 'package:remix/remix.dart';
final style = AppBarStyler()
.color(const Color(0xFFFBFBFB))
.toolbarHeight(64)
.spacing(12)
.toolbar(
BoxStyler()
.padding(.horizontal(16))
.border(.bottom(.color(const Color(0xFFE5E5E5)).width(1))),
)
.actions(FlexBoxStyler().spacing(8))
.title(TextStyler().fontSize(18).fontWeight(FontWeight.w600));Fortal recipe
FortalAppBar is the Fortal-themed app bar generated from fortalAppBarStyle.
It configures a solid panel background, a 64px (space9) toolbar height with an
inset bottom border, space4 horizontal padding, and bold size4 title
typography. The toolbar height and border match the sidebar header in
FortalDashboardShell to ensure horizontal alignment across scaling levels.
Fortal app bar
import 'package:flutter/material.dart';
import 'ui/ui.dart';
FortalAppBar buildHeader({required VoidCallback onExport}) => FortalAppBar(
title: 'Orders',
actions: [
FortalButton(label: 'Export', onPressed: onExport),
],
);Constructor
Public API
import 'package:flutter/widgets.dart';
import 'package:remix/remix.dart';
RemixAppBar remixAppBarConstructor({
Key? key,
Widget? leading,
String? title,
Widget? titleChild,
List<Widget> actions = const [],
Widget? bottom,
bool centerTitle = false,
bool safeArea = true,
bool? namesRoute,
int? headingLevel,
AppBarStyler style = const AppBarStyler.create(),
AppBarSpec? styleSpec,
}) => throw UnimplementedError();