diff --git a/docs/pages/docs/_meta.json b/docs/pages/docs/_meta.json
index bed9fbf34..823f943f4 100644
--- a/docs/pages/docs/_meta.json
+++ b/docs/pages/docs/_meta.json
@@ -1,7 +1,7 @@
{
"index": "Getting Started",
"themes": "Themes",
- "icon": "Icon",
+ "icon-library": "Icon",
"api_reference": {
"title": "API Reference ↗",
"href": "https://pub.dev/documentation/forui",
diff --git a/docs/pages/docs/alert.mdx b/docs/pages/docs/alert.mdx
index 1bfb17b2f..25e49497f 100644
--- a/docs/pages/docs/alert.mdx
+++ b/docs/pages/docs/alert.mdx
@@ -30,7 +30,7 @@ Displays a callout for user attention.
```dart
FAlert(
- icon: FAlertIcon(icon: FAssets.icons.badgeAlert),
+ icon: FIcon(FAssets.icons.badgeAlert),
title: const Text('Heads Up!'),
subtitle: const Text('You can add components to your app using the cli.'),
);
diff --git a/docs/pages/docs/bottom-navigation-bar.mdx b/docs/pages/docs/bottom-navigation-bar.mdx
index 571916f52..ffd74245a 100644
--- a/docs/pages/docs/bottom-navigation-bar.mdx
+++ b/docs/pages/docs/bottom-navigation-bar.mdx
@@ -34,23 +34,23 @@ It is used to navigate between a small number of views, typically between three
onChange: (index) => setState(() => this.index = index),
children: [
FBottomNavigationBarItem(
- icon: FAssets.icons.home,
+ icon: FIcon(FAssets.icons.home),
label: const Text('Home'),
),
FBottomNavigationBarItem(
- icon: FAssets.icons.layoutGrid,
+ icon: FIcon(FAssets.icons.layoutGrid),
label: const Text('Browse'),
),
FBottomNavigationBarItem(
- icon: FAssets.icons.radio,
+ icon: FIcon(FAssets.icons.radio),
label: const Text('Radio'),
),
FBottomNavigationBarItem(
- icon: FAssets.icons.libraryBig,
+ icon: FIcon(FAssets.icons.libraryBig),
label: const Text('Library'),
),
FBottomNavigationBarItem(
- icon: FAssets.icons.search,
+ icon: FIcon(FAssets.icons.search),
label: const Text('Search'),
),
],
@@ -74,81 +74,9 @@ FBottomNavigationBar(
onChange: (index) => {},
children: [
FBottomNavigationBarItem(
- icon: FAssets.icons.home,
+ icon: FIcon(FAssets.icons.home),
label: const Text('Home'),
),
],
)
```
-
-## Examples
-
-### Custom Icon
-
-
-
-
-
-
- ```dart
- class Application extends StatefulWidget {
- const Application({super.key});
-
- @override
- State createState() => _ApplicationState();
- }
-
- class _ApplicationState extends State {
- int index = 1;
-
- @override
- Widget build(BuildContext context) => FBottomNavigationBar(
- index: index,
- onChange: (index) => setState(() => this.index = index),
- children: [
- FBottomNavigationBarItem.customIcon(
- iconBuilder: (_, data, __) => Icon(
- Icons.home_outlined,
- size: data.itemStyle.iconSize,
- color: data.selected ? data.itemStyle.activeIconColor : data.itemStyle.inactiveIconColor,
- ),
- label: const Text('Home'),
- ),
- FBottomNavigationBarItem.customIcon(
- iconBuilder: (_, data, __) => Icon(
- Icons.browse_gallery_outlined,
- size: data.itemStyle.iconSize,
- color: data.selected ? data.itemStyle.activeIconColor : data.itemStyle.inactiveIconColor,
- ),
- label: const Text('Browse'),
- ),
- FBottomNavigationBarItem.customIcon(
- iconBuilder: (_, data, __) => Icon(
- Icons.radio_outlined,
- size: data.itemStyle.iconSize,
- color: data.selected ? data.itemStyle.activeIconColor : data.itemStyle.inactiveIconColor,
- ),
- label: const Text('Radio'),
- ),
- FBottomNavigationBarItem.customIcon(
- iconBuilder: (_, data, __) => Icon(
- Icons.library_books_outlined,
- size: data.itemStyle.iconSize,
- color: data.selected ? data.itemStyle.activeIconColor : data.itemStyle.inactiveIconColor,
- ),
- label: const Text('Library'),
- ),
- FBottomNavigationBarItem.customIcon(
- iconBuilder: (_, data, __) => Icon(
- Icons.search_outlined,
- size: data.itemStyle.iconSize,
- color: data.selected ? data.itemStyle.activeIconColor : data.itemStyle.inactiveIconColor,
- ),
- label: const Text('Search'),
- ),
- ],
- );
- }
- ```
-
-
diff --git a/docs/pages/docs/button.mdx b/docs/pages/docs/button.mdx
index 0ae156848..76366fa91 100644
--- a/docs/pages/docs/button.mdx
+++ b/docs/pages/docs/button.mdx
@@ -122,7 +122,7 @@ FButton.raw(
```dart {2}
FButton(
- prefix: FButtonIcon(icon: FAssets.icons.mail),
+ prefix: FIcon(icon: FAssets.icons.mail),
label: const Text('Login with Email'),
onPress: () {},
),
@@ -138,7 +138,7 @@ FButton.raw(
```dart {2}
FButton.icon(
- icon: FButtonIcon(icon: FAssets.icons.chevronRight),
+ icon: FIcon(icon: FAssets.icons.chevronRight),
onPress: () {},
),
```
diff --git a/docs/pages/docs/icon-library.mdx b/docs/pages/docs/icon-library.mdx
new file mode 100644
index 000000000..64584e1c6
--- /dev/null
+++ b/docs/pages/docs/icon-library.mdx
@@ -0,0 +1,57 @@
+import {Callout} from "nextra/components";
+
+
+# Icon
+
+Forui Assets is flutter library that provides a set of high-quality icons from [Lucide](https://lucide.dev/).
+
+
+## Installation
+
+
+ Forui Assets is bundled with forui package. You don't need to install it separately if you installed `forui`.
+
+
+From your Flutter project directory, run the following command to install Forui Assets.
+
+```bash filename="bash"
+flutter pub install forui_assets
+```
+
+
+## Usage
+
+
+ The best way to find a list of icons is to visit the [Lucide](https://lucide.dev/icons/) website.
+ We periodically update the icons in the Forui Assets package.
+ If you notice a missing icon in Forui Assets, please [open an issue](https://github.com/forus-labs/forui/issues/new).
+
+
+
+ While you can use an icon from `forui_assets` directly, it is recommended to wrap it in an [FIcon](/docs/icon) to
+ automatically configure its color and size.
+
+
+```dart
+import 'package:forui/forui.dart';
+
+// alternatively; if you've only installed forui_assets.
+import 'package:forui_assets/forui_assets.dart';
+
+// Dog icon as a Widget. It is recommended to wrap icons in FIcon if you're using Forui.
+final dogIconWidget = FIcon(FAssets.icons.dog);
+
+// Bird icon as a Widget.
+final birdIconWidget = FAssets.icons.bird();
+
+// White cat icon with a size of 24x24.
+final catIconWidget = FAssets.icons.cat(
+ width: 24,
+ height: 24,
+ colorFilter: const ColorFilter.mode(Color(0xFFFFFFFF), BlendMode.srcIn),
+);
+
+// Saving an icon to a variable for later use.
+final rabbitSvgAsset = FAssets.icons.rabbit;
+final rabbitIconWidget = rabbitSvgAsset();
+```
\ No newline at end of file
diff --git a/docs/pages/docs/icon.mdx b/docs/pages/docs/icon.mdx
index 6a5102688..300a7abde 100644
--- a/docs/pages/docs/icon.mdx
+++ b/docs/pages/docs/icon.mdx
@@ -1,49 +1,214 @@
-import {Callout} from "nextra/components";
-
+import { Tabs } from 'nextra/components';
+import { Callout } from "nextra/components";
+import { Widget } from "../../components/widget";
+import LinkBadge from "../../components/link-badge/link-badge";
+import LinkBadgeGroup from "../../components/link-badge/link-badge-group";
# Icon
+An icon that inherits its style from an enclosing, supported widget, such as a [button](/docs/button).
+
+
+
+
+
+
+
+
+
+
+ ```dart
+ Row(
+ children: [
+ FButton.icon(
+ style: FButtonStyle.primary,
+ child: FIcon(FAssets.icons.bird),
+ onPress: () {},
+ ),
+ const SizedBox(width: 10),
+ FButton.icon(
+ style: FButtonStyle.secondary,
+ child: FIcon(FAssets.icons.bird),
+ onPress: () {},
+ ),
+ ]
+ );
+ ```
+
+
-Forui Icons is flutter library that provides a set of high-quality icons from [Lucide](https://lucide.dev/).
-
-
-## Installation
-
- Forui Icons is bundled with forui package. You don't need to install it separately if you installed `forui`.
-
+## Usage
-From your Flutter project directory, run the following command to install Forui Icon.
+### `FIcon(...)`
-```bash filename="bash"
-flutter pub install forui_assets
+```dart
+FIcon(
+ FAssets.icons.bird,
+ color: Colors.red,
+ size: 24,
+ semanticLabel: 'Label',
+);
```
+### `FIcon.data(...)`
-## Usage
+```dart
+FIcon.data(
+ Icons.abc,
+ color: Colors.red,
+ size: 24,
+ semanticLabel: 'Label',
+);
+```
-
- The best way to find a list of icons is to visit the [Lucide](https://lucide.dev/icons/) website.
- We periodically update the icons in the Forui Icons package.
- If you notice a missing icon in Forui Icons, please [open an issue](https://github.com/forus-labs/forui/issues/new).
-
+### `FIcon.image(...)`
```dart
-import 'package:forui/forui.dart';
-
-// alternatively; if you've only installed forui_assets.
-import 'package:forui_assets/forui_assets.dart';
+FIcon.image(
+ NetworkImage('https://raw.githubusercontent.com/forus-labs/forui/main/samples/assets/avatar.png'),
+ color: Colors.red,
+ size: 24,
+ semanticLabel: 'Label',
+);
+```
-// Bird icon as a Widget.
-final birdIconWidget = FAssets.icons.bird();
+### `FIcon.raw(...)`
-// White cat icon with a size of 24x24.
-final catIconWidget = FAssets.icons.cat(
- width: 24,
- height: 24,
- colorFilter: const ColorFilter.mode(Color(0xFFFFFFFF), BlendMode.srcIn),
+```dart
+FIcon.raw(
+ builder: (context, style, child) => Container(
+ color: style.color,
+ height: style.size,
+ width: style.size,
+ child: child!,
+ ),
+ child: const Text('Button'),
);
+```
+
+## Examples
+
+### Bundled/SVG
+
+
+
+
+
+
+ ```dart {3}
+ FButton.icon(
+ style: FButtonStyle.secondary,
+ child: FIcon(FAssets.icons.wifi),
+ onPress: () {},
+ );
+ ```
+
+
+
+### `IconData`
+
+
+
+
+
+
+ ```dart {3}
+ FButton.icon(
+ style: FButtonStyle.secondary,
+ child: const FIcon.data(Icons.wifi),
+ onPress: () {},
+ );
+ ```
+
+
+
+### Image
+
+
+ It is important that the image's background is transparent. An image with a non-transparent background will be
+ completely filled with a single color.
+
-// Saving an icon to a variable for later use.
-final rabbitSvgAsset = FAssets.icons.rabbit;
-final rabbitIconWidget = rabbitSvgAsset();
-```
\ No newline at end of file
+
+
+
+
+
+ ```dart {7, 14}
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ // Original color
+ FButton.icon(
+ style: FButtonStyle.primary,
+ child: FIcon.image(AssetImage(path('forus-labs.png')), color: Colors.transparent),
+ onPress: () {},
+ ),
+ const SizedBox(width: 10),
+ // Recolored
+ FButton.icon(
+ style: FButtonStyle.primary,
+ child: FIcon.image(AssetImage(path('forus-labs.png'))),
+ onPress: () {},
+ ),
+ ],
+ );
+ ```
+
+
+
+### Raw
+
+
+
+
+
+
+ ```dart {23-34}
+ class _Example extends StatefulWidget {
+ @override
+ State<_Example> createState() => _ExampleState();
+ }
+
+ class _ExampleState extends State<_Example> with SingleTickerProviderStateMixin {
+ late AnimationController controller;
+ late Animation animation;
+
+ @override
+ void initState() {
+ super.initState();
+ controller = AnimationController(
+ vsync: this,
+ duration: const Duration(seconds: 3),
+ )
+ ..forward()
+ ..repeat(reverse: true);
+ animation = Tween(begin: 0.0, end: 1.0).animate(controller);
+ }
+
+ @override
+ Widget build(BuildContext context) => FButton.icon(
+ child: FIcon.raw(builder: (context, style, child) {
+ // You can access widget specific inherited data inside here.
+ final FButtonData(:enabled) = FButtonData.of(context);
+ return enabled ?
+ AnimatedIcon(
+ icon: AnimatedIcons.home_menu,
+ progress: animation,
+ color: style.color,
+ size: style.size,
+ semanticLabel: 'Home menu',
+ ) : const FIcon.data(Icons.menu);
+ }),
+ onPress: () {},
+ );
+
+ @override
+ void dispose() {
+ controller.dispose();
+ super.dispose();
+ }
+ }
+ ```
+
+
\ No newline at end of file
diff --git a/forui/CHANGELOG.md b/forui/CHANGELOG.md
index 0551a5534..42ec6f9ec 100644
--- a/forui/CHANGELOG.md
+++ b/forui/CHANGELOG.md
@@ -3,8 +3,6 @@
### Additions
* Add `FSlider`
-* Add `FBottomNavigationBarItem.custom(...)`.
-
* Add `FButtonCustomStyle.enabledHoverBoxDecoration`.
* Add `FTextField.contentInsertionConfiguration`.
@@ -46,6 +44,10 @@
* * **Breaking** Change FButtonCustomStyle to better represent the style's layout - this will only affect users that
create a custom `FButtonCustomStyle`.
+* **Breaking** Change `FBottomNavigationBarItem.icon` from `SvgAsset` to `Widget` - wrap the asset in ` FIcon` instead.
+
+* **Breaking** Change `FHeaderAction.icon` from `SvgAsset` to `Widget` - wrap the asset in ` FIcon` instead.
+
### Fixes
* Fix `FBottomNavigationBar` items hit region being smaller than intended.
diff --git a/forui/example/lib/main.dart b/forui/example/lib/main.dart
index 914bf0283..2baf702d2 100644
--- a/forui/example/lib/main.dart
+++ b/forui/example/lib/main.dart
@@ -41,7 +41,7 @@ class _ApplicationState extends State {
title: const Text('Example'),
actions: [
FHeaderAction(
- icon: FAssets.icons.plus,
+ icon: FIcon(FAssets.icons.plus),
onPress: () {},
),
],
@@ -52,23 +52,23 @@ class _ApplicationState extends State {
onChange: (index) => setState(() => this.index = index),
children: [
FBottomNavigationBarItem(
- icon: FAssets.icons.home,
+ icon: FIcon(FAssets.icons.home),
label: const Text('Home'),
),
FBottomNavigationBarItem(
- icon: FAssets.icons.layoutGrid,
+ icon: FIcon(FAssets.icons.layoutGrid),
label: const Text('Categories'),
),
FBottomNavigationBarItem(
- icon: FAssets.icons.search,
+ icon: FIcon(FAssets.icons.search),
label: const Text('Search'),
),
FBottomNavigationBarItem(
- icon: FAssets.icons.settings,
+ icon: FIcon(FAssets.icons.settings),
label: const Text('Settings'),
),
FBottomNavigationBarItem(
- icon: FAssets.icons.castle,
+ icon: FIcon(FAssets.icons.castle),
label: const Text('Sandbox'),
),
],
diff --git a/forui/example/pubspec.yaml b/forui/example/pubspec.yaml
index b29afe800..da90e7713 100644
--- a/forui/example/pubspec.yaml
+++ b/forui/example/pubspec.yaml
@@ -47,7 +47,7 @@ flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
- uses-material-design: false
+ uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
diff --git a/forui/lib/forui.dart b/forui/lib/forui.dart
index 248cc0135..4ceb8001f 100644
--- a/forui/lib/forui.dart
+++ b/forui/lib/forui.dart
@@ -17,6 +17,7 @@ export 'widgets/checkbox.dart';
export 'widgets/dialog.dart';
export 'widgets/divider.dart';
export 'widgets/header.dart';
+export 'widgets/icon.dart';
export 'widgets/label.dart';
export 'widgets/popover.dart';
export 'widgets/progress.dart';
diff --git a/forui/lib/foundation.dart b/forui/lib/foundation.dart
index 048634819..bf706ca5f 100644
--- a/forui/lib/foundation.dart
+++ b/forui/lib/foundation.dart
@@ -1,7 +1,6 @@
/// Low-level utilities and services.
library forui.foundation;
-export 'src/foundation/icon.dart';
export 'src/foundation/rendering.dart' hide Alignments, RenderBoxes;
export 'src/foundation/portal/portal.dart';
export 'src/foundation/portal/portal_shift.dart';
diff --git a/forui/lib/src/theme/style.dart b/forui/lib/src/theme/style.dart
index fecd58f8b..834b3999b 100644
--- a/forui/lib/src/theme/style.dart
+++ b/forui/lib/src/theme/style.dart
@@ -4,7 +4,6 @@ import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';
import 'package:forui/forui.dart';
-import 'package:forui/theme.dart';
/// A set of miscellaneous properties that is part of a [FThemeData].
///
@@ -20,6 +19,9 @@ final class FStyle with Diagnosticable {
/// The style for the form field when it has an error.
final FFormFieldErrorStyle errorFormFieldStyle;
+ /// The icon style.
+ final FIconStyle iconStyle;
+
/// The border radius. Defaults to `BorderRadius.circular(8)`.
final BorderRadius borderRadius;
@@ -37,6 +39,7 @@ final class FStyle with Diagnosticable {
required this.enabledFormFieldStyle,
required this.disabledFormFieldStyle,
required this.errorFormFieldStyle,
+ required this.iconStyle,
this.borderRadius = const BorderRadius.all(Radius.circular(8)),
this.borderWidth = 1,
this.pagePadding = const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
@@ -63,6 +66,10 @@ final class FStyle with Diagnosticable {
errorColor: colorScheme.error,
typography: typography,
),
+ iconStyle: FIconStyle(
+ color: colorScheme.primary,
+ size: 20,
+ ),
);
/// Returns a copy of this [FStyle] with the given properties replaced.
@@ -71,6 +78,7 @@ final class FStyle with Diagnosticable {
FFormFieldStyle? enabledFormFieldStyle,
FFormFieldStyle? disabledFormFieldStyle,
FFormFieldErrorStyle? errorFormFieldStyle,
+ FIconStyle? iconStyle,
BorderRadius? borderRadius,
double? borderWidth,
EdgeInsets? pagePadding,
@@ -79,6 +87,7 @@ final class FStyle with Diagnosticable {
enabledFormFieldStyle: enabledFormFieldStyle ?? this.enabledFormFieldStyle,
disabledFormFieldStyle: disabledFormFieldStyle ?? this.disabledFormFieldStyle,
errorFormFieldStyle: errorFormFieldStyle ?? this.errorFormFieldStyle,
+ iconStyle: iconStyle ?? this.iconStyle,
borderRadius: borderRadius ?? this.borderRadius,
borderWidth: borderWidth ?? this.borderWidth,
pagePadding: pagePadding ?? this.pagePadding,
@@ -91,6 +100,7 @@ final class FStyle with Diagnosticable {
..add(DiagnosticsProperty('enabledFormFieldStyle', enabledFormFieldStyle))
..add(DiagnosticsProperty('disabledFormFieldStyle', disabledFormFieldStyle))
..add(DiagnosticsProperty('errorFormFieldStyle', errorFormFieldStyle))
+ ..add(DiagnosticsProperty('iconStyle', iconStyle))
..add(DiagnosticsProperty('borderRadius', borderRadius, defaultValue: BorderRadius.circular(8)))
..add(DoubleProperty('borderWidth', borderWidth, defaultValue: 1))
..add(DiagnosticsProperty('pagePadding', pagePadding, defaultValue: const EdgeInsets.all(4)));
@@ -104,6 +114,7 @@ final class FStyle with Diagnosticable {
enabledFormFieldStyle == other.enabledFormFieldStyle &&
disabledFormFieldStyle == other.disabledFormFieldStyle &&
errorFormFieldStyle == other.errorFormFieldStyle &&
+ iconStyle == other.iconStyle &&
borderRadius == other.borderRadius &&
borderWidth == other.borderWidth &&
pagePadding == other.pagePadding;
@@ -113,6 +124,7 @@ final class FStyle with Diagnosticable {
enabledFormFieldStyle.hashCode ^
disabledFormFieldStyle.hashCode ^
errorFormFieldStyle.hashCode ^
+ iconStyle.hashCode ^
borderRadius.hashCode ^
borderWidth.hashCode ^
pagePadding.hashCode;
diff --git a/forui/lib/src/widgets/alert/alert.dart b/forui/lib/src/widgets/alert.dart
similarity index 69%
rename from forui/lib/src/widgets/alert/alert.dart
rename to forui/lib/src/widgets/alert.dart
index f106293c9..534e0d4b2 100644
--- a/forui/lib/src/widgets/alert/alert.dart
+++ b/forui/lib/src/widgets/alert.dart
@@ -15,7 +15,7 @@ import 'package:forui/forui.dart';
class FAlert extends StatelessWidget {
/// The icon. Defaults to [FAssets.icons.circleAlert].
///
- /// Icons are wrapped in [FIconData], and therefore works with [FIcon]s.
+ /// [icon] is wrapped in [FIconStyle], and therefore works with [FIcon]s.
final Widget icon;
/// The title.
@@ -63,9 +63,8 @@ class FAlert extends StatelessWidget {
children: [
Row(
children: [
- FIconData(
- color: style.iconColor,
- size: style.iconSize,
+ FInheritedIconStyle(
+ style: FIconStyle(color: style.iconColor, size: style.iconSize),
child: icon,
),
Flexible(
@@ -107,6 +106,84 @@ class FAlert extends StatelessWidget {
}
}
+/// [FAlertCustomStyle]'s style.
+final 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),
+ iconColor: colorScheme.foreground,
+ 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,
+ ),
+ ),
+ destructive = FAlertCustomStyle(
+ padding: const EdgeInsets.fromLTRB(16, 12, 16, 12),
+ iconColor: colorScheme.destructive,
+ 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,
+ ),
+ );
+
+ @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.
+ @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;
+}
+
/// A [FAlert]'s style.
///
/// A style can be either one of the pre-defined styles in [FAlertStyle] or a [FAlertCustomStyle]. The pre-defined
diff --git a/forui/lib/src/widgets/alert/alert_styles.dart b/forui/lib/src/widgets/alert/alert_styles.dart
deleted file mode 100644
index ef75dab5a..000000000
--- a/forui/lib/src/widgets/alert/alert_styles.dart
+++ /dev/null
@@ -1,84 +0,0 @@
-import 'package:flutter/foundation.dart';
-import 'package:flutter/widgets.dart';
-
-import 'package:meta/meta.dart';
-
-import 'package:forui/forui.dart';
-
-/// [FAlertCustomStyle]'s style.
-final 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),
- iconColor: colorScheme.foreground,
- 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,
- ),
- ),
- destructive = FAlertCustomStyle(
- padding: const EdgeInsets.fromLTRB(16, 12, 16, 12),
- iconColor: colorScheme.destructive,
- 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,
- ),
- );
-
- @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.
- @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/bottom_navigation_bar/bottom_navigation_bar_item.dart b/forui/lib/src/widgets/bottom_navigation_bar/bottom_navigation_bar_item.dart
index f8cf887c7..6830d4de7 100644
--- a/forui/lib/src/widgets/bottom_navigation_bar/bottom_navigation_bar_item.dart
+++ b/forui/lib/src/widgets/bottom_navigation_bar/bottom_navigation_bar_item.dart
@@ -7,35 +7,21 @@ import 'package:forui/forui.dart';
/// A [FBottomNavigationBar] item.
class FBottomNavigationBarItem extends StatelessWidget {
- static ValueWidgetBuilder _icon(SvgAsset icon) => (_, data, __) => icon(
- height: data.itemStyle.iconSize,
- colorFilter: ColorFilter.mode(
- data.selected ? data.itemStyle.activeIconColor : data.itemStyle.inactiveIconColor,
- BlendMode.srcIn,
- ),
- );
-
/// The style.
final FBottomNavigationBarItemStyle? style;
- /// The icon's builder.
- final ValueWidgetBuilder iconBuilder;
+ /// The icon.
+ ///
+ /// [icon] is wrapped in [FIconStyle], and therefore works with [FIcon]s.
+ final Widget icon;
/// The label.
final Widget label;
/// Creates a [FBottomNavigationBarItem].
- FBottomNavigationBarItem({
+ const FBottomNavigationBarItem({
required this.label,
- required SvgAsset icon,
- this.style,
- super.key,
- }) : iconBuilder = _icon(icon);
-
- /// Creates a [FBottomNavigationBarItem] with a custom icon.
- const FBottomNavigationBarItem.custom({
- required this.label,
- required this.iconBuilder,
+ required this.icon,
this.style,
super.key,
});
@@ -51,7 +37,15 @@ class FBottomNavigationBarItem extends StatelessWidget {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
- ExcludeSemantics(child: iconBuilder(context, data, null)),
+ ExcludeSemantics(
+ child: FInheritedIconStyle(
+ style: FIconStyle(
+ color: data.selected ? data.itemStyle.activeIconColor : data.itemStyle.inactiveIconColor,
+ size: data.itemStyle.iconSize,
+ ),
+ child: icon,
+ ),
+ ),
const SizedBox(height: 2),
DefaultTextStyle.merge(
style: selected ? style.activeTextStyle : style.inactiveTextStyle,
@@ -66,9 +60,7 @@ class FBottomNavigationBarItem extends StatelessWidget {
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
- properties
- ..add(DiagnosticsProperty('style', style))
- ..add(ObjectFlagProperty.has('iconBuilder', iconBuilder));
+ properties.add(DiagnosticsProperty('style', style));
}
}
diff --git a/forui/lib/src/widgets/button/button.dart b/forui/lib/src/widgets/button/button.dart
index b1139ca4c..ed3ae3a43 100644
--- a/forui/lib/src/widgets/button/button.dart
+++ b/forui/lib/src/widgets/button/button.dart
@@ -62,7 +62,7 @@ class FButton extends StatelessWidget {
/// Creates a [FButton] that contains a [prefix], [label], and [suffix].
///
- /// [prefix] and [suffix] are wrapped in [FIconData], and therefore works with [FIcon]s.
+ /// [prefix] and [suffix] are wrapped in [FIconStyle], and therefore works with [FIcon]s.
///
/// The button layout is as follows, assuming the locale is read from left to right:
/// ```
@@ -84,6 +84,8 @@ class FButton extends StatelessWidget {
}) : child = Content(prefix: prefix, suffix: suffix, label: label);
/// Creates a [FButton] that contains only an icon.
+ ///
+ /// [child] is wrapped in [FIconStyle], and therefore works with [FIcon]s.
FButton.icon({
required this.onPress,
required Widget child,
diff --git a/forui/lib/src/widgets/button/button_content.dart b/forui/lib/src/widgets/button/button_content.dart
index 5fa4102d8..086735f0c 100644
--- a/forui/lib/src/widgets/button/button_content.dart
+++ b/forui/lib/src/widgets/button/button_content.dart
@@ -27,9 +27,11 @@ class Content extends StatelessWidget {
padding: content.padding,
child: DefaultTextStyle.merge(
style: enabled ? content.enabledTextStyle : content.disabledTextStyle,
- child: FIconData(
- color: enabled ? content.enabledIconColor : content.disabledIconColor,
- size: content.iconSize,
+ child: FInheritedIconStyle(
+ style: FIconStyle(
+ color: enabled ? content.enabledIconColor : content.disabledIconColor,
+ size: content.iconSize,
+ ),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: separate(
@@ -61,9 +63,11 @@ class IconContent extends StatelessWidget {
return Padding(
padding: style.iconContent.padding,
- child: FIconData(
- color: enabled ? style.iconContent.enabled : style.iconContent.disabled,
- size: style.iconContent.size,
+ child: FInheritedIconStyle(
+ style: FIconStyle(
+ color: enabled ? style.iconContent.enabled : style.iconContent.disabled,
+ size: style.iconContent.size,
+ ),
child: child,
),
);
diff --git a/forui/lib/src/widgets/header/header_action.dart b/forui/lib/src/widgets/header/header_action.dart
index d18229e75..2ff24130f 100644
--- a/forui/lib/src/widgets/header/header_action.dart
+++ b/forui/lib/src/widgets/header/header_action.dart
@@ -11,7 +11,9 @@ class FHeaderAction extends StatelessWidget {
final String? semanticLabel;
/// The icon.
- final SvgAsset icon;
+ ///
+ /// [icon] is wrapped in [FIconStyle], and therefore works with [FIcon]s.
+ final Widget icon;
/// A callback for when the button is pressed.
///
@@ -41,7 +43,7 @@ class FHeaderAction extends StatelessWidget {
Key? key,
}) =>
FHeaderAction(
- icon: FAssets.icons.arrowLeft,
+ icon: FIcon(FAssets.icons.arrowLeft),
onPress: onPress,
style: style,
semanticLabel: semanticLabel,
@@ -55,7 +57,7 @@ class FHeaderAction extends StatelessWidget {
Key? key,
}) =>
FHeaderAction(
- icon: FAssets.icons.x,
+ icon: FIcon(FAssets.icons.x),
onPress: onPress,
style: style,
key: key,
@@ -70,9 +72,12 @@ class FHeaderAction extends StatelessWidget {
semanticLabel: semanticLabel,
onPress: onPress,
onLongPress: onLongPress,
- child: icon(
- height: style.size,
- colorFilter: ColorFilter.mode(enabled ? style.enabledColor : style.disabledColor, BlendMode.srcIn),
+ child: FInheritedIconStyle(
+ style: FIconStyle(
+ color: enabled ? style.enabledColor : style.disabledColor,
+ size: style.size,
+ ),
+ child: icon,
),
);
}
diff --git a/forui/lib/src/foundation/icon.dart b/forui/lib/src/widgets/icon.dart
similarity index 61%
rename from forui/lib/src/foundation/icon.dart
rename to forui/lib/src/widgets/icon.dart
index 487e67b88..505d9ae4b 100644
--- a/forui/lib/src/foundation/icon.dart
+++ b/forui/lib/src/widgets/icon.dart
@@ -1,11 +1,35 @@
import 'package:flutter/foundation.dart';
-import 'package:flutter/widgets.dart';
-import 'package:forui/assets.dart';
+import 'package:flutter/material.dart';
+import 'package:forui/forui.dart';
+
+/// The [FIconStyle] that this [FInheritedIconStyle]'s widget subtree should inherit.
+class FInheritedIconStyle extends InheritedWidget {
+ /// The icon's data.
+ final FIconStyle style;
+
+ /// Creates a [FIconStyle].
+ const FInheritedIconStyle({
+ required this.style,
+ required super.child,
+ super.key,
+ });
+
+ @override
+ bool updateShouldNotify(FInheritedIconStyle old) => style != old.style;
+
+ @override
+ void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+ super.debugFillProperties(properties);
+ properties.add(DiagnosticsProperty('style', style));
+ }
+}
/// The default properties of [FIcon]s in a widget subtree.
-class FIconData extends InheritedWidget {
- /// The data from the closest instance of this class that encloses the given context, if any.
- static FIconData? maybeOf(BuildContext context) => context.dependOnInheritedWidgetOfExactType();
+class FIconStyle with Diagnosticable {
+ /// The icon style from the closest instance of [FInheritedIconStyle] that encloses the given context, or
+ /// [FStyle.iconStyle] otherwise.
+ static FIconStyle of(BuildContext context) =>
+ context.dependOnInheritedWidgetOfExactType()?.style ?? context.theme.style.iconStyle;
/// The icon's color.
final Color color;
@@ -16,17 +40,12 @@ class FIconData extends InheritedWidget {
/// Throws [AssertionError] if `size` is not positive.
final double size;
- /// Creates a [FIconData].
- const FIconData({
+ /// Creates a [FIconStyle].
+ const FIconStyle({
required this.color,
required this.size,
- required super.child,
- super.key,
}) : assert(0 < size, 'size is $size, but it should be positive.');
- @override
- bool updateShouldNotify(FIconData old) => color != old.color || size != old.size;
-
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
@@ -36,7 +55,15 @@ class FIconData extends InheritedWidget {
}
}
-/// A graphical icon widget that inherits from a [FIconData], if any.
+/// A graphical icon widget that inherits its style from an [FIconStyle], if any.
+///
+/// [FIconStyle] allows icons to be automatically configured by an enclosing widget. It is explicitly mentioned in
+/// a widget's documentation, such as [FButton.icon], if it provides an [FIconStyle]. [FIcon] defaults to
+/// [FStyle.iconStyle] otherwise.
+///
+/// See:
+/// * [FIconStyle] for the properties that can be inherited.
+/// * [FAssets.icons] for bundled Forui icons.
abstract class FIcon extends StatelessWidget {
/// The icon's color.
final Color? color;
@@ -86,15 +113,35 @@ abstract class FIcon extends StatelessWidget {
/// Creates a [FIcon] from an [ImageProvider].
///
+ /// **Note:** Provided images should always have a transparent background. Otherwise, the entire icon will be [color].
+ ///
+ /// Set [color] to [Colors.transparent] to avoid recoloring the image.
+ ///
/// See [ImageIcon] for more information.
const factory FIcon.image(
ImageProvider