diff --git a/.github/workflows/forui_example_build.yaml b/.github/workflows/forui_example_build.yaml index af5861869..3f304b563 100644 --- a/.github/workflows/forui_example_build.yaml +++ b/.github/workflows/forui_example_build.yaml @@ -47,5 +47,5 @@ jobs: - run: flutter analyze - run: flutter test - run: pod repo update - working-directory: samples/ios + working-directory: forui/example/ios - run: flutter build ios --debug --no-codesign --no-pub diff --git a/forui/lib/src/theme/font.dart b/forui/lib/src/theme/font.dart index f22989fab..f3f57ff94 100644 --- a/forui/lib/src/theme/font.dart +++ b/forui/lib/src/theme/font.dart @@ -185,3 +185,43 @@ final class FFont with Diagnosticable { heightScalar.hashCode; } + +/// Provides functions for working with [FFont]s. +extension FontTextStyle on TextStyle { + + /// Returns a [TextStyle] scaled using the given [font]. + /// + /// ```dart + /// final font = FFont( + /// family: 'packages/forui/my-font', + /// sizeScalar: 2, + /// letterSpacingScalar: 3, + /// wordSpacingScalar: 4, + /// heightScalar: 5, + /// ); + /// + /// final style = TextStyle( + /// fontFamily: 'default-font', + /// fontSize: 1, + /// letterSpacing: 1, + /// wordSpacing: 1, + /// height: 1, + /// ).withFont(font); + /// + /// print(style.fontFamily); // 'packages/forui/my-font' + /// print(style.fontSize); // 2 + /// print(style.letterSpacing); // 3 + /// print(style.wordSpacing); // 4 + /// print(style.height); // 5 + /// ``` + TextStyle withFont(FFont font) => TextStyle( + fontFamily: font.family, + fontSize: _scale(fontSize, font.sizeScalar), + letterSpacing: _scale(letterSpacing, font.letterSpacingScalar), + wordSpacing: _scale(wordSpacing, font.wordSpacingScalar), + height: _scale(height, font.heightScalar), + ); + + double? _scale(double? value, double factor) => value == null ? null : value * factor; + +} diff --git a/forui/lib/src/theme/style.dart b/forui/lib/src/theme/style.dart index c2cf19a9e..5d4cdde2a 100644 --- a/forui/lib/src/theme/style.dart +++ b/forui/lib/src/theme/style.dart @@ -8,16 +8,22 @@ final class FStyle with Diagnosticable { final BorderRadius borderRadius; /// Creates an [FStyle]. - FStyle({BorderRadius? borderRadius}) : borderRadius = borderRadius ?? BorderRadius.circular(8); + FStyle({BorderRadius? borderRadius}): + borderRadius = borderRadius ?? BorderRadius.circular(8); @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties.add(DiagnosticsProperty('borderRadius', borderRadius)); + properties + .add(DiagnosticsProperty('borderRadius', borderRadius)); } @override - bool operator ==(Object other) => identical(this, other) || other is FStyle && borderRadius == other.borderRadius; + bool operator ==(Object other) => + identical(this, other) || + other is FStyle && + runtimeType == other.runtimeType && + borderRadius == other.borderRadius; @override int get hashCode => borderRadius.hashCode; diff --git a/forui/lib/src/theme/theme.dart b/forui/lib/src/theme/theme.dart index b6125c1b6..01ddfc636 100644 --- a/forui/lib/src/theme/theme.dart +++ b/forui/lib/src/theme/theme.dart @@ -3,9 +3,14 @@ import 'package:flutter/material.dart'; import 'package:forui/forui.dart'; -/// Represents a ForUI theme. +/// Represents a Forui theme. +/// +/// See [ThemeBuildContext.theme] for accessing the current theme. class FTheme extends StatelessWidget { - /// Retrieves the theme data. + + /// Retrieves the current theme data. + /// + /// It is recommended to use [ThemeBuildContext.theme] to access the current theme instead. static FThemeData of(BuildContext context) { final theme = context.dependOnInheritedWidgetOfExactType<_InheritedTheme>(); return theme?.data ?? FThemes.zinc.light; @@ -35,7 +40,14 @@ class FTheme extends StatelessWidget { data: data, child: Directionality( textDirection: textDirection ?? Directionality.of(context), - child: child, + child: DefaultTextStyle( + // TODO: replace with configurable default font. + style: data.font.toTextStyle( + fontSize: 10, + color: data.colorScheme.foreground, + ), + child: child, + ), ), ); @@ -55,3 +67,15 @@ class _InheritedTheme extends InheritedWidget { @override bool updateShouldNotify(covariant _InheritedTheme old) => data != old.data; } + +/// Provides functions for accessing the current [FThemeData]. +extension ThemeBuildContext on BuildContext { + + /// Retrieves the current [FThemeData] from an ancestor [FTheme]. Defaults to [FThemes.zinc.light] if there is no + /// ancestor [FTheme]. + FThemeData get theme { + final theme = dependOnInheritedWidgetOfExactType<_InheritedTheme>(); + return theme?.data ?? FThemes.zinc.light; + } + +} diff --git a/forui/lib/src/theme/theme_data.dart b/forui/lib/src/theme/theme_data.dart index 353c0ab00..441faf1d1 100644 --- a/forui/lib/src/theme/theme_data.dart +++ b/forui/lib/src/theme/theme_data.dart @@ -34,8 +34,8 @@ class FThemeData with Diagnosticable { required this.font, required this.style, }): - boxStyle = FBoxStyle.inherit(colorScheme: colorScheme, font: font), - cardStyle = FCardStyle.inherit(colorScheme: colorScheme, font: font, style: style); + boxStyle = FBoxStyle.inherit(colorScheme: colorScheme), + cardStyle = FCardStyle.inherit(colorScheme: colorScheme, style: style); /// Creates a copy of this [FThemeData] with the given properties replaced. FThemeData copyWith({ diff --git a/forui/lib/src/widgets/box/box.dart b/forui/lib/src/widgets/box/box.dart index 68e61545c..6545391e3 100644 --- a/forui/lib/src/widgets/box/box.dart +++ b/forui/lib/src/widgets/box/box.dart @@ -17,13 +17,12 @@ class FBox extends StatelessWidget { @override Widget build(BuildContext context) { - final style = this.style ?? FTheme.of(context).boxStyle; - + final FBoxStyle(:color, :text) = style ?? context.theme.boxStyle; return ColoredBox( - color: style.color, + color: color, child: Text( - text, - style: style.text, + this.text, + style: text.withFont(context.theme.font), ), ); } diff --git a/forui/lib/src/widgets/box/box_style.dart b/forui/lib/src/widgets/box/box_style.dart index 893694ca4..b0895537f 100644 --- a/forui/lib/src/widgets/box/box_style.dart +++ b/forui/lib/src/widgets/box/box_style.dart @@ -11,7 +11,8 @@ class FBoxStyle { /// Creates a [FBoxStyle]. const FBoxStyle({required this.color, required this.text}); - /// Creates a [FBoxStyle] that inherits its properties from [font] and [colorScheme]. - FBoxStyle.inherit({required FColorScheme colorScheme, required FFont font}): - color = colorScheme.muted, text = font.toTextStyle(fontSize: 20); + /// Creates a [FBoxStyle] that inherits its properties from [colorScheme]. + FBoxStyle.inherit({required FColorScheme colorScheme}): + color = colorScheme.muted, + text = const TextStyle(fontSize: 20); } diff --git a/forui/lib/src/widgets/card/card.dart b/forui/lib/src/widgets/card/card.dart index c8008d0a2..e9f2df2f1 100644 --- a/forui/lib/src/widgets/card/card.dart +++ b/forui/lib/src/widgets/card/card.dart @@ -1,4 +1,5 @@ -import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; import 'package:forui/forui.dart'; @@ -28,8 +29,8 @@ class FCard extends StatelessWidget { @override Widget build(BuildContext context) { - final style = this.style ?? FTheme.of(context).cardStyle; - + final theme = context.theme; + final style = this.style ?? theme.cardStyle; return DecoratedBox( decoration: style.decoration, child: child, diff --git a/forui/lib/src/widgets/card/card_content.dart b/forui/lib/src/widgets/card/card_content.dart index dcf51066a..81af7d719 100644 --- a/forui/lib/src/widgets/card/card_content.dart +++ b/forui/lib/src/widgets/card/card_content.dart @@ -19,15 +19,15 @@ final class FCardContent extends StatelessWidget { @override Widget build(BuildContext context) { - final style = this.style ?? FTheme.of(context).cardStyle.content; - + final font = context.theme.font; + final style = this.style ?? context.theme.cardStyle.content; return Padding( padding: style.padding, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (title != null) Text(title!, style: style.title), - if (subtitle != null) Text(subtitle!, style: style.subtitle), + if (title != null) Text(title!, style: style.title.withFont(font)), + if (subtitle != null) Text(subtitle!, style: style.subtitle.withFont(font)), if (child != null) Padding( padding: (title == null && subtitle == null) ? const EdgeInsets.only(top: 4) : const EdgeInsets.only(top: 10), diff --git a/forui/lib/src/widgets/card/card_content_style.dart b/forui/lib/src/widgets/card/card_content_style.dart index 513d79ebb..9b4e247a1 100644 --- a/forui/lib/src/widgets/card/card_content_style.dart +++ b/forui/lib/src/widgets/card/card_content_style.dart @@ -1,7 +1,7 @@ part of 'card.dart'; /// [FCardContent]'s style. -class FCardContentStyle { +class FCardContentStyle with Diagnosticable { /// The padding. final EdgeInsets padding; @@ -14,10 +14,10 @@ class FCardContentStyle { /// Creates a [FCardContentStyle]. const FCardContentStyle({required this.padding, required this.title, required this.subtitle}); - /// Creates a [FCardContentStyle] that inherits its properties from [colorScheme] and [font]. - FCardContentStyle.inherit({required FColorScheme colorScheme, required FFont font}): + /// Creates a [FCardContentStyle] that inherits its properties from [colorScheme]. + FCardContentStyle.inherit({required FColorScheme colorScheme}): padding = const EdgeInsets.fromLTRB(16, 12, 16, 16), - title = font.toTextStyle( + title = TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: colorScheme.foreground, @@ -27,4 +27,23 @@ class FCardContentStyle { color: colorScheme.mutedForeground, ); + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('padding', padding)) + ..add(DiagnosticsProperty('title', title)) + ..add(DiagnosticsProperty('subtitle', subtitle)); + } + + @override + bool operator ==(Object other) => identical(this, other) || other is FCardContentStyle && + runtimeType == other.runtimeType && + padding == other.padding && + title == other.title && + subtitle == other.subtitle; + + @override + int get hashCode => padding.hashCode ^ title.hashCode ^ subtitle.hashCode; + } diff --git a/forui/lib/src/widgets/card/card_style.dart b/forui/lib/src/widgets/card/card_style.dart index 7f5abb72d..56040a7df 100644 --- a/forui/lib/src/widgets/card/card_style.dart +++ b/forui/lib/src/widgets/card/card_style.dart @@ -1,7 +1,7 @@ part of 'card.dart'; /// [FCard]'s style. -class FCardStyle { +final class FCardStyle with Diagnosticable { /// The decoration. final BoxDecoration decoration; @@ -11,11 +11,28 @@ class FCardStyle { /// Creates a [FCardStyle]. FCardStyle({required this.decoration, required this.content}); - /// Creates a [FCardStyle] that inherits its properties from [colorScheme] and [font]. - FCardStyle.inherit({required FColorScheme colorScheme, required FFont font, required FStyle style}) - : decoration = BoxDecoration( - border: Border.all(color: colorScheme.border), - borderRadius: style.borderRadius, - ), - content = FCardContentStyle.inherit(colorScheme: colorScheme, font: font); + /// Creates a [FCardStyle] that inherits its properties from [colorScheme] and [style]. + FCardStyle.inherit({required FColorScheme colorScheme, required FStyle style}): + decoration = BoxDecoration( + border: Border.all(color: colorScheme.border), + borderRadius: style.borderRadius, + ), + content = FCardContentStyle.inherit(colorScheme: colorScheme); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + .add(DiagnosticsProperty('content', content)); + } + + @override + bool operator ==(Object other) => identical(this, other) || other is FCardStyle && + runtimeType == other.runtimeType && + decoration == other.decoration && + content == other.content; + + @override + int get hashCode => decoration.hashCode ^ content.hashCode; + }