Skip to content

Commit

Permalink
Implement nested header
Browse files Browse the repository at this point in the history
  • Loading branch information
kawaijoe committed Jun 27, 2024
1 parent 293c868 commit 65b38f1
Show file tree
Hide file tree
Showing 8 changed files with 438 additions and 96 deletions.
62 changes: 62 additions & 0 deletions forui/example/lib/example.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import 'package:flutter/material.dart';

import 'package:forui/forui.dart';

class Example extends StatefulWidget {
const Example({super.key});

@override
State<Example> createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
@override
void initState() {
super.initState();
}

@override
Widget build(BuildContext context) => Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
Expanded(
child: FTabs(
tabs: [
FTabEntry(
label: 'Account',
content: FCard(
title: 'Account',
subtitle: 'Make changes to your account here. Click save when you are done.',
child: Column(
children: [
Container(
color: Colors.red,
height: 100,
),
],
),
),
),
FTabEntry(
label: 'Password',
content: FCard(
title: 'Password',
subtitle: 'Change your password here. After saving, you will be logged out.',
child: Column(
children: [
Container(
color: Colors.blue,
height: 100,
)
],
),
),
),
],
),
),
],
);
}
78 changes: 13 additions & 65 deletions forui/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import 'package:flutter/material.dart';

import 'package:forui/forui.dart';

import 'package:forui_example/example.dart';

void main() {
runApp(const Application());
}
Expand All @@ -15,72 +17,18 @@ class Application extends StatelessWidget {
Widget build(BuildContext context) => MaterialApp(
home: FTheme(
data: FThemes.zinc.light,
child: const FScaffold(
header: FHeader(title: 'Example'),
content: ExampleWidget(),
),
),
);
}

class ExampleWidget extends StatefulWidget {
const ExampleWidget({super.key});

@override
State<ExampleWidget> createState() => _ExampleWidgetState();
}

class _ExampleWidgetState extends State<ExampleWidget> {
@override
void initState() {
super.initState();
}

@override
Widget build(BuildContext context) => Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
Expanded(
child: FTabs(
tabs: [
FTabEntry(
label: 'Account',
content: FCard(
title: 'Account',
subtitle: 'Make changes to your account here. Click save when you are done.',
child: Column(
children: [
Container(
color: Colors.red,
height: 100,
),
],
),
),
),
FTabEntry(
label: 'Password',
content: FCard(
title: 'Password',
subtitle: 'Change your password here. After saving, you will be logged out.',
child: Column(
children: [
Container(
color: Colors.blue,
height: 100,
)
],
),
),
),
],
),
child: FScaffold(
header: FHeader(
title: 'Example Example Example Example',
actions: [
FHeaderAction(
icon: FAssets.icons.plus,
onPress: () {},
),
],
),
],
content: const Example(),
),
),
);
}
1 change: 1 addition & 0 deletions forui/lib/forui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ 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';
Expand Down
10 changes: 10 additions & 0 deletions forui/lib/src/theme/theme_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ final class FThemeData with Diagnosticable {
/// The header styles.
final FHeaderStyle headerStyle;

/// The nested header styles.
final FNestedHeaderStyle nestedHeaderStyle;

/// The tabs styles.
final FTabsStyle tabsStyle;

Expand Down Expand Up @@ -68,6 +71,7 @@ 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,
Expand All @@ -94,6 +98,7 @@ 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),
Expand Down Expand Up @@ -128,6 +133,7 @@ final class FThemeData with Diagnosticable {
FCardStyle? cardStyle,
FDialogStyle? dialogStyle,
FHeaderStyle? headerStyle,
FNestedHeaderStyle? nestedHeaderStyle,
FTabsStyle? tabsStyle,
FTextFieldStyle? textFieldStyle,
FScaffoldStyle? scaffoldStyle,
Expand All @@ -143,6 +149,7 @@ 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,
Expand All @@ -162,6 +169,7 @@ 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))
Expand All @@ -182,6 +190,7 @@ 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 &&
Expand All @@ -198,6 +207,7 @@ final class FThemeData with Diagnosticable {
cardStyle.hashCode ^
dialogStyle.hashCode ^
headerStyle.hashCode ^
nestedHeaderStyle.hashCode ^
tabsStyle.hashCode ^
textFieldStyle.hashCode ^
scaffoldStyle.hashCode ^
Expand Down
28 changes: 18 additions & 10 deletions forui/lib/src/widgets/header/header.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ part 'header_action.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 navigation actions.
/// It is typically used on pages at the root of the navigation stack.
///
/// See:
/// * https://forui.dev/docs/header for working examples.
/// * [FHeaderStyle] for customizing a header's appearance.
class FHeader extends StatelessWidget {
final class FHeader extends StatelessWidget {
/// The header's style. Defaults to [FThemeData.headerStyle].
final FHeaderStyle? style;

Expand Down Expand Up @@ -79,7 +79,7 @@ class FHeader extends StatelessWidget {
child: title,
),
),
Row(children: actions),
Row(children: actions.expand((action) => [action, const SizedBox(width: 10)]).toList()),
],
),
),
Expand All @@ -102,15 +102,19 @@ final class FHeaderStyle with Diagnosticable {
final TextStyle titleTextStyle;

/// The [FHeaderAction]'s style.
final FHeaderActionStyle action;
final FHeaderActionStyle actionStyle;

/// The spacing between [FHeaderAction]s.
final double actionSpacing;

/// The padding.
final EdgeInsets padding;

/// Creates a [FHeaderStyle].
FHeaderStyle({
required this.titleTextStyle,
required this.action,
required this.actionStyle,
required this.actionSpacing,
required this.padding,
});

Expand All @@ -124,7 +128,8 @@ final class FHeaderStyle with Diagnosticable {
fontWeight: FontWeight.w700,
height: 1,
),
action = FHeaderActionStyle.inherit(colorScheme: colorScheme),
actionStyle = FHeaderActionStyle.inherit(colorScheme: colorScheme),
actionSpacing = 10,
padding = style.pagePadding.copyWith(bottom: 15);

/// Returns a copy of this [FHeaderStyle] with the given properties replaced.
Expand All @@ -145,12 +150,14 @@ final class FHeaderStyle with Diagnosticable {
@useResult
FHeaderStyle copyWith({
TextStyle? titleTextStyle,
FHeaderActionStyle? action,
FHeaderActionStyle? actionStyle,
double? actionSpacing,
EdgeInsets? padding,
}) =>
FHeaderStyle(
titleTextStyle: titleTextStyle ?? this.titleTextStyle,
action: action ?? this.action,
actionStyle: actionStyle ?? this.actionStyle,
actionSpacing: actionSpacing ?? this.actionSpacing,
padding: padding ?? this.padding,
);

Expand All @@ -159,7 +166,8 @@ final class FHeaderStyle with Diagnosticable {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('titleTextStyle', titleTextStyle))
..add(DiagnosticsProperty('action', action))
..add(DiagnosticsProperty('action', actionStyle))
..add(DiagnosticsProperty('actionSpacing', actionSpacing))
..add(DiagnosticsProperty('padding', padding));
}
}
32 changes: 11 additions & 21 deletions forui/lib/src/widgets/header/header_action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,17 @@ class FHeaderAction extends StatelessWidget {

@override
Widget build(BuildContext context) {
final style = this.style ?? context.theme.headerStyle.action;
final style = this.style ?? context.theme.headerStyle.actionStyle;
final enabled = onPress != null || onLongPress != null;

return Padding(
padding: style.padding,
child: 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),
),
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),
),
),
);
Expand Down Expand Up @@ -72,23 +69,18 @@ class FHeaderActionStyle with Diagnosticable {
/// The icon's size. Defaults to 30.
final double size;

/// The padding. Defaults to `EdgeInsets.only(left: 10)`.
final EdgeInsets padding;

/// Creates a [FHeaderActionStyle].
FHeaderActionStyle({
required this.enabledColor,
required this.disabledColor,
this.size = 30,
this.padding = const EdgeInsets.only(left: 10),
});

/// Creates a [FHeaderActionStyle] that inherits its properties from the given [FColorScheme].
FHeaderActionStyle.inherit({required FColorScheme colorScheme})
: enabledColor = colorScheme.foreground,
disabledColor = colorScheme.foreground.withOpacity(0.5),
size = 30,
padding = const EdgeInsets.only(left: 10);
size = 30;

/// Returns a copy of this [FHeaderActionStyle] with the given properties replaced.
///
Expand Down Expand Up @@ -116,7 +108,6 @@ class FHeaderActionStyle with Diagnosticable {
enabledColor: enabledColor ?? this.enabledColor,
disabledColor: disabledColor ?? this.disabledColor,
size: size ?? this.size,
padding: padding ?? this.padding,
);

@override
Expand All @@ -125,7 +116,6 @@ class FHeaderActionStyle with Diagnosticable {
properties
..add(ColorProperty('enabledColor', enabledColor))
..add(ColorProperty('disabledColor', disabledColor))
..add(DoubleProperty('size', size, defaultValue: 30))
..add(DiagnosticsProperty('padding', padding, defaultValue: const EdgeInsets.only(left: 10)));
..add(DoubleProperty('size', size, defaultValue: 30));
}
}
Loading

0 comments on commit 65b38f1

Please sign in to comment.