diff --git a/forui/lib/forui.dart b/forui/lib/forui.dart index e70ce397a..3f5fc5b56 100644 --- a/forui/lib/forui.dart +++ b/forui/lib/forui.dart @@ -1,6 +1,17 @@ /// A Flutter package for building beautiful user interfaces. library forui; +// Theme +export 'src/theme/style_data.dart'; export 'src/theme/theme.dart'; export 'src/theme/theme_data.dart'; -export 'src/theme/style/zinc_style.dart'; +export 'src/theme/widget_data.dart'; + +export 'src/theme/font/font_data.dart'; +export 'src/theme/font/scaled_text_style.dart'; + +export 'src/themes/zinc_theme.dart'; + +// Widgets +export 'src/widgets/box/box.dart'; +export 'src/widgets/box/box_style.dart'; diff --git a/forui/lib/src/theme/font/font_data.dart b/forui/lib/src/theme/font/font_data.dart new file mode 100644 index 000000000..bb6b18dd0 --- /dev/null +++ b/forui/lib/src/theme/font/font_data.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +import 'package:forui/forui.dart'; + +/// The font data that is inherited from a [FTheme] by child Forui widgets and [ScaledTextStyle]s. +/// +/// This class contains scalar values that are multiplied with the corresponding properties on [TextStyle] to ensure +/// that various fonts are scaled consistently across the application. +class FFontData { + /// A value used to scale [TextStyle.fontSize]. + final double fontSizeScalar; + + /// A value used to scale [TextStyle.letterSpacing]. + final double letterSpacingScalar; + + /// A value used to scale [TextStyle.wordSpacing]. + final double wordSpacingScalar; + + /// A value used to scale [TextStyle.height]. + final double heightScalar; + + /// Creates a [FFontData]. + const FFontData({ + this.fontSizeScalar = 1, + this.letterSpacingScalar = 1, + this.wordSpacingScalar = 1, + this.heightScalar = 1, + }); + + /// Creates a copy of this [FFontData] with the given properties replaced. + FFontData copyWith({ + double? fontSizeScalar, + double? letterSpacingScalar, + double? wordSpacingScalar, + double? heightScalar, + }) => + FFontData( + fontSizeScalar: fontSizeScalar ?? this.fontSizeScalar, + letterSpacingScalar: letterSpacingScalar ?? this.letterSpacingScalar, + wordSpacingScalar: wordSpacingScalar ?? this.wordSpacingScalar, + heightScalar: heightScalar ?? this.heightScalar, + ); +} diff --git a/forui/lib/src/theme/font/scaled_text_style.dart b/forui/lib/src/theme/font/scaled_text_style.dart new file mode 100644 index 000000000..a5e0e6b31 --- /dev/null +++ b/forui/lib/src/theme/font/scaled_text_style.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +import 'package:forui/forui.dart'; + +/// A [TextStyle] that scales its properties by factors. +/// +/// The scaled properties are: +/// * Font size +/// * Height +/// * Letter spacing +/// * Word spacing +extension type ScaledTextStyle._(TextStyle style) implements TextStyle { + /// Creates a [TextStyle] that inherits the properties from the given [FFontData]. + ScaledTextStyle(TextStyle base, FFontData data): style = TextStyle( + fontSize: _calculateFactor(base.fontSize, data.fontSizeScalar), + letterSpacing: _calculateFactor(base.letterSpacing, data.letterSpacingScalar), + wordSpacing: _calculateFactor(base.wordSpacing, data.wordSpacingScalar), + height: _calculateFactor(base.height, data.heightScalar), + ); + + static double? _calculateFactor(double? value, double factor) => value == null ? null : value * factor; +} diff --git a/forui/lib/src/theme/style/zinc_style.dart b/forui/lib/src/theme/style/zinc_style.dart deleted file mode 100644 index 34c0b3afc..000000000 --- a/forui/lib/src/theme/style/zinc_style.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:forui/forui.dart'; - -/// Zinc style of [FThemeData]. -/// -/// The zinc style is based on [shadcn/ui](https://ui.shadcn.com/themes). -extension FZincThemeData on Never { - /// The light theme of [FZincThemeData]. - static FThemeData light = FThemeData( - background: const Color(0xFFFFFFFF), - foreground: const Color(0xFF09090B), - primary: const Color(0xFF18181B), - primaryForeground: const Color(0xFFFAFAFA), - secondary: const Color(0xFFF4F4F5), - secondaryForeground: const Color(0xFF18181B), - muted: const Color(0xFFF4F4F5), - mutedForeground: const Color(0xFF71717A), - destructive: const Color(0xFFEF4444), - destructiveForeground: const Color(0xFFFAFAFA), - border: const Color(0xFFE4E4E7), - borderRadius: BorderRadius.circular(8), - ); - - /// The dark theme of [FZincThemeData]. - static FThemeData dark = FThemeData( - background: const Color(0xFF09090B), - foreground: const Color(0xFFFAFAFA), - primary: const Color(0xFFFAFAFA), - primaryForeground: const Color(0xFF18181B), - secondary: const Color(0xFF27272A), - secondaryForeground: const Color(0xFFFAFAFA), - muted: const Color(0xFF27272A), - mutedForeground: const Color(0xFFA1A1AA), - destructive: const Color(0xFF7F1D1D), - destructiveForeground: const Color(0xFFFAFAFA), - border: const Color(0xFF27272A), - borderRadius: BorderRadius.circular(8), - ); -} diff --git a/forui/lib/src/theme/style_data.dart b/forui/lib/src/theme/style_data.dart new file mode 100644 index 000000000..351d4e539 --- /dev/null +++ b/forui/lib/src/theme/style_data.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; + +import 'package:forui/forui.dart'; +import 'package:forui/src/theme/theme.dart'; + +/// The default theme data that is inherited from a [FTheme] by child Forui widgets. +class FStyleData { + /// The background color. + final Color background; + + /// The foreground color. + final Color foreground; + + /// The primary color. + final Color primary; + + /// The primary foreground color. + final Color primaryForeground; + + /// The secondary color. + final Color secondary; + + /// The secondary foreground color. + final Color secondaryForeground; + + /// The muted color. + final Color muted; + + /// The muted foreground color. + final Color mutedForeground; + + /// The destructive color. + final Color destructive; + + /// The destructive foreground color. + final Color destructiveForeground; + + /// The border color. + final Color border; + + /// The border radius. + final BorderRadius borderRadius; + + /// Creates a [FStyleData]. + const FStyleData({ + required this.background, + required this.foreground, + required this.primary, + required this.primaryForeground, + required this.secondary, + required this.secondaryForeground, + required this.muted, + required this.mutedForeground, + required this.destructive, + required this.destructiveForeground, + required this.border, + required this.borderRadius, + }); + + /// Creates a copy of this [FStyleData] with the given properties replaced. + FStyleData copyWith({ + Color? background, + Color? foreground, + Color? primary, + Color? primaryForeground, + Color? secondary, + Color? secondaryForeground, + Color? muted, + Color? mutedForeground, + Color? destructive, + Color? destructiveForeground, + Color? border, + BorderRadius? borderRadius, + }) => + FStyleData( + background: background ?? this.background, + foreground: foreground ?? this.foreground, + primary: primary ?? this.primary, + primaryForeground: primaryForeground ?? this.primaryForeground, + secondary: secondary ?? this.secondary, + secondaryForeground: secondaryForeground ?? this.secondaryForeground, + muted: muted ?? this.muted, + mutedForeground: mutedForeground ?? this.mutedForeground, + destructive: destructive ?? this.destructive, + destructiveForeground: destructiveForeground ?? this.destructiveForeground, + border: border ?? this.border, + borderRadius: borderRadius ?? this.borderRadius, + ); +} diff --git a/forui/lib/src/theme/theme.dart b/forui/lib/src/theme/theme.dart index ed930b70a..d8d272dc0 100644 --- a/forui/lib/src/theme/theme.dart +++ b/forui/lib/src/theme/theme.dart @@ -1,14 +1,13 @@ import 'package:flutter/material.dart'; -import 'package:forui/src/theme/style/zinc_style.dart'; -import 'package:forui/src/theme/theme_data.dart'; +import 'package:forui/forui.dart'; /// Represents a ForUI theme. class FTheme extends StatefulWidget { /// Retrieves the theme data. static FThemeData of(BuildContext context) { final theme = context.dependOnInheritedWidgetOfExactType<_InheritedTheme>(); - return theme?.data ?? FZincThemeData.light; + return theme?.data ?? FZincTheme.light; } /// The theme data. diff --git a/forui/lib/src/theme/theme_data.dart b/forui/lib/src/theme/theme_data.dart index a35b2e939..32d183cb2 100644 --- a/forui/lib/src/theme/theme_data.dart +++ b/forui/lib/src/theme/theme_data.dart @@ -1,86 +1,31 @@ import 'package:flutter/material.dart'; -/// A class that holds the theme data for the app. -class FThemeData { - /// The background color. - final Color background; - - /// The foreground color. - final Color foreground; - - /// The primary color. - final Color primary; - - /// The primary foreground color. - final Color primaryForeground; - - /// The secondary color. - final Color secondary; - - /// The secondary foreground color. - final Color secondaryForeground; +import 'package:forui/forui.dart'; - /// The muted color. - final Color muted; - - /// The muted foreground color. - final Color mutedForeground; - - /// The destructive color. - final Color destructive; - - /// The destructive foreground color. - final Color destructiveForeground; +/// The default font and theme data that are inherited by child Forui widgets. +class FThemeData { + /// The font data. + final FFontData font; - /// The border color. - final Color border; + /// The style data. + final FStyleData style; - /// The border radius. - final BorderRadius borderRadius; + /// The widget data. + final FWidgetData widgets; /// Creates a [FThemeData]. - const FThemeData({ - required this.background, - required this.foreground, - required this.primary, - required this.primaryForeground, - required this.secondary, - required this.secondaryForeground, - required this.muted, - required this.mutedForeground, - required this.destructive, - required this.destructiveForeground, - required this.border, - required this.borderRadius, - }); - - /// Creates a copy of this theme data with the given properties replaced. - FThemeData copyWith({ - Color? background, - Color? foreground, - Color? primary, - Color? primaryForeground, - Color? secondary, - Color? secondaryForeground, - Color? muted, - Color? mutedForeground, - Color? destructive, - Color? destructiveForeground, - Color? border, - BorderRadius? borderRadius, - }) => - FThemeData( - background: background ?? this.background, - foreground: foreground ?? this.foreground, - primary: primary ?? this.primary, - primaryForeground: primaryForeground ?? this.primaryForeground, - secondary: secondary ?? this.secondary, - secondaryForeground: secondaryForeground ?? this.secondaryForeground, - muted: muted ?? this.muted, - mutedForeground: mutedForeground ?? this.mutedForeground, - destructive: destructive ?? this.destructive, - destructiveForeground: destructiveForeground ?? this.destructiveForeground, - border: border ?? this.border, - borderRadius: borderRadius ?? this.borderRadius, + FThemeData({required this.font, required this.style, required this.widgets}); + + /// Creates a [FThemeData] that inherits the properties from the given [FFontData] and [FStyleData]. + FThemeData.inherit({ + required this.font, + required this.style, + }) : widgets = FWidgetData.inherit(data: font, style: style); + + /// Creates a copy of this [ThemeData] with the given properties replaced. + FThemeData copyWith({FFontData? fontData, FStyleData? styleData, FWidgetData? widgetData}) => FThemeData( + font: fontData ?? font, + style: styleData ?? style, + widgets: widgetData ?? widgets, ); } diff --git a/forui/lib/src/theme/widget_data.dart b/forui/lib/src/theme/widget_data.dart new file mode 100644 index 000000000..f63a3ddce --- /dev/null +++ b/forui/lib/src/theme/widget_data.dart @@ -0,0 +1,21 @@ +import 'package:forui/forui.dart'; + +/// The default widget-specific theme and font data that is inherited from a [FTheme] by child Forui widgets. +class FWidgetData { + /// The box style. + final FBoxStyle box; + + /// Creates a [FWidgetData]. + FWidgetData({required this.box}); + + /// Creates a [FWidgetData] that inherits the properties from the given [FFontData] and [FStyleData]. + FWidgetData.inherit({required FFontData data, required FStyleData style}): + box = FBoxStyle.inherit(data: data, style: style); + + /// Creates a copy of this [FWidgetData] with the given properties replaced. + FWidgetData copyWith({ + FBoxStyle? boxStyle, + }) => FWidgetData( + box: boxStyle ?? box, + ); +} diff --git a/forui/lib/src/themes/zinc_theme.dart b/forui/lib/src/themes/zinc_theme.dart new file mode 100644 index 000000000..772b87c0b --- /dev/null +++ b/forui/lib/src/themes/zinc_theme.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; + +import 'package:forui/forui.dart'; + +/// Zinc style of [FThemeData]. +/// +/// The zinc theme is based on [shadcn/ui](https://ui.shadcn.com/themes). +extension FZincTheme on Never { + /// The light variant. + static FThemeData light = FThemeData.inherit( + font: const FFontData(), + style: FStyleData( + background: const Color(0xFFFFFFFF), + foreground: const Color(0xFF09090B), + primary: const Color(0xFF18181B), + primaryForeground: const Color(0xFFFAFAFA), + secondary: const Color(0xFFF4F4F5), + secondaryForeground: const Color(0xFF18181B), + muted: const Color(0xFFF4F4F5), + mutedForeground: const Color(0xFF71717A), + destructive: const Color(0xFFEF4444), + destructiveForeground: const Color(0xFFFAFAFA), + border: const Color(0xFFE4E4E7), + borderRadius: BorderRadius.circular(8), + ), + ); + + /// The dark variant. + static FThemeData dark = FThemeData.inherit( + font: const FFontData(), + style: FStyleData( + background: const Color(0xFF09090B), + foreground: const Color(0xFFFAFAFA), + primary: const Color(0xFFFAFAFA), + primaryForeground: const Color(0xFF18181B), + secondary: const Color(0xFF27272A), + secondaryForeground: const Color(0xFFFAFAFA), + muted: const Color(0xFF27272A), + mutedForeground: const Color(0xFFA1A1AA), + destructive: const Color(0xFF7F1D1D), + destructiveForeground: const Color(0xFFFAFAFA), + border: const Color(0xFF27272A), + borderRadius: BorderRadius.circular(8), + ), + ); +} diff --git a/forui/lib/src/widgets/box/box.dart b/forui/lib/src/widgets/box/box.dart new file mode 100644 index 000000000..9d1c8a752 --- /dev/null +++ b/forui/lib/src/widgets/box/box.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; + +import 'package:forui/forui.dart'; + +/// A sample widget that demonstrates Forui's architecture. +class FBox extends StatelessWidget { + /// The text. + final String text; + + /// The style. + final FBoxStyle? style; + + /// Creates a [FBox]. + const FBox({required this.text, this.style, super.key}); + + @override + Widget build(BuildContext context) { + final style = this.style ?? FTheme.of(context).widgets.box; + + return ColoredBox( + color: style.color, + child: Text( + text, + style: style.text, + ), + ); + } +} diff --git a/forui/lib/src/widgets/box/box_style.dart b/forui/lib/src/widgets/box/box_style.dart new file mode 100644 index 000000000..bf4bf75c7 --- /dev/null +++ b/forui/lib/src/widgets/box/box_style.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +import 'package:forui/forui.dart'; + +/// [FBox]'s style. +class FBoxStyle { + /// The color. + final Color color; + + /// The text. + final TextStyle text; + + /// Creates a [FBoxStyle]. + const FBoxStyle({required this.color, required this.text}); + + /// Creates a [FBoxStyle] that inherits its properties from [data] and [style]. + FBoxStyle.inherit({required FFontData data, required FStyleData style}): + color = style.background, text = ScaledTextStyle(const TextStyle(fontSize: 20), data); +} diff --git a/samples/lib/main.dart b/samples/lib/main.dart index 6e5df6b1c..29532fb31 100644 --- a/samples/lib/main.dart +++ b/samples/lib/main.dart @@ -13,7 +13,7 @@ class Application extends StatelessWidget { @override Widget build(BuildContext context) => FTheme( - data: FZincThemeData.light, + data: FZincTheme.light, child: Container(), ); }