diff --git a/docs/pages/docs/alert.mdx b/docs/pages/docs/alert.mdx new file mode 100644 index 000000000..f40846f5a --- /dev/null +++ b/docs/pages/docs/alert.mdx @@ -0,0 +1,64 @@ +import { Tabs } from 'nextra/components'; +import { Widget } from "../../components/widget"; + +# Alert +Displays a callout for user attention. + + + + + + + ```dart + FAlert( + title: const Text('Heads Up!'), + subtitle: const Text('You can add components to your app using the cli.'), + ); + ``` + + + +## Usage + +### `FAlert(...)` + +```dart +FAlert( + icon: FAlertIcon(icon: FAssets.icons.badgeAlert), + title: const Text('Heads Up!'), + subtitle: const Text('You can add components to your app using the cli.'), +); +``` + +## Examples + +### Primary + + + + + + ```dart + FAlert( + title: const Text('Heads Up!'), + subtitle: const Text('You can add components to your app using the cli.'), + ); + ``` + + + +### Destructive + + + + + + ```dart + FAlert( + title: const Text('Heads Up!'), + subtitle: const Text('You can add components to your app using the cli.'), + style: FAlertStyle.destructive, + ); + ``` + + diff --git a/forui/CHANGELOG.md b/forui/CHANGELOG.md index 14edc0f29..c54ae0504 100644 --- a/forui/CHANGELOG.md +++ b/forui/CHANGELOG.md @@ -1,6 +1,7 @@ ## Next ### Additions +* Add `FAlert` * Add `FCalendar` * Add `FBottomNavigationBar` diff --git a/forui/example/lib/example.dart b/forui/example/lib/example.dart index 25f410fea..788445b5c 100644 --- a/forui/example/lib/example.dart +++ b/forui/example/lib/example.dart @@ -23,6 +23,10 @@ class _ExampleState extends State { const SizedBox(height: 100), FProgress(value: 0.9), const SizedBox(height: 10), + FAlert( + title: const Text('Heads Up! Forui is coming to flutter!'), + subtitle: const Text('You can add components dfijsoi djfosfj to your app using the cli.'), + ) ], ); } diff --git a/forui/lib/src/theme/theme_data.dart b/forui/lib/src/theme/theme_data.dart index 18f131ffe..7528de02f 100644 --- a/forui/lib/src/theme/theme_data.dart +++ b/forui/lib/src/theme/theme_data.dart @@ -25,6 +25,9 @@ final class FThemeData with Diagnosticable { /// The style. It is used to configure the miscellaneous properties, such as border radii, of Forui widgets. final FStyle style; + /// The alert styles. + final FAlertStyles alertStyles; + /// The badge styles. final FBadgeStyles badgeStyles; @@ -80,6 +83,7 @@ final class FThemeData with Diagnosticable { required this.buttonStyles, required this.calendarStyle, required this.cardStyle, + required this.alertStyles, required this.checkboxStyle, required this.dialogStyle, required this.headerStyle, @@ -105,6 +109,7 @@ final class FThemeData with Diagnosticable { colorScheme: colorScheme, typography: typography, style: style, + alertStyles: FAlertStyles.inherit(colorScheme: colorScheme, typography: typography, style: style), badgeStyles: FBadgeStyles.inherit(colorScheme: colorScheme, typography: typography, style: style), bottomNavigationBarStyle: FBottomNavigationBarStyle.inherit(colorScheme: colorScheme, typography: typography), buttonStyles: FButtonStyles.inherit(colorScheme: colorScheme, typography: typography, style: style), @@ -143,6 +148,7 @@ final class FThemeData with Diagnosticable { FColorScheme? colorScheme, FTypography? typography, FStyle? style, + FAlertStyles? alertStyles, FBadgeStyles? badgeStyles, FBottomNavigationBarStyle? bottomNavigationBarStyle, FButtonStyles? buttonStyles, @@ -162,6 +168,7 @@ final class FThemeData with Diagnosticable { colorScheme: colorScheme ?? this.colorScheme, typography: typography ?? this.typography, style: style ?? this.style, + alertStyles: alertStyles ?? this.alertStyles, badgeStyles: badgeStyles ?? this.badgeStyles, bottomNavigationBarStyle: bottomNavigationBarStyle ?? this.bottomNavigationBarStyle, buttonStyles: buttonStyles ?? this.buttonStyles, @@ -185,6 +192,7 @@ final class FThemeData with Diagnosticable { ..add(DiagnosticsProperty('colorScheme', colorScheme, level: DiagnosticLevel.debug)) ..add(DiagnosticsProperty('typography', typography, level: DiagnosticLevel.debug)) ..add(DiagnosticsProperty('style', style, level: DiagnosticLevel.debug)) + ..add(DiagnosticsProperty('alertStyles', alertStyles, level: DiagnosticLevel.debug)) ..add(DiagnosticsProperty('badgeStyles', badgeStyles, level: DiagnosticLevel.debug)) ..add(DiagnosticsProperty('bottomNavigationBarStyle', bottomNavigationBarStyle, level: DiagnosticLevel.debug)) ..add(DiagnosticsProperty('buttonStyles', buttonStyles, level: DiagnosticLevel.debug)) @@ -209,6 +217,7 @@ final class FThemeData with Diagnosticable { colorScheme == other.colorScheme && typography == other.typography && style == other.style && + alertStyles == other.alertStyles && badgeStyles == other.badgeStyles && bottomNavigationBarStyle == other.bottomNavigationBarStyle && buttonStyles == other.buttonStyles && @@ -229,6 +238,7 @@ final class FThemeData with Diagnosticable { colorScheme.hashCode ^ typography.hashCode ^ style.hashCode ^ + alertStyles.hashCode ^ badgeStyles.hashCode ^ bottomNavigationBarStyle.hashCode ^ buttonStyles.hashCode ^ diff --git a/forui/lib/src/widgets/alert/alert.dart b/forui/lib/src/widgets/alert/alert.dart new file mode 100644 index 000000000..8ea8cb3f1 --- /dev/null +++ b/forui/lib/src/widgets/alert/alert.dart @@ -0,0 +1,205 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; + +import 'package:meta/meta.dart'; + +import 'package:forui/forui.dart'; + +part 'alert_styles.dart'; + +part 'alert_icon.dart'; + +/// An alert. +/// +/// Displays a callout for user attention. +/// +/// See: +/// * https://forui.dev/docs/alert for working examples. +/// * [FAlertStyle] for customizing an alert's appearance. +class FAlert extends StatelessWidget { + @useResult + static FAlertCustomStyle _of(BuildContext context) { + final theme = context.dependOnInheritedWidgetOfExactType<_InheritedData>(); + return theme?.style ?? context.theme.alertStyles.primary; + } + + /// The icon. Defaults to [FAssets.icons.circleAlert]. + final Widget icon; + + /// The title. + final Widget title; + + /// The subtitle. + final Widget? subtitle; + + /// The style. Defaults to [FAlertStyle.primary]. + /// + /// Although typically one of the pre-defined styles in [FAlertStyle], it can also be a [FAlertCustomStyle]. + final FAlertStyle style; + + /// Creates a [FAlert] with a tile, subtitle, and icon. + /// + /// The alert's layout is as follows: + /// ``` + /// |---------------------------| + /// | [icon] [title] | + /// | [subtitle] | + /// |---------------------------| + /// ``` + FAlert({ + required this.title, + Widget? icon, + this.subtitle, + this.style = FAlertStyle.primary, + super.key, + }) : icon = icon ?? FAlertIcon(icon: FAssets.icons.circleAlert); + + @override + Widget build(BuildContext context) { + final style = switch (this.style) { + final FAlertCustomStyle style => style, + Variant.primary => context.theme.alertStyles.primary, + Variant.destructive => context.theme.alertStyles.destructive, + }; + + return DecoratedBox( + decoration: style.decoration, + child: Padding( + padding: style.padding, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + _InheritedData(style: style, child: icon), + Flexible( + child: Padding( + padding: const EdgeInsets.only(left: 8), + child: DefaultTextStyle.merge( + style: style.titleTextStyle, + child: title, + ), + ), + ), + ], + ), + if (subtitle != null) + Row( + children: [ + SizedBox(width: style.icon.height), + Flexible( + child: Padding( + padding: const EdgeInsets.only(top: 3, left: 8), + child: DefaultTextStyle.merge( + style: style.subtitleTextStyle, + child: subtitle!, + ), + ), + ), + ], + ), + ], + ), + ), + ); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DiagnosticsProperty('style', style)); + } +} + +/// A [FAlert]'s style. +/// +/// A style can be either one of the pre-defined styles in [FAlertStyle] or a [FAlertCustomStyle]. The pre-defined +/// styles are a convenient shorthand for the various [FAlertCustomStyle]s in the current context's [FAlertStyles]. +sealed class FAlertStyle { + /// The alert's primary style. + /// + /// Shorthand for the current context's [FAlertStyle.primary] style. + static const FAlertStyle primary = Variant.primary; + + /// The alert's destructive style. + /// + /// Shorthand for the current context's [FAlertStyle.destructive] style. + static const FAlertStyle destructive = Variant.destructive; +} + +@internal +enum Variant implements FAlertStyle { + primary, + destructive, +} + +/// A custom [FAlert] style. +final class FAlertCustomStyle extends FAlertStyle with Diagnosticable { + /// The decoration. + final BoxDecoration decoration; + + /// The padding. Defaults to `EdgeInsets.fromLTRB(16, 12, 16, 16)`. + final EdgeInsets padding; + + /// The icon's style. + final FAlertIconStyle icon; + + /// The title's [TextStyle]. + final TextStyle titleTextStyle; + + /// The subtitle's [TextStyle]. + final TextStyle subtitleTextStyle; + + /// Creates a [FAlertCustomStyle]. + FAlertCustomStyle({ + required this.decoration, + required this.icon, + required this.titleTextStyle, + required this.subtitleTextStyle, + this.padding = const EdgeInsets.all(16), + }); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('decoration', decoration)) + ..add(DiagnosticsProperty('padding', padding)) + ..add(DiagnosticsProperty('icon', icon)) + ..add(DiagnosticsProperty('titleTextStyle', titleTextStyle)) + ..add(DiagnosticsProperty('subtitleTextStyle', subtitleTextStyle)); + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is FAlertCustomStyle && + runtimeType == other.runtimeType && + decoration == other.decoration && + padding == other.padding && + icon == other.icon && + titleTextStyle == other.titleTextStyle && + subtitleTextStyle == other.subtitleTextStyle; + + @override + int get hashCode => + decoration.hashCode ^ padding.hashCode ^ icon.hashCode ^ titleTextStyle.hashCode ^ subtitleTextStyle.hashCode; +} + +class _InheritedData extends InheritedWidget { + final FAlertCustomStyle style; + + const _InheritedData({ + required this.style, + required super.child, + }); + + @override + bool updateShouldNotify(covariant _InheritedData old) => style != old.style; + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DiagnosticsProperty('style', style)); + } +} diff --git a/forui/lib/src/widgets/alert/alert_icon.dart b/forui/lib/src/widgets/alert/alert_icon.dart new file mode 100644 index 000000000..7e05287d7 --- /dev/null +++ b/forui/lib/src/widgets/alert/alert_icon.dart @@ -0,0 +1,90 @@ +part of 'alert.dart'; + +/// A [FAlert]'s icon. +class FAlertIcon extends StatelessWidget { + /// The icon. + final SvgAsset icon; + + /// Creates a [FAlertIcon] from the given SVG [icon]. + const FAlertIcon({required this.icon, super.key}); + + @override + Widget build(BuildContext context) { + final FAlertCustomStyle(:icon) = FAlert._of(context); + + return this.icon( + height: icon.height, + colorFilter: ColorFilter.mode(icon.color, BlendMode.srcIn), + ); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DiagnosticsProperty('icon', icon)); + } +} + +/// [FAlertIcon]'s style. +final class FAlertIconStyle with Diagnosticable { + /// The icon's color. + final Color color; + + /// The icon's height. Defaults to 20. + final double height; + + /// Creates a [FButtonIconStyle]. + /// + /// ## Contract: + /// Throws [AssertionError] if: + /// * `height` <= 0.0 + /// * `height` is Nan + FAlertIconStyle({ + required this.color, + this.height = 20, + }) : assert(0 < height, 'The height is $height, but it should be in the range "0 < height".'); + + /// Returns a copy of this [FAlertIconStyle] with the given properties replaced. + /// + /// ```dart + /// final style = FAlertIconStyle( + /// height: 20, + /// color: Colors.red, + /// ); + /// + /// final copy = style.copyWith( + /// color: Colors.blue, + /// ); + /// + /// print(copy.color); // Colors.blue + /// print(copy.height); // 20 + /// ``` + @useResult + FAlertIconStyle copyWith({ + Color? color, + double? height, + }) => + FAlertIconStyle( + color: color ?? this.color, + height: height ?? this.height, + ); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(ColorProperty('color', color)) + ..add(DoubleProperty('height', height, defaultValue: 20)); + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is FButtonIconStyle && + runtimeType == other.runtimeType && + color == other.enabledColor && + height == other.height; + + @override + int get hashCode => color.hashCode ^ height.hashCode; +} diff --git a/forui/lib/src/widgets/alert/alert_styles.dart b/forui/lib/src/widgets/alert/alert_styles.dart new file mode 100644 index 000000000..8891add7d --- /dev/null +++ b/forui/lib/src/widgets/alert/alert_styles.dart @@ -0,0 +1,92 @@ +part of 'alert.dart'; + +/// [FAlertCustomStyle]'s style. +class FAlertStyles with Diagnosticable { + /// The primary alert style. + final FAlertCustomStyle primary; + + /// The destructive alert style. + final FAlertCustomStyle destructive; + + /// Creates a [FAlertStyles]. + const FAlertStyles({ + required this.primary, + required this.destructive, + }); + + /// Creates a [FAlertStyles] that inherits its properties from the provided [colorScheme], [typography], and + /// [style]. + FAlertStyles.inherit({required FColorScheme colorScheme, required FTypography typography, required FStyle style}) + : primary = FAlertCustomStyle( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 12), + titleTextStyle: typography.base.copyWith( + fontWeight: FontWeight.w500, + color: colorScheme.foreground, + height: 1.2, + ), + subtitleTextStyle: typography.sm.copyWith(color: colorScheme.foreground), + decoration: BoxDecoration( + border: Border.all(color: colorScheme.border), + borderRadius: style.borderRadius, + color: colorScheme.background, + ), + icon: FAlertIconStyle(color: colorScheme.foreground), + ), + destructive = FAlertCustomStyle( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 12), + titleTextStyle: typography.base.copyWith( + fontWeight: FontWeight.w500, + color: colorScheme.destructive, + height: 1.2, + ), + subtitleTextStyle: typography.sm.copyWith(color: colorScheme.destructive), + decoration: BoxDecoration( + border: Border.all(color: colorScheme.destructive), + borderRadius: style.borderRadius, + color: colorScheme.background, + ), + icon: FAlertIconStyle(color: colorScheme.destructive), + ); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('primary', primary)) + ..add(DiagnosticsProperty('destructive', destructive)); + } + + /// Returns a copy of this [FAlertStyles] with the given properties replaced. + /// + /// ```dart + /// final style = FAlertStyles( + /// primary: ..., + /// destructive: ..., + /// ); + /// + /// final copy = style.copyWith(destructive: ...); + /// + /// print(style.primary == copy.primary); // true + /// print(style.destructive == copy.destructive); // false + /// ``` + @useResult + FAlertStyles copyWith({ + FAlertCustomStyle? primary, + FAlertCustomStyle? destructive, + }) => + FAlertStyles( + primary: primary ?? this.primary, + destructive: destructive ?? this.destructive, + ); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is FAlertStyles && + runtimeType == other.runtimeType && + primary == other.primary && + destructive == other.destructive; + + @override + int get hashCode => primary.hashCode ^ destructive.hashCode; +} diff --git a/forui/lib/src/widgets/card/card.dart b/forui/lib/src/widgets/card/card.dart index 554d3b1f1..2c5dd572b 100644 --- a/forui/lib/src/widgets/card/card.dart +++ b/forui/lib/src/widgets/card/card.dart @@ -95,7 +95,11 @@ final class FCardStyle with Diagnosticable { /// print(style.content == copy.content); // false /// ``` @useResult - FCardStyle copyWith({BoxDecoration? decoration, FCardContentStyle? content}) => FCardStyle( + FCardStyle copyWith({ + BoxDecoration? decoration, + FCardContentStyle? content, + }) => + FCardStyle( decoration: decoration ?? this.decoration, content: content ?? this.content, ); @@ -104,8 +108,8 @@ final class FCardStyle with Diagnosticable { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('content', content)) - ..add(DiagnosticsProperty('decoration', decoration)); + ..add(DiagnosticsProperty('decoration', decoration)) + ..add(DiagnosticsProperty('content', content)); } @override diff --git a/forui/lib/widgets.dart b/forui/lib/widgets.dart index be92536da..19861440e 100644 --- a/forui/lib/widgets.dart +++ b/forui/lib/widgets.dart @@ -18,6 +18,7 @@ library forui.widgets; import 'package:forui/forui.dart'; +export 'src/widgets/alert/alert.dart' hide Variant; export 'src/widgets/badge/badge.dart' hide Variant; export 'src/widgets/bottom_navigation_bar/bottom_navigation_bar.dart'; export 'src/widgets/button/button.dart' hide Variant; diff --git a/forui/test/golden/alert/zinc-dark-Variant.destructive-with-default-icon.png b/forui/test/golden/alert/zinc-dark-Variant.destructive-with-default-icon.png new file mode 100644 index 000000000..4641ac1a4 Binary files /dev/null and b/forui/test/golden/alert/zinc-dark-Variant.destructive-with-default-icon.png differ diff --git a/forui/test/golden/alert/zinc-dark-Variant.destructive-without-user-icon.png b/forui/test/golden/alert/zinc-dark-Variant.destructive-without-user-icon.png new file mode 100644 index 000000000..7b0eba8e0 Binary files /dev/null and b/forui/test/golden/alert/zinc-dark-Variant.destructive-without-user-icon.png differ diff --git a/forui/test/golden/alert/zinc-dark-Variant.primary-with-default-icon.png b/forui/test/golden/alert/zinc-dark-Variant.primary-with-default-icon.png new file mode 100644 index 000000000..4152520c7 Binary files /dev/null and b/forui/test/golden/alert/zinc-dark-Variant.primary-with-default-icon.png differ diff --git a/forui/test/golden/alert/zinc-dark-Variant.primary-without-user-icon.png b/forui/test/golden/alert/zinc-dark-Variant.primary-without-user-icon.png new file mode 100644 index 000000000..f25596a77 Binary files /dev/null and b/forui/test/golden/alert/zinc-dark-Variant.primary-without-user-icon.png differ diff --git a/forui/test/golden/alert/zinc-light-Variant.destructive-with-default-icon.png b/forui/test/golden/alert/zinc-light-Variant.destructive-with-default-icon.png new file mode 100644 index 000000000..5392349a0 Binary files /dev/null and b/forui/test/golden/alert/zinc-light-Variant.destructive-with-default-icon.png differ diff --git a/forui/test/golden/alert/zinc-light-Variant.destructive-without-user-icon.png b/forui/test/golden/alert/zinc-light-Variant.destructive-without-user-icon.png new file mode 100644 index 000000000..6689fab99 Binary files /dev/null and b/forui/test/golden/alert/zinc-light-Variant.destructive-without-user-icon.png differ diff --git a/forui/test/golden/alert/zinc-light-Variant.primary-with-default-icon.png b/forui/test/golden/alert/zinc-light-Variant.primary-with-default-icon.png new file mode 100644 index 000000000..7a1844a69 Binary files /dev/null and b/forui/test/golden/alert/zinc-light-Variant.primary-with-default-icon.png differ diff --git a/forui/test/golden/alert/zinc-light-Variant.primary-without-user-icon.png b/forui/test/golden/alert/zinc-light-Variant.primary-without-user-icon.png new file mode 100644 index 000000000..6f8496658 Binary files /dev/null and b/forui/test/golden/alert/zinc-light-Variant.primary-without-user-icon.png differ diff --git a/forui/test/src/widgets/alert/alert_golden_test.dart b/forui/test/src/widgets/alert/alert_golden_test.dart new file mode 100644 index 000000000..a2ff1a053 --- /dev/null +++ b/forui/test/src/widgets/alert/alert_golden_test.dart @@ -0,0 +1,61 @@ +@Tags(['golden']) +library; + +import 'package:flutter/material.dart'; + +import 'package:flutter_test/flutter_test.dart'; + +import 'package:forui/forui.dart'; +import 'package:forui/src/widgets/alert/alert.dart'; +import '../../test_scaffold.dart'; + +void main() { + group('FAlert', () { + for (final (name, theme, _) in TestScaffold.themes) { + for (final variant in Variant.values) { + testWidgets('$name with default icon', (tester) async { + await tester.pumpWidget( + TestScaffold( + data: theme, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: FAlert( + title: const Text('Alert Title'), + subtitle: const Text('Alert description with extra text'), + style: variant, + ), + ), + ), + ); + + await expectLater( + find.byType(TestScaffold), + matchesGoldenFile('alert/$name-$variant-with-default-icon.png'), + ); + }); + + testWidgets('$name without user icon', (tester) async { + await tester.pumpWidget( + TestScaffold( + data: theme, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: FAlert( + icon: FAlertIcon(icon: FAssets.icons.badgeAlert), + title: const Text('Alert Title'), + subtitle: const Text('Alert description with extra text'), + style: variant, + ), + ), + ), + ); + + await expectLater( + find.byType(TestScaffold), + matchesGoldenFile('alert/$name-$variant-without-user-icon.png'), + ); + }); + } + } + }); +} diff --git a/forui/test/src/widgets/alert/alert_icon_test.dart b/forui/test/src/widgets/alert/alert_icon_test.dart new file mode 100644 index 000000000..37dc5de85 --- /dev/null +++ b/forui/test/src/widgets/alert/alert_icon_test.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_test/flutter_test.dart'; + +import 'package:forui/forui.dart'; + +void main() { + group('FAlertIconStyle', () { + test('invalid height', () { + expect( + () => FAlertIconStyle( + height: 0, + color: Colors.white, + ), + throwsAssertionError, + ); + }); + + test('valid height', () { + expect( + () => FAlertIconStyle( + height: 1, + color: Colors.white, + ), + returnsNormally, + ); + }); + }); +} diff --git a/samples/lib/main.dart b/samples/lib/main.dart index 6dae46aaa..f9c49a59d 100644 --- a/samples/lib/main.dart +++ b/samples/lib/main.dart @@ -39,6 +39,10 @@ class _AppRouter extends $_AppRouter { page: EmptyRoute.page, initial: true, ), + AutoRoute( + path: '/alert/default', + page: AlertRoute.page, + ), AutoRoute( path: '/badge/default', page: BadgeRoute.page, diff --git a/samples/lib/widgets/alert.dart b/samples/lib/widgets/alert.dart new file mode 100644 index 000000000..f8b4f87e4 --- /dev/null +++ b/samples/lib/widgets/alert.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +import 'package:auto_route/auto_route.dart'; +import 'package:forui/src/widgets/alert/alert.dart'; + +import 'package:forui_samples/sample_scaffold.dart'; + +// ignore_for_file: invalid_use_of_internal_member, implementation_imports + +final variants = { + for (final value in Variant.values) value.name: value, +}; + +@RoutePage() +class AlertPage extends SampleScaffold { + final Variant variant; + + AlertPage({ + @queryParam super.theme, + @queryParam String style = 'primary', + }) : variant = variants[style] ?? Variant.primary; + + @override + Widget child(BuildContext context) => Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FAlert( + title: const Text('Heads Up!'), + subtitle: const Text('You can add components to your app using the cli.'), + style: variant, + ), + ], + ); +}