diff --git a/docs/pages/docs/header.mdx b/docs/pages/docs/header.mdx
index d842dc704..dea4c9ac6 100644
--- a/docs/pages/docs/header.mdx
+++ b/docs/pages/docs/header.mdx
@@ -12,7 +12,7 @@ It is typically used on pages at the root of the navigation stack.
```dart
FHeader(
- title: 'Edit Alarm',
+ title: const Text('Edit Alarm'),
actions: [
FHeaderAction(
icon: FAssets.icons.alarmClock,
@@ -23,18 +23,48 @@ It is typically used on pages at the root of the navigation stack.
onPress: () {},
),
],
- )
+ );
```
+## Nested Header
+
+It is typically used on nested pages or sheets.
+
+
+
+
+
+
+ ```dart
+ FHeader.nested(
+ title: const Text('Appointment'),
+ leftActions: [
+ FHeaderAction.back(onPress: () {}),
+ ],
+ rightActions: [
+ FHeaderAction(
+ icon: FAssets.icons.info,
+ onPress: () {},
+ ),
+ FHeaderAction(
+ icon: FAssets.icons.plus,
+ onPress: () {},
+ ),
+ ],
+ );
+ ```
+
+
+
## Usage
### `FHeader(...)`
```dart
FHeader(
- title: 'Edit Alarm',
+ title: const Text('Title'),
actions: [
FHeaderAction(
icon: FAssets.icons.alarmClock,
@@ -45,5 +75,103 @@ FHeader(
onPress: () {},
),
],
-)
+);
```
+
+### `FHeader.nested(...)`
+
+```dart
+FHeader.nested(
+ title: const Text('Title'),
+ leftActions: [
+ FHeaderAction.back(onPress: () {}),
+ ],
+ rightActions: [
+ FHeaderAction(
+ icon: FAssets.icons.info,
+ onPress: () {},
+ ),
+ FHeaderAction.x(onPress: () {}),
+ ],
+);
+```
+
+## Examples
+
+### Header
+
+
+
+
+
+ ```dart
+ FHeader(
+ title: const Text('Edit Alarm'),
+ actions: [
+ FHeaderAction(
+ icon: FAssets.icons.alarmClock,
+ onPress: () {},
+ ),
+ FHeaderAction(
+ icon: FAssets.icons.plus,
+ onPress: () {},
+ ),
+ ],
+ );
+ ```
+
+
+
+### Nested Header with back icon
+
+
+
+
+
+ ```dart
+ FHeader.nested(
+ title: const Text('Appointment'),
+ leftActions: [
+ FHeaderAction.back(onPress: () {}),
+ ],
+ rightActions: [
+ FHeaderAction(
+ icon: FAssets.icons.info,
+ onPress: () {},
+ ),
+ FHeaderAction(
+ icon: FAssets.icons.plus,
+ onPress: () {},
+ ),
+ ],
+ );
+ ```
+
+
+
+### Nested Header with X icon
+
+
+
+
+
+ ```dart
+ FHeader.nested(
+ title: const Text('Climate'),
+ leftActions: [
+ FHeaderAction(
+ icon: FAssets.icons.thermometer,
+ onPress: () {},
+ ),
+ FHeaderAction(
+ icon: FAssets.icons.wind,
+ onPress: null,
+ ),
+ ],
+ rightActions: [
+ FHeaderAction.x(onPress: () {}),
+ ],
+ );
+ ```
+
+
diff --git a/docs/pages/docs/nested-header.mdx b/docs/pages/docs/nested-header.mdx
deleted file mode 100644
index 101238060..000000000
--- a/docs/pages/docs/nested-header.mdx
+++ /dev/null
@@ -1,81 +0,0 @@
-import { Tabs } from 'nextra/components';
-import { Widget } from "../../components/widget";
-
-# Nested Header
-A nested header contains the page's title and actions (including pop navigation).
-It is typically used on pages that are not at the root of the navigation stack.
-
-
-
-
-
-
- ```dart
- FNestedHeader(
- title: 'Appointment',
- leftActions: [
- FNestedHeaderAction.back(onPress: () {}),
- ],
- rightActions: [
- FNestedHeaderAction(
- icon: FAssets.icons.info,
- onPress: () {},
- ),
- FNestedHeaderAction(
- icon: FAssets.icons.plus,
- onPress: () {},
- ),
- ],
- );
- ```
-
-
-
-## Usage
-
-### `FNestedHeader(...)`
-
-```dart
-FNestedHeader(
- title: 'Appointment',
- leftActions: [
- FNestedHeaderAction.back(onPress: () {}),
- ],
- rightActions: [
- FNestedHeaderAction(
- icon: FAssets.icons.info,
- onPress: null,
- ),
- FNestedHeaderAction.x(onPress: () {}),
- ],
-);
-```
-
-## Examples
-
-### With X icon to exit
-
-
-
-
-
- ```dart
- FNestedHeader(
- title: 'Climate',
- leftActions: [
- FNestedHeaderAction(
- icon: FAssets.icons.thermometer,
- onPress: () {},
- ),
- FNestedHeaderAction(
- icon: FAssets.icons.wind,
- onPress: null,
- ),
- ],
- rightActions: [
- FNestedHeaderAction.x(onPress: () {}),
- ],
- );
- ```
-
-
diff --git a/docs/pages/docs/scaffold.mdx b/docs/pages/docs/scaffold.mdx
index 2b46303e2..af16760f5 100644
--- a/docs/pages/docs/scaffold.mdx
+++ b/docs/pages/docs/scaffold.mdx
@@ -12,7 +12,7 @@ Creates a visual scaffold for Forui widgets.
```dart
FScaffold(
header: FHeader(
- title: 'Settings',
+ title: const Text('Settings'),
actions: [
FHeaderAction(
icon: FAssets.icons.ellipsis,
@@ -63,7 +63,7 @@ Creates a visual scaffold for Forui widgets.
```dart
FScaffold(
header: FHeader(
- title: 'Settings',
+ title: const Text('Settings'),
actions: [
FHeaderAction(
icon: FAssets.icons.ellipsis,
diff --git a/forui/example/lib/main.dart b/forui/example/lib/main.dart
index 22a3ebafe..c9a91b563 100644
--- a/forui/example/lib/main.dart
+++ b/forui/example/lib/main.dart
@@ -19,7 +19,7 @@ class Application extends StatelessWidget {
data: FThemes.zinc.light,
child: FScaffold(
header: FHeader(
- title: 'Example Example Example Example',
+ title: const Text('Example Example Example Example'),
actions: [
FHeaderAction(
icon: FAssets.icons.plus,
diff --git a/forui/lib/forui.dart b/forui/lib/forui.dart
index 82ce85605..a9e3d7982 100644
--- a/forui/lib/forui.dart
+++ b/forui/lib/forui.dart
@@ -25,7 +25,6 @@ export 'src/widgets/button/button.dart' hide FButtonContent, Variant;
export 'src/widgets/card/card.dart' hide FCardContent;
export 'src/widgets/dialog/dialog.dart' hide FDialogContent, FHorizontalDialogContent, FVerticalDialogContent;
export 'src/widgets/header/header.dart';
-export 'src/widgets/nested_header/nested_header.dart';
export 'src/widgets/tabs/tabs.dart';
export 'src/widgets/text_field/text_field.dart';
export 'src/widgets/scaffold.dart';
diff --git a/forui/lib/src/theme/theme_data.dart b/forui/lib/src/theme/theme_data.dart
index 33b37ff89..0d5f6e41b 100644
--- a/forui/lib/src/theme/theme_data.dart
+++ b/forui/lib/src/theme/theme_data.dart
@@ -40,9 +40,6 @@ final class FThemeData with Diagnosticable {
/// The header styles.
final FHeaderStyle headerStyle;
- /// The nested header styles.
- final FNestedHeaderStyle nestedHeaderStyle;
-
/// The tabs styles.
final FTabsStyle tabsStyle;
@@ -71,7 +68,6 @@ final class FThemeData with Diagnosticable {
required this.cardStyle,
required this.dialogStyle,
required this.headerStyle,
- required this.nestedHeaderStyle,
required this.tabsStyle,
required this.textFieldStyle,
required this.scaffoldStyle,
@@ -98,7 +94,6 @@ final class FThemeData with Diagnosticable {
cardStyle: FCardStyle.inherit(colorScheme: colorScheme, typography: typography, style: style),
dialogStyle: FDialogStyle.inherit(colorScheme: colorScheme, typography: typography, style: style),
headerStyle: FHeaderStyle.inherit(colorScheme: colorScheme, typography: typography, style: style),
- nestedHeaderStyle: FNestedHeaderStyle.inherit(colorScheme: colorScheme, typography: typography, style: style),
tabsStyle: FTabsStyle.inherit(colorScheme: colorScheme, typography: typography, style: style),
textFieldStyle: FTextFieldStyle.inherit(colorScheme: colorScheme, typography: typography, style: style),
scaffoldStyle: FScaffoldStyle.inherit(colorScheme: colorScheme, style: style),
@@ -149,7 +144,6 @@ final class FThemeData with Diagnosticable {
cardStyle: cardStyle ?? this.cardStyle,
dialogStyle: dialogStyle ?? this.dialogStyle,
headerStyle: headerStyle ?? this.headerStyle,
- nestedHeaderStyle: nestedHeaderStyle ?? this.nestedHeaderStyle,
tabsStyle: tabsStyle ?? this.tabsStyle,
textFieldStyle: textFieldStyle ?? this.textFieldStyle,
scaffoldStyle: scaffoldStyle ?? this.scaffoldStyle,
@@ -169,7 +163,6 @@ final class FThemeData with Diagnosticable {
..add(DiagnosticsProperty('cardStyle', cardStyle, level: DiagnosticLevel.debug))
..add(DiagnosticsProperty('dialogStyle', dialogStyle, level: DiagnosticLevel.debug))
..add(DiagnosticsProperty('headerStyle', headerStyle, level: DiagnosticLevel.debug))
- ..add(DiagnosticsProperty('nestedHeaderStyle', nestedHeaderStyle, level: DiagnosticLevel.debug))
..add(DiagnosticsProperty('tabsStyle', tabsStyle, level: DiagnosticLevel.debug))
..add(DiagnosticsProperty('textFieldStyle', textFieldStyle, level: DiagnosticLevel.debug))
..add(DiagnosticsProperty('scaffoldStyle', scaffoldStyle, level: DiagnosticLevel.debug))
@@ -190,7 +183,6 @@ final class FThemeData with Diagnosticable {
cardStyle == other.cardStyle &&
dialogStyle == other.dialogStyle &&
headerStyle == other.headerStyle &&
- nestedHeaderStyle == other.nestedHeaderStyle &&
tabsStyle == other.tabsStyle &&
textFieldStyle == other.textFieldStyle &&
scaffoldStyle == other.scaffoldStyle &&
@@ -207,7 +199,6 @@ final class FThemeData with Diagnosticable {
cardStyle.hashCode ^
dialogStyle.hashCode ^
headerStyle.hashCode ^
- nestedHeaderStyle.hashCode ^
tabsStyle.hashCode ^
textFieldStyle.hashCode ^
scaffoldStyle.hashCode ^
diff --git a/forui/lib/src/widgets/header/header.dart b/forui/lib/src/widgets/header/header.dart
index 63bc7ec72..1a7c32faf 100644
--- a/forui/lib/src/widgets/header/header.dart
+++ b/forui/lib/src/widgets/header/header.dart
@@ -8,166 +8,105 @@ import 'package:forui/src/foundation/tappable.dart';
part 'header_action.dart';
+part 'root_header.dart';
+
+part 'nested_header.dart';
+
/// A header.
///
-/// A header contains the page's title and navigation actions.
-/// It is typically used on pages at the root of the navigation stack.
+/// A header contains the page's title and actions.
+
///
/// See:
/// * https://forui.dev/docs/header for working examples.
/// * [FHeaderStyle] for customizing a header's appearance.
-final class FHeader extends StatelessWidget {
- /// The header's style. Defaults to [FThemeData.headerStyle].
- final FHeaderStyle? style;
+sealed class FHeader extends StatelessWidget {
+ const FHeader._({super.key});
- /// The title, aligned to the left.
+ /// Creates a header.
///
- /// ## Contract:
- /// Throws [AssertionError]:
- /// * if [title] and [rawTitle] are both not null
- /// * if [title] and [rawTitle] are both null
- final String? title;
-
- /// The title, aligned to the left.
+ /// It is typically used on pages at the root of the navigation stack.
+ const factory FHeader({
+ required Widget title,
+ FRootHeaderStyle? style,
+ List actions,
+ Key? key,
+ }) = _FRootHeader;
+
+ /// Creates a nested header.
///
- /// ## Contract:
- /// Throws [AssertionError]:
- /// * if [title] and [rawTitle] are both not null
- /// * if [title] and [rawTitle] are both null
- final Widget? rawTitle;
-
- /// The actions, aligned to the right. Defaults to an empty list.
- final List actions;
-
- /// Creates a [FHeader].
- ///
- /// ## Contract:
- /// Throws [AssertionError]:
- /// * if [title] and [rawTitle] are both not null
- /// * if [title] and [rawTitle] are both null
- const FHeader({
- this.style,
- this.title,
- this.rawTitle,
- this.actions = const [],
- super.key,
- }) : assert((title != null) ^ (rawTitle != null), 'title or rawTitle must be provided, but not both.');
-
- @override
- Widget build(BuildContext context) {
- final style = this.style ?? context.theme.headerStyle;
-
- final title = switch ((this.title, rawTitle)) {
- (final String title, _) => Text(title),
- (_, final Widget title) => title,
- _ => const Placeholder(),
- };
-
- return SafeArea(
- bottom: false,
- child: Padding(
- padding: style.padding,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Expanded(
- child: DefaultTextStyle.merge(
- overflow: TextOverflow.fade,
- maxLines: 1,
- softWrap: false,
- style: style.titleTextStyle,
- child: title,
- ),
- ),
- Row(children: actions.expand((action) => [action, const SizedBox(width: 10)]).toList()),
- ],
- ),
- ),
- );
- }
-
- @override
- void debugFillProperties(DiagnosticPropertiesBuilder properties) {
- super.debugFillProperties(properties);
- properties
- ..add(DiagnosticsProperty('style', style))
- ..add(StringProperty('title', title))
- ..add(IterableProperty('actions', actions));
- }
+ /// It is typically used on pages NOT at the root of the navigation stack.
+ const factory FHeader.nested({
+ required Widget title,
+ FNestedHeaderStyle? style,
+ List leftActions,
+ List rightActions,
+ Key? key,
+ }) = _FNestedHeader;
}
/// [FHeader]'s style.
final class FHeaderStyle with Diagnosticable {
- /// The title's [TextStyle].
- final TextStyle titleTextStyle;
-
- /// The [FHeaderAction]'s style.
- final FHeaderActionStyle actionStyle;
+ /// The root header style.
+ final FRootHeaderStyle rootStyle;
- /// The spacing between [FHeaderAction]s.
- final double actionSpacing;
-
- /// The padding.
- final EdgeInsets padding;
+ /// The nested header style.
+ final FNestedHeaderStyle nestedStyle;
/// Creates a [FHeaderStyle].
- FHeaderStyle({
- required this.titleTextStyle,
- required this.actionStyle,
- required this.actionSpacing,
- required this.padding,
+ const FHeaderStyle({
+ required this.rootStyle,
+ required this.nestedStyle,
});
- /// Creates a [FHeaderStyle] that inherits its properties from the given [FColorScheme] and [FTypography].
+ /// Creates a [FRootHeaderStyle] that inherits its properties from the given [FColorScheme], [FTypography] and [FStyle].
FHeaderStyle.inherit({
required FColorScheme colorScheme,
required FTypography typography,
required FStyle style,
- }) : titleTextStyle = typography.xl3.copyWith(
- color: colorScheme.foreground,
- fontWeight: FontWeight.w700,
- height: 1,
- ),
- actionStyle = FHeaderActionStyle.inherit(colorScheme: colorScheme),
- actionSpacing = 10,
- padding = style.pagePadding.copyWith(bottom: 15);
+ }) : rootStyle = FRootHeaderStyle.inherit(colorScheme: colorScheme, typography: typography, style: style),
+ nestedStyle = FNestedHeaderStyle.inherit(colorScheme: colorScheme, typography: typography, style: style);
/// Returns a copy of this [FHeaderStyle] with the given properties replaced.
///
/// ```dart
/// final style = FHeaderStyle(
- /// titleTextStyle: ...,
- /// action: ...,
+ /// rootStyle: ...,
+ /// nestedStyle: ...,
/// );
///
/// final copy = style.copyWith(
- /// action: ...,
+ /// nestedStyle: ...,
/// );
///
- /// print(style.titleTextStyle == copy.titleTextStyle); // true
- /// print(style.action == copy.action); // false
+ /// print(style.rootStyle == copy.rootStyle); // true
+ /// print(style.nestedStyle == copy.nestedStyle); // false
/// ```
- @useResult
FHeaderStyle copyWith({
- TextStyle? titleTextStyle,
- FHeaderActionStyle? actionStyle,
- double? actionSpacing,
- EdgeInsets? padding,
+ FRootHeaderStyle? rootStyle,
+ FNestedHeaderStyle? nestedStyle,
}) =>
FHeaderStyle(
- titleTextStyle: titleTextStyle ?? this.titleTextStyle,
- actionStyle: actionStyle ?? this.actionStyle,
- actionSpacing: actionSpacing ?? this.actionSpacing,
- padding: padding ?? this.padding,
+ rootStyle: rootStyle ?? this.rootStyle,
+ nestedStyle: nestedStyle ?? this.nestedStyle,
);
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
- ..add(DiagnosticsProperty('titleTextStyle', titleTextStyle))
- ..add(DiagnosticsProperty('action', actionStyle))
- ..add(DiagnosticsProperty('actionSpacing', actionSpacing))
- ..add(DiagnosticsProperty('padding', padding));
+ ..add(DiagnosticsProperty('rootStyle', rootStyle))
+ ..add(DiagnosticsProperty('nestedStyle', nestedStyle));
}
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is FHeaderStyle &&
+ runtimeType == other.runtimeType &&
+ rootStyle == other.rootStyle &&
+ nestedStyle == other.nestedStyle;
+
+ @override
+ int get hashCode => rootStyle.hashCode ^ nestedStyle.hashCode;
}
diff --git a/forui/lib/src/widgets/header/header_action.dart b/forui/lib/src/widgets/header/header_action.dart
index 64ebf5e59..15ed8471a 100644
--- a/forui/lib/src/widgets/header/header_action.dart
+++ b/forui/lib/src/widgets/header/header_action.dart
@@ -29,9 +29,35 @@ class FHeaderAction extends StatelessWidget {
super.key,
});
+ /// Creates a [FHeaderAction] with [FAssets.icons.arrowLeft].
+ factory FHeaderAction.back({
+ required VoidCallback? onPress,
+ FHeaderActionStyle? style,
+ Key? key,
+ }) =>
+ FHeaderAction(
+ icon: FAssets.icons.arrowLeft,
+ onPress: onPress,
+ style: style,
+ key: key,
+ );
+
+ /// Creates a [FHeaderAction] with [FAssets.icons.x].
+ factory FHeaderAction.x({
+ required VoidCallback? onPress,
+ FHeaderActionStyle? style,
+ Key? key,
+ }) =>
+ FHeaderAction(
+ icon: FAssets.icons.x,
+ onPress: onPress,
+ style: style,
+ key: key,
+ );
+
@override
Widget build(BuildContext context) {
- final style = this.style ?? context.theme.headerStyle.actionStyle;
+ final style = FHeaderActionStyle._of(context);
final enabled = onPress != null || onLongPress != null;
return MouseRegion(
@@ -60,27 +86,35 @@ class FHeaderAction extends StatelessWidget {
/// [FHeaderAction]'s style.
class FHeaderActionStyle with Diagnosticable {
+ @useResult
+ static FHeaderActionStyle _of(BuildContext context) =>
+ context.dependOnInheritedWidgetOfExactType<_InheritedActionStyle>()?.style ??
+ context.theme.headerStyle.rootStyle.actionStyle;
+
/// The icon's color when this action is enabled.
final Color enabledColor;
/// The icon's color when this action is disabled.
final Color disabledColor;
- /// The icon's size. Defaults to 30.
+ /// The icon's size.
+ ///
+ /// Defaults to:
+ /// * 30 for [FHeader].
+ /// * 25 for [FHeader.nested]
final double size;
/// Creates a [FHeaderActionStyle].
FHeaderActionStyle({
required this.enabledColor,
required this.disabledColor,
- this.size = 30,
+ required this.size,
});
/// Creates a [FHeaderActionStyle] that inherits its properties from the given [FColorScheme].
- FHeaderActionStyle.inherit({required FColorScheme colorScheme})
+ FHeaderActionStyle.inherit({required FColorScheme colorScheme, required this.size})
: enabledColor = colorScheme.foreground,
- disabledColor = colorScheme.foreground.withOpacity(0.5),
- size = 30;
+ disabledColor = colorScheme.foreground.withOpacity(0.5);
/// Returns a copy of this [FHeaderActionStyle] with the given properties replaced.
///
@@ -116,6 +150,24 @@ class FHeaderActionStyle with Diagnosticable {
properties
..add(ColorProperty('enabledColor', enabledColor))
..add(ColorProperty('disabledColor', disabledColor))
- ..add(DoubleProperty('size', size, defaultValue: 30));
+ ..add(DoubleProperty('size', size));
+ }
+}
+
+class _InheritedActionStyle extends InheritedWidget {
+ final FHeaderActionStyle style;
+
+ const _InheritedActionStyle({
+ required this.style,
+ required super.child,
+ });
+
+ @override
+ bool updateShouldNotify(_InheritedActionStyle oldWidget) => style != oldWidget.style;
+
+ @override
+ void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+ super.debugFillProperties(properties);
+ properties.add(DiagnosticsProperty('style', style));
}
}
diff --git a/forui/lib/src/widgets/nested_header/nested_header.dart b/forui/lib/src/widgets/header/nested_header.dart
similarity index 62%
rename from forui/lib/src/widgets/nested_header/nested_header.dart
rename to forui/lib/src/widgets/header/nested_header.dart
index c4caaa9c3..93d81c196 100644
--- a/forui/lib/src/widgets/nested_header/nested_header.dart
+++ b/forui/lib/src/widgets/header/nested_header.dart
@@ -1,12 +1,4 @@
-import 'package:flutter/foundation.dart';
-import 'package:flutter/widgets.dart';
-
-import 'package:meta/meta.dart';
-
-import 'package:forui/forui.dart';
-import 'package:forui/src/foundation/tappable.dart';
-
-part 'nested_header_action.dart';
+part of 'header.dart';
/// A nested header.
///
@@ -14,27 +6,14 @@ part 'nested_header_action.dart';
/// It is typically used on pages that are not at the root of the navigation stack.
///
/// See:
-/// * https://forui.dev/docs/nested-header for working examples.
+/// * https://forui.dev/docs/header for working examples.
/// * [FNestedHeaderStyle] for customizing a header's appearance.
-final class FNestedHeader extends StatelessWidget {
- /// The style. Defaults to [FThemeData.nestedHeaderStyle].
+final class _FNestedHeader extends FHeader {
+ /// The style. Defaults to [FThemeData.headerStyle.nestedStyle].
final FNestedHeaderStyle? style;
/// The title, aligned to the center.
- ///
- /// ## Contract:
- /// Throws [AssertionError]:
- /// * if [title] and [rawTitle] are both not null
- /// * if [title] and [rawTitle] are both null
- final String? title;
-
- /// The title, aligned to the center.
- ///
- /// ## Contract:
- /// Throws [AssertionError]:
- /// * if [title] and [rawTitle] are both not null
- /// * if [title] and [rawTitle] are both null
- final Widget? rawTitle;
+ final Widget title;
/// The actions, aligned to the left. Defaults to an empty list.
final List leftActions;
@@ -42,29 +21,18 @@ final class FNestedHeader extends StatelessWidget {
/// The actions, aligned to the right. Defaults to an empty list.
final List rightActions;
- /// Creates a [FNestedHeader].
- ///
- /// ## Contract:
- /// Throws [AssertionError] if:
- /// * [title] and [rawTitle] are both not null.
- const FNestedHeader({
+ /// Creates a [_FNestedHeader].
+ const _FNestedHeader({
+ required this.title,
this.style,
- this.title,
- this.rawTitle,
this.leftActions = const [],
this.rightActions = const [],
super.key,
- }) : assert((title != null) ^ (rawTitle != null), 'title or rawTitle must be provided, but not both.');
+ }) : super._();
@override
Widget build(BuildContext context) {
- final style = this.style ?? context.theme.nestedHeaderStyle;
-
- final title = switch ((this.title, rawTitle)) {
- (final String title, _) => Text(title),
- (_, final Widget title) => title,
- _ => const Placeholder(),
- };
+ final style = this.style ?? context.theme.headerStyle.nestedStyle;
return SafeArea(
bottom: false,
@@ -72,12 +40,15 @@ final class FNestedHeader extends StatelessWidget {
padding: style.padding,
child: Stack(
children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Row(children: leftActions.expand((action) => [action, const SizedBox(width: 10)]).toList()),
- Row(children: rightActions.expand((action) => [const SizedBox(width: 10), action]).toList()),
- ],
+ _InheritedActionStyle(
+ style: style.actionStyle,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(children: leftActions.expand((action) => [action, const SizedBox(width: 10)]).toList()),
+ Row(children: rightActions.expand((action) => [const SizedBox(width: 10), action]).toList()),
+ ],
+ ),
),
Positioned.fill(
child: Align(
@@ -101,18 +72,18 @@ final class FNestedHeader extends StatelessWidget {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('style', style))
- ..add(StringProperty('title', title))
- ..add(DiagnosticsProperty('rawTitle', rawTitle));
+ ..add(IterableProperty('leftActions', leftActions))
+ ..add(IterableProperty('rightActions', rightActions));
}
}
-/// [FNestedHeader]'s style.
+/// [_FNestedHeader]'s style.
final class FNestedHeaderStyle with Diagnosticable {
/// The title's [TextStyle].
final TextStyle titleTextStyle;
- /// The [FHeaderAction]'s style for [FNestedHeader.leftActions] and [FNestedHeader.rightActions].
- final FNestedHeaderActionStyle actionStyle;
+ /// The [FHeaderAction]'s style for [_FNestedHeader.leftActions] and [_FNestedHeader.rightActions].
+ final FHeaderActionStyle actionStyle;
/// The spacing between [FHeaderAction]s.
final double actionSpacing;
@@ -128,7 +99,7 @@ final class FNestedHeaderStyle with Diagnosticable {
required this.padding,
});
- /// Creates a [FNestedHeaderStyle] that inherits its properties from the given [FColorScheme] and [FTypography].
+ /// Creates a [FNestedHeaderStyle] that inherits its properties from the given [FColorScheme], [FTypography] and [FStyle].
FNestedHeaderStyle.inherit({
required FColorScheme colorScheme,
required FTypography typography,
@@ -138,7 +109,7 @@ final class FNestedHeaderStyle with Diagnosticable {
fontWeight: FontWeight.w600,
height: 1,
),
- actionStyle = FNestedHeaderActionStyle.inherit(colorScheme: colorScheme),
+ actionStyle = FHeaderActionStyle.inherit(colorScheme: colorScheme, size: 25),
actionSpacing = 10,
padding = style.pagePadding.copyWith(bottom: 15);
@@ -159,7 +130,7 @@ final class FNestedHeaderStyle with Diagnosticable {
/// ```
FNestedHeaderStyle copyWith({
TextStyle? titleTextStyle,
- FNestedHeaderActionStyle? actionStyle,
+ FHeaderActionStyle? actionStyle,
double? actionSpacing,
EdgeInsets? padding,
}) =>
@@ -179,4 +150,17 @@ final class FNestedHeaderStyle with Diagnosticable {
..add(DiagnosticsProperty('actionSpacing', actionSpacing))
..add(DiagnosticsProperty('padding', padding));
}
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is FNestedHeaderStyle &&
+ runtimeType == other.runtimeType &&
+ titleTextStyle == other.titleTextStyle &&
+ actionStyle == other.actionStyle &&
+ actionSpacing == other.actionSpacing &&
+ padding == other.padding;
+
+ @override
+ int get hashCode => titleTextStyle.hashCode ^ actionStyle.hashCode ^ actionSpacing.hashCode ^ padding.hashCode;
}
diff --git a/forui/lib/src/widgets/header/root_header.dart b/forui/lib/src/widgets/header/root_header.dart
new file mode 100644
index 000000000..76ae91489
--- /dev/null
+++ b/forui/lib/src/widgets/header/root_header.dart
@@ -0,0 +1,156 @@
+part of 'header.dart';
+
+/// A root header.
+///
+/// A root header contains the page's title and actions.
+/// It is typically used on pages at the root of the navigation stack.
+///
+/// See:
+/// * https://forui.dev/docs/header for working examples.
+/// * [FRootHeaderStyle] for customizing a header's appearance.
+final class _FRootHeader extends FHeader {
+ /// The header's style. Defaults to [FThemeData.headerStyle].
+ final FRootHeaderStyle? style;
+
+ /// The title, aligned to the left.
+ final Widget title;
+
+ /// The actions, aligned to the right. Defaults to an empty list.
+ final List actions;
+
+ /// Creates a [FHeader].
+ const _FRootHeader({
+ required this.title,
+ this.style,
+ this.actions = const [],
+ super.key,
+ }) : super._();
+
+ @override
+ Widget build(BuildContext context) {
+ final style = this.style ?? context.theme.headerStyle.rootStyle;
+
+ return SafeArea(
+ bottom: false,
+ child: Padding(
+ padding: style.padding,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: DefaultTextStyle.merge(
+ overflow: TextOverflow.fade,
+ maxLines: 1,
+ softWrap: false,
+ style: style.titleTextStyle,
+ child: title,
+ ),
+ ),
+ _InheritedActionStyle(
+ style: style.actionStyle,
+ child: Row(children: actions.expand((action) => [action, const SizedBox(width: 10)]).toList()),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ @override
+ void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+ super.debugFillProperties(properties);
+ properties
+ ..add(DiagnosticsProperty('style', style))
+ ..add(DiagnosticsProperty('title', title))
+ ..add(IterableProperty('actions', actions));
+ }
+}
+
+/// [_FRootHeader]'s style.
+final class FRootHeaderStyle with Diagnosticable {
+ /// The title's [TextStyle].
+ final TextStyle titleTextStyle;
+
+ /// The [FHeaderAction]'s style.
+ final FHeaderActionStyle actionStyle;
+
+ /// The spacing between [FHeaderAction]s.
+ final double actionSpacing;
+
+ /// The padding.
+ final EdgeInsets padding;
+
+ /// Creates a [FRootHeaderStyle].
+ FRootHeaderStyle({
+ required this.titleTextStyle,
+ required this.actionStyle,
+ required this.actionSpacing,
+ required this.padding,
+ });
+
+ /// Creates a [FRootHeaderStyle] that inherits its properties from the given [FColorScheme], [FTypography] and [FStyle].
+ FRootHeaderStyle.inherit({
+ required FColorScheme colorScheme,
+ required FTypography typography,
+ required FStyle style,
+ }) : titleTextStyle = typography.xl3.copyWith(
+ color: colorScheme.foreground,
+ fontWeight: FontWeight.w700,
+ height: 1,
+ ),
+ actionStyle = FHeaderActionStyle.inherit(colorScheme: colorScheme, size: 30),
+ actionSpacing = 10,
+ padding = style.pagePadding.copyWith(bottom: 15);
+
+ /// Returns a copy of this [FRootHeaderStyle] with the given properties replaced.
+ ///
+ /// ```dart
+ /// final style = FHeaderStyle(
+ /// titleTextStyle: ...,
+ /// action: ...,
+ /// );
+ ///
+ /// final copy = style.copyWith(
+ /// action: ...,
+ /// );
+ ///
+ /// print(style.titleTextStyle == copy.titleTextStyle); // true
+ /// print(style.action == copy.action); // false
+ /// ```
+ @useResult
+ FRootHeaderStyle copyWith({
+ TextStyle? titleTextStyle,
+ FHeaderActionStyle? actionStyle,
+ double? actionSpacing,
+ EdgeInsets? padding,
+ }) =>
+ FRootHeaderStyle(
+ titleTextStyle: titleTextStyle ?? this.titleTextStyle,
+ actionStyle: actionStyle ?? this.actionStyle,
+ actionSpacing: actionSpacing ?? this.actionSpacing,
+ padding: padding ?? this.padding,
+ );
+
+ @override
+ void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+ super.debugFillProperties(properties);
+ properties
+ ..add(DiagnosticsProperty('titleTextStyle', titleTextStyle))
+ ..add(DiagnosticsProperty('action', actionStyle))
+ ..add(DiagnosticsProperty('actionSpacing', actionSpacing))
+ ..add(DiagnosticsProperty('padding', padding));
+ }
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is FRootHeaderStyle &&
+ runtimeType == other.runtimeType &&
+ titleTextStyle == other.titleTextStyle &&
+ actionStyle == other.actionStyle &&
+ actionSpacing == other.actionSpacing &&
+ padding == other.padding;
+
+ @override
+ int get hashCode => titleTextStyle.hashCode ^ actionStyle.hashCode ^ actionSpacing.hashCode ^ padding.hashCode;
+}
diff --git a/forui/lib/src/widgets/nested_header/nested_header_action.dart b/forui/lib/src/widgets/nested_header/nested_header_action.dart
deleted file mode 100644
index 9cdc26772..000000000
--- a/forui/lib/src/widgets/nested_header/nested_header_action.dart
+++ /dev/null
@@ -1,147 +0,0 @@
-part of 'nested_header.dart';
-
-/// A [FNestedHeader] action.
-///
-/// If the [onPress] and [onLongPress] callbacks are null, then this action will be disabled, it will not react to touch.
-class FNestedHeaderAction extends StatelessWidget {
- /// The style.
- final FNestedHeaderActionStyle? style;
-
- /// The icon.
- final SvgAsset icon;
-
- /// A callback for when the button is pressed.
- ///
- /// The action will be disabled if both [onPress] and [onLongPress] are null.
- final VoidCallback? onPress;
-
- /// A callback for when the button is long pressed.
- ///
- /// The action will be disabled if both [onPress] and [onLongPress] are null.
- final VoidCallback? onLongPress;
-
- /// Creates a [FNestedHeaderAction] from the given SVG [icon].
- const FNestedHeaderAction({
- required this.icon,
- required this.onPress,
- this.onLongPress,
- this.style,
- super.key,
- });
-
- /// Creates a [FNestedHeaderAction] with [FAssets.icons.arrowLeft].
- factory FNestedHeaderAction.back({
- required VoidCallback? onPress,
- FNestedHeaderActionStyle? style,
- Key? key,
- }) =>
- FNestedHeaderAction(
- icon: FAssets.icons.arrowLeft,
- onPress: onPress,
- style: style,
- key: key,
- );
-
- /// Creates a [FNestedHeaderAction] with [FAssets.icons.x].
- factory FNestedHeaderAction.x({
- required VoidCallback? onPress,
- FNestedHeaderActionStyle? style,
- Key? key,
- }) =>
- FNestedHeaderAction(
- icon: FAssets.icons.x,
- onPress: onPress,
- style: style,
- key: key,
- );
-
- @override
- Widget build(BuildContext context) {
- final style = this.style ?? context.theme.nestedHeaderStyle.actionStyle;
- final enabled = onPress != null || onLongPress != null;
-
- return MouseRegion(
- cursor: enabled ? SystemMouseCursors.click : MouseCursor.defer,
- child: FTappable(
- onTap: onPress,
- onLongPress: onLongPress,
- child: icon(
- height: style.size,
- colorFilter: ColorFilter.mode(onPress == null ? style.disabledColor : style.enabledColor, BlendMode.srcIn),
- ),
- ),
- );
- }
-
- @override
- void debugFillProperties(DiagnosticPropertiesBuilder properties) {
- super.debugFillProperties(properties);
- properties
- ..add(DiagnosticsProperty('style', style))
- ..add(DiagnosticsProperty('icon', icon))
- ..add(DiagnosticsProperty('onPress', onPress))
- ..add(DiagnosticsProperty('onLongPress', onLongPress));
- }
-}
-
-/// [FNestedHeaderAction]'s style.
-class FNestedHeaderActionStyle with Diagnosticable {
- /// The icon's color when this action is enabled.
- final Color enabledColor;
-
- /// The icon's color when this action is disabled.
- final Color disabledColor;
-
- /// The icon's size. Defaults to 20.
- final double size;
-
- /// Creates a [FNestedHeaderActionStyle].
- FNestedHeaderActionStyle({
- required this.enabledColor,
- required this.disabledColor,
- this.size = 25,
- });
-
- /// Creates a [FNestedHeaderActionStyle] that inherits its properties from the given [FColorScheme].
- FNestedHeaderActionStyle.inherit({required FColorScheme colorScheme})
- : enabledColor = colorScheme.foreground,
- disabledColor = colorScheme.foreground.withOpacity(0.5),
- size = 25;
-
- /// Returns a copy of this [FNestedHeaderActionStyle] with the given properties replaced.
- ///
- /// ```dart
- /// final style = FNestedHeaderActionStyle(
- /// enabledColor: Colors.black,
- /// disabledColor: Colors.white,
- /// );
- ///
- /// final copy = style.copyWith(
- /// disabledColor: Colors.blue,
- /// );
- ///
- /// print(copy.enabledColor); // black
- /// print(copy.disabledColor); // blue
- /// ```
- @useResult
- FNestedHeaderActionStyle copyWith({
- Color? enabledColor,
- Color? disabledColor,
- double? size,
- EdgeInsets? padding,
- }) =>
- FNestedHeaderActionStyle(
- enabledColor: enabledColor ?? this.enabledColor,
- disabledColor: disabledColor ?? this.disabledColor,
- size: size ?? this.size,
- );
-
- @override
- void debugFillProperties(DiagnosticPropertiesBuilder properties) {
- super.debugFillProperties(properties);
- properties
- ..add(ColorProperty('enabledColor', enabledColor))
- ..add(ColorProperty('disabledColor', disabledColor))
- ..add(DoubleProperty('size', size, defaultValue: 30));
- }
-}
diff --git a/forui/test/golden/header/nested/zinc-dark-header.png b/forui/test/golden/header/nested/zinc-dark-header.png
new file mode 100644
index 000000000..4e5ff5981
Binary files /dev/null and b/forui/test/golden/header/nested/zinc-dark-header.png differ
diff --git a/forui/test/golden/nested-header/zinc-light-header.png b/forui/test/golden/header/nested/zinc-light-header.png
similarity index 50%
rename from forui/test/golden/nested-header/zinc-light-header.png
rename to forui/test/golden/header/nested/zinc-light-header.png
index 6b54dac10..a6207a84a 100644
Binary files a/forui/test/golden/nested-header/zinc-light-header.png and b/forui/test/golden/header/nested/zinc-light-header.png differ
diff --git a/forui/test/golden/header/root/zinc-dark-header.png b/forui/test/golden/header/root/zinc-dark-header.png
new file mode 100644
index 000000000..51bd97489
Binary files /dev/null and b/forui/test/golden/header/root/zinc-dark-header.png differ
diff --git a/forui/test/golden/header/root/zinc-light-header.png b/forui/test/golden/header/root/zinc-light-header.png
new file mode 100644
index 000000000..cf92e2963
Binary files /dev/null and b/forui/test/golden/header/root/zinc-light-header.png differ
diff --git a/forui/test/golden/header/zinc-dark-header.png b/forui/test/golden/header/zinc-dark-header.png
deleted file mode 100644
index 784f4f27b..000000000
Binary files a/forui/test/golden/header/zinc-dark-header.png and /dev/null differ
diff --git a/forui/test/golden/header/zinc-dark-raw-title.png b/forui/test/golden/header/zinc-dark-raw-title.png
deleted file mode 100644
index f8f0847cf..000000000
Binary files a/forui/test/golden/header/zinc-dark-raw-title.png and /dev/null differ
diff --git a/forui/test/golden/header/zinc-light-header.png b/forui/test/golden/header/zinc-light-header.png
deleted file mode 100644
index 8e6e820e4..000000000
Binary files a/forui/test/golden/header/zinc-light-header.png and /dev/null differ
diff --git a/forui/test/golden/header/zinc-light-raw-title.png b/forui/test/golden/header/zinc-light-raw-title.png
deleted file mode 100644
index 88810b55a..000000000
Binary files a/forui/test/golden/header/zinc-light-raw-title.png and /dev/null differ
diff --git a/forui/test/golden/nested-header/zinc-dark-header.png b/forui/test/golden/nested-header/zinc-dark-header.png
deleted file mode 100644
index 5404af175..000000000
Binary files a/forui/test/golden/nested-header/zinc-dark-header.png and /dev/null differ
diff --git a/forui/test/golden/nested-header/zinc-dark-raw-title.png b/forui/test/golden/nested-header/zinc-dark-raw-title.png
deleted file mode 100644
index 9eea2c830..000000000
Binary files a/forui/test/golden/nested-header/zinc-dark-raw-title.png and /dev/null differ
diff --git a/forui/test/golden/nested-header/zinc-light-raw-title.png b/forui/test/golden/nested-header/zinc-light-raw-title.png
deleted file mode 100644
index fdcb32118..000000000
Binary files a/forui/test/golden/nested-header/zinc-light-raw-title.png and /dev/null differ
diff --git a/forui/test/src/widgets/header/header_test.dart b/forui/test/src/widgets/header/header_test.dart
deleted file mode 100644
index 8b7c9b5d2..000000000
--- a/forui/test/src/widgets/header/header_test.dart
+++ /dev/null
@@ -1,39 +0,0 @@
-import 'package:flutter/widgets.dart';
-
-import 'package:flutter_test/flutter_test.dart';
-
-import 'package:forui/forui.dart';
-
-void main() {
- group('FHeader', () {
- for (final (title, rawTitle) in [
- ('', null),
- (null, const SizedBox()),
- ]) {
- testWidgets('constructor does not throw error', (tester) async {
- expect(
- () => FHeader(
- title: title,
- rawTitle: rawTitle,
- ),
- returnsNormally,
- );
- });
- }
-
- for (final (title, rawTitle) in [
- (null, null),
- ('', const SizedBox()),
- ]) {
- testWidgets('constructor throws error', (tester) async {
- expect(
- () => FHeader(
- title: title,
- rawTitle: rawTitle,
- ),
- throwsAssertionError,
- );
- });
- }
- });
-}
diff --git a/forui/test/src/widgets/header/nested_header_golden_test.dart b/forui/test/src/widgets/header/nested_header_golden_test.dart
new file mode 100644
index 000000000..bca8ffa74
--- /dev/null
+++ b/forui/test/src/widgets/header/nested_header_golden_test.dart
@@ -0,0 +1,48 @@
+@Tags(['golden'])
+library;
+
+import 'package:flutter/widgets.dart';
+
+import 'package:flutter_test/flutter_test.dart';
+
+import 'package:forui/forui.dart';
+import '../../test_scaffold.dart';
+
+void main() {
+ group('FNestedHeader', () {
+ for (final (name, theme, _) in TestScaffold.themes) {
+ testWidgets('$name with FNestedHeader actions', (tester) async {
+ await tester.pumpWidget(
+ TestScaffold(
+ data: theme,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 20),
+ child: FHeader.nested(
+ title: const Text('Title'),
+ leftActions: [
+ FHeaderAction.back(onPress: () {}),
+ FHeaderAction(
+ icon: FAssets.icons.alarmClock,
+ onPress: null,
+ ),
+ ],
+ rightActions: [
+ FHeaderAction(
+ icon: FAssets.icons.plus,
+ onPress: () {},
+ ),
+ FHeaderAction.x(onPress: () {}),
+ ],
+ ),
+ ),
+ ),
+ );
+
+ await expectLater(
+ find.byType(TestScaffold),
+ matchesGoldenFile('header/nested/$name-header.png'),
+ );
+ });
+ }
+ });
+}
diff --git a/forui/test/src/widgets/header/header_golden_test.dart b/forui/test/src/widgets/header/root_header_golden_test.dart
similarity index 54%
rename from forui/test/src/widgets/header/header_golden_test.dart
rename to forui/test/src/widgets/header/root_header_golden_test.dart
index 46b7e9f11..bc1645ad4 100644
--- a/forui/test/src/widgets/header/header_golden_test.dart
+++ b/forui/test/src/widgets/header/root_header_golden_test.dart
@@ -12,16 +12,16 @@ const title =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.';
void main() {
- group('FHeader', () {
+ group('FRootHeader', () {
for (final (name, theme, _) in TestScaffold.themes) {
- testWidgets('$name with FHeader actions', (tester) async {
+ testWidgets('$name with FRootHeader actions', (tester) async {
await tester.pumpWidget(
TestScaffold(
data: theme,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: FHeader(
- title: title,
+ title: const Text(title),
actions: [
FHeaderAction(
icon: FAssets.icons.alarmClock,
@@ -39,36 +39,7 @@ void main() {
await expectLater(
find.byType(TestScaffold),
- matchesGoldenFile('header/$name-header.png'),
- );
- });
-
- testWidgets('$name with raw title', (tester) async {
- await tester.pumpWidget(
- TestScaffold(
- data: theme,
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 20),
- child: FHeader(
- rawTitle: const Text('Title'),
- actions: [
- FHeaderAction(
- icon: FAssets.icons.alarmClock,
- onPress: null,
- ),
- FHeaderAction(
- icon: FAssets.icons.plus,
- onPress: () {},
- ),
- ],
- ),
- ),
- ),
- );
-
- await expectLater(
- find.byType(TestScaffold),
- matchesGoldenFile('header/$name-raw-title.png'),
+ matchesGoldenFile('header/root/$name-header.png'),
);
});
}
diff --git a/forui/test/src/widgets/nested_header/nested_header_golden_test.dart b/forui/test/src/widgets/nested_header/nested_header_golden_test.dart
deleted file mode 100644
index 674fd1447..000000000
--- a/forui/test/src/widgets/nested_header/nested_header_golden_test.dart
+++ /dev/null
@@ -1,79 +0,0 @@
-@Tags(['golden'])
-library;
-
-import 'package:flutter/widgets.dart';
-
-import 'package:flutter_test/flutter_test.dart';
-
-import 'package:forui/forui.dart';
-import '../../test_scaffold.dart';
-
-void main() {
- group('FNestedHeader', () {
- for (final (name, theme, _) in TestScaffold.themes) {
- testWidgets('$name with FNestedHeader actions', (tester) async {
- await tester.pumpWidget(
- TestScaffold(
- data: theme,
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 20),
- child: FNestedHeader(
- title: 'Title',
- rightActions: [
- FNestedHeaderAction(
- icon: FAssets.icons.alarmClock,
- onPress: null,
- ),
- FNestedHeaderAction(
- icon: FAssets.icons.plus,
- onPress: () {},
- ),
- ],
- leftActions: [
- FNestedHeaderAction.back(onPress: () {}),
- ],
- ),
- ),
- ),
- );
-
- await expectLater(
- find.byType(TestScaffold),
- matchesGoldenFile('nested-header/$name-header.png'),
- );
- });
-
- testWidgets('$name with raw title', (tester) async {
- await tester.pumpWidget(
- TestScaffold(
- data: theme,
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 20),
- child: FNestedHeader(
- rawTitle: const Text('Title'),
- leftActions: [
- FNestedHeaderAction(
- icon: FAssets.icons.alarmClock,
- onPress: () {},
- ),
- FNestedHeaderAction(
- icon: FAssets.icons.cookingPot,
- onPress: null,
- ),
- ],
- rightActions: [
- FNestedHeaderAction.x(onPress: () {}),
- ],
- ),
- ),
- ),
- );
-
- await expectLater(
- find.byType(TestScaffold),
- matchesGoldenFile('nested-header/$name-raw-title.png'),
- );
- });
- }
- });
-}
diff --git a/forui/test/src/widgets/nested_header/nested_header_test.dart b/forui/test/src/widgets/nested_header/nested_header_test.dart
deleted file mode 100644
index a70b8875b..000000000
--- a/forui/test/src/widgets/nested_header/nested_header_test.dart
+++ /dev/null
@@ -1,39 +0,0 @@
-import 'package:flutter/widgets.dart';
-
-import 'package:flutter_test/flutter_test.dart';
-
-import 'package:forui/forui.dart';
-
-void main() {
- group('FNestedHeader', () {
- for (final (title, rawTitle) in [
- ('', null),
- (null, const SizedBox()),
- ]) {
- testWidgets('constructor does not throw error', (tester) async {
- expect(
- () => FNestedHeader(
- title: title,
- rawTitle: rawTitle,
- ),
- returnsNormally,
- );
- });
- }
-
- for (final (title, rawTitle) in [
- (null, null),
- ('', const SizedBox()),
- ]) {
- testWidgets('constructor throws error', (tester) async {
- expect(
- () => FNestedHeader(
- title: title,
- rawTitle: rawTitle,
- ),
- throwsAssertionError,
- );
- });
- }
- });
-}
diff --git a/samples/lib/main.dart b/samples/lib/main.dart
index 08ce6eab2..acb2aac51 100644
--- a/samples/lib/main.dart
+++ b/samples/lib/main.dart
@@ -60,14 +60,14 @@ class _AppRouter extends $_AppRouter {
),
AutoRoute(
path: '/header/default',
- page: HeaderRoute.page,
+ page: RootHeaderRoute.page,
),
AutoRoute(
- path: '/nested-header/default',
+ path: '/header/nested',
page: NestedHeaderRoute.page,
),
AutoRoute(
- path: '/nested-header/x',
+ path: '/header/nested-x',
page: XNestedHeaderRoute.page,
),
AutoRoute(
diff --git a/samples/lib/widgets/header.dart b/samples/lib/widgets/header.dart
index ce3324781..0bb8f3c7e 100644
--- a/samples/lib/widgets/header.dart
+++ b/samples/lib/widgets/header.dart
@@ -6,14 +6,14 @@ import 'package:forui/forui.dart';
import 'package:forui_samples/sample_scaffold.dart';
@RoutePage()
-class HeaderPage extends SampleScaffold {
- HeaderPage({
+class RootHeaderPage extends SampleScaffold {
+ RootHeaderPage({
@queryParam super.theme,
});
@override
Widget child(BuildContext context) => FHeader(
- title: 'Edit Alarm',
+ title: const Text('Edit Alarm'),
actions: [
FHeaderAction(
icon: FAssets.icons.alarmClock,
@@ -26,3 +26,53 @@ class HeaderPage extends SampleScaffold {
],
);
}
+
+@RoutePage()
+class NestedHeaderPage extends SampleScaffold {
+ NestedHeaderPage({
+ @queryParam super.theme,
+ });
+
+ @override
+ Widget child(BuildContext context) => FHeader.nested(
+ title: const Text('Appointment'),
+ leftActions: [
+ FHeaderAction.back(onPress: () {}),
+ ],
+ rightActions: [
+ FHeaderAction(
+ icon: FAssets.icons.info,
+ onPress: () {},
+ ),
+ FHeaderAction(
+ icon: FAssets.icons.plus,
+ onPress: () {},
+ ),
+ ],
+ );
+}
+
+@RoutePage()
+class XNestedHeaderPage extends SampleScaffold {
+ XNestedHeaderPage({
+ @queryParam super.theme,
+ });
+
+ @override
+ Widget child(BuildContext context) => FHeader.nested(
+ title: const Text('Climate'),
+ leftActions: [
+ FHeaderAction(
+ icon: FAssets.icons.thermometer,
+ onPress: () {},
+ ),
+ FHeaderAction(
+ icon: FAssets.icons.wind,
+ onPress: null,
+ ),
+ ],
+ rightActions: [
+ FHeaderAction.x(onPress: () {}),
+ ],
+ );
+}
diff --git a/samples/lib/widgets/nested_header.dart b/samples/lib/widgets/nested_header.dart
deleted file mode 100644
index e713ee1b8..000000000
--- a/samples/lib/widgets/nested_header.dart
+++ /dev/null
@@ -1,75 +0,0 @@
-import 'package:flutter/widgets.dart';
-
-import 'package:auto_route/auto_route.dart';
-import 'package:forui/forui.dart';
-
-import 'package:forui_samples/sample_scaffold.dart';
-
-@RoutePage()
-class NestedHeaderPage extends SampleScaffold {
- NestedHeaderPage({
- @queryParam super.theme,
- });
-
- @override
- Widget child(BuildContext context) => FNestedHeader(
- title: 'Appointment',
- leftActions: [
- FNestedHeaderAction.back(onPress: () {}),
- ],
- rightActions: [
- FNestedHeaderAction(
- icon: FAssets.icons.info,
- onPress: () {},
- ),
- FNestedHeaderAction(
- icon: FAssets.icons.plus,
- onPress: () {},
- ),
- ],
- );
-}
-
-@RoutePage()
-class XNestedHeaderPage extends SampleScaffold {
- XNestedHeaderPage({
- @queryParam super.theme,
- });
-
- @override
- Widget child(BuildContext context) => FNestedHeader(
- title: 'Climate',
- leftActions: [
- FNestedHeaderAction(
- icon: FAssets.icons.thermometer,
- onPress: () {},
- ),
- FNestedHeaderAction(
- icon: FAssets.icons.wind,
- onPress: null,
- ),
- ],
- rightActions: [
- FNestedHeaderAction.x(onPress: () {}),
- ],
- );
-}
-
-void t() {
-FNestedHeader(
- title: 'Climate',
- leftActions: [
- FNestedHeaderAction(
- icon: FAssets.icons.thermometer,
- onPress: () {},
- ),
- FNestedHeaderAction(
- icon: FAssets.icons.wind,
- onPress: null,
- ),
- ],
- rightActions: [
- FNestedHeaderAction.x(onPress: () {}),
- ],
-);
-}
diff --git a/samples/lib/widgets/scaffold.dart b/samples/lib/widgets/scaffold.dart
index d9e8ba725..15b496541 100644
--- a/samples/lib/widgets/scaffold.dart
+++ b/samples/lib/widgets/scaffold.dart
@@ -20,7 +20,7 @@ class ScaffoldPage extends SampleScaffold {
height: 420,
child: FScaffold(
header: FHeader(
- title: 'Settings',
+ title: const Text('Settings'),
actions: [
FHeaderAction(
icon: FAssets.icons.ellipsis,