diff --git a/docs/pages/docs/divider.mdx b/docs/pages/docs/divider.mdx index 57dd80cac..d60260c39 100644 --- a/docs/pages/docs/divider.mdx +++ b/docs/pages/docs/divider.mdx @@ -46,12 +46,12 @@ Visually or semantically separates content. 'Blog', style: typography.sm.copyWith(color: colorScheme.foreground), ), - const FDivider(vertical: true), + const FDivider(axis : Axis.vertical), Text( 'Docs', style: typography.sm.copyWith(color: colorScheme.foreground), ), - const FDivider(vertical: true), + const FDivider(axis : Axis.vertical), Text( 'Source', style: typography.sm.copyWith(color: colorScheme.foreground), @@ -71,5 +71,5 @@ Visually or semantically separates content. ### `FDivider(...)` ```dart -FDivider(vertical: true); +const FDivider(axis : Axis.vertical); ``` diff --git a/forui/CHANGELOG.md b/forui/CHANGELOG.md index a9e82f83d..e476b138b 100644 --- a/forui/CHANGELOG.md +++ b/forui/CHANGELOG.md @@ -30,6 +30,8 @@ * **Breaking:** Rename `FButtonIconStyle.height` to `FButtonIconStyle.size`. +* **Breaking:** Change `FDivider.vertical` to `FDivider.axis`. + * Change `FResizable` to resize by `FResizable.resizePercentage` when using a keyboard. * **Breaking:** Change `FResiableDividerStyle.thickness` to `FResizableDividerStyle.width`. @@ -44,6 +46,8 @@ * **Breaking:** Remove `FTextField.error` - use `FTextField.forceErrorText` instead. +* Change `FTabController` to implement `ChangeNotifier` instead of `Listenable`. + ### Fixes * Fix `FResizable` not rendering properly in an expanded widget when its crossAxisExtent is null. @@ -54,6 +58,8 @@ * Fix `FCheckboxStyle.inherit(...)` icon color inheriting from the wrong color. +* Fix `FTabs` not handling indexes properly. + ## 0.4.0 diff --git a/forui/lib/src/widgets/bottom_navigation_bar/bottom_navigation_bar.dart b/forui/lib/src/widgets/bottom_navigation_bar/bottom_navigation_bar.dart index feac004c8..4b6a8baec 100644 --- a/forui/lib/src/widgets/bottom_navigation_bar/bottom_navigation_bar.dart +++ b/forui/lib/src/widgets/bottom_navigation_bar/bottom_navigation_bar.dart @@ -84,7 +84,7 @@ class FBottomNavigationBar extends StatelessWidget { } } -/// AFBottomNavigationBar]'s data. +/// A FBottomNavigationBar]'s data. class FBottomNavigationBarData extends InheritedWidget { /// Returns the [FBottomNavigationBarItemStyle] and currently selected index of the [FBottomNavigationBar] in the /// given [context]. diff --git a/forui/lib/src/widgets/checkbox.dart b/forui/lib/src/widgets/checkbox.dart index fa7d190c9..21955aee1 100644 --- a/forui/lib/src/widgets/checkbox.dart +++ b/forui/lib/src/widgets/checkbox.dart @@ -66,10 +66,10 @@ class FCheckbox extends FFormField { @override Widget builder(BuildContext context, FormFieldState state) { final style = this.style ?? context.theme.checkboxStyle; - final stateStyle = switch ((enabled, state.hasError)) { - (true, false) => style.enabledStyle, - (false, false) => style.disabledStyle, - (_, true) => style.errorStyle, + final (labelState, stateStyle) = switch ((enabled, state.hasError)) { + (true, false) => (FLabelState.enabled, style.enabledStyle), + (false, false) => (FLabelState.disabled, style.disabledStyle), + (_, true) => (FLabelState.error, style.errorStyle), }; final value = state.value ?? initialValue; @@ -92,11 +92,7 @@ class FCheckbox extends FFormField { : null, child: FLabel( axis: Axis.horizontal, - state: switch ((enabled, state.hasError)) { - (true, false) => FLabelState.enabled, - (false, false) => FLabelState.disabled, - (_, true) => FLabelState.error, - }, + state: labelState, label: label, description: description, error: Text(state.errorText ?? ''), @@ -206,21 +202,6 @@ final class FCheckboxStyle with Diagnosticable { ); /// Returns a copy of this [FCheckboxStyle] with the given properties replaced. - /// - /// ```dart - /// final style = FCheckboxStyle( - /// animationDuration: const Duration(minutes: 1), - /// curve: Curves.linear, - /// // Other arguments omitted for brevity. - /// ); - /// - /// final copy = style.copyWith( - /// curve: Curves.bounceIn, - /// ); - /// - /// print(style.animationDuration); // const Duration(minutes: 1) - /// print(copy.curve); // Curves.bounceIn - /// ``` @useResult FCheckboxStyle copyWith({ Duration? animationDuration, @@ -296,21 +277,6 @@ final class FCheckboxStateStyle with Diagnosticable { }); /// Returns a copy of this [FCheckboxStateStyle] with the given properties replaced. - /// - /// ```dart - /// final style = FCheckBoxStateStyle( - /// iconColor: ..., - /// checkedBackgroundColor: ..., - /// // Other arguments omitted for brevity. - /// ); - /// - /// final copy = style.copyWith( - /// checkedBackgroundColor: ..., - /// ); - /// - /// print(style.iconColor == copy.iconColor); // true - /// print(style.checkedBackgroundColor == copy.checkedBackgroundColor); // false - /// ``` @useResult FCheckboxStateStyle copyWith({ Color? borderColor, diff --git a/forui/lib/src/widgets/divider.dart b/forui/lib/src/widgets/divider.dart index 2556b5ff1..6dfede880 100644 --- a/forui/lib/src/widgets/divider.dart +++ b/forui/lib/src/widgets/divider.dart @@ -16,23 +16,25 @@ final class FDivider extends StatelessWidget { /// The divider's style. Defaults to the appropriate style in [FThemeData.dividerStyles]. final FDividerStyle? style; - /// True if this divider is vertical. Defaults to false (horizontal). - final bool vertical; + /// The axis. Defaults to horizontal. + final Axis axis; /// Creates a [FDivider]. - const FDivider({this.style, this.vertical = false, super.key}); + const FDivider({this.style, this.axis = Axis.horizontal, super.key}); @override Widget build(BuildContext context) { - final style = - this.style ?? (vertical ? context.theme.dividerStyles.vertical : context.theme.dividerStyles.horizontal); - final (height, width) = vertical ? (null, style.width) : (style.width, null); + final style = this.style ?? + switch (axis) { + Axis.horizontal => context.theme.dividerStyles.horizontal, + Axis.vertical => context.theme.dividerStyles.vertical, + }; return Container( margin: style.padding, color: style.color, - height: height, - width: width, + height: axis == Axis.horizontal ? style.width : null, + width: axis == Axis.horizontal ? null : style.width, ); } @@ -40,8 +42,8 @@ final class FDivider extends StatelessWidget { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(FlagProperty('vertical', value: vertical, defaultValue: false, ifTrue: 'vertical')) - ..add(DiagnosticsProperty('style', style)); + ..add(DiagnosticsProperty('style', style)) + ..add(EnumProperty('axis', axis)); } } @@ -110,7 +112,7 @@ final class FDividerStyle with Diagnosticable { /// The padding surrounding the separating line. Defaults to the appropriate padding in [defaultPadding]. final EdgeInsetsGeometry padding; - /// The width of the separating line. Defaults to 1. + /// The width (thickness) of the separating line. Defaults to 1. /// /// ## Contract /// Throws [AssertionError] if: diff --git a/forui/lib/src/widgets/progress.dart b/forui/lib/src/widgets/progress.dart index d9e43f4ae..b5fb82bf4 100644 --- a/forui/lib/src/widgets/progress.dart +++ b/forui/lib/src/widgets/progress.dart @@ -13,25 +13,24 @@ import 'package:forui/forui.dart'; /// * https://forui.dev/docs/progress for working examples. /// * [FProgressStyle] for customizing a progress's appearance. class FProgress extends StatelessWidget { + /// The style. Defaults to [FThemeData.progressStyle]. + final FProgressStyle? style; + /// If non-null, the value of this progress indicator. /// /// A value of 0.0 means no progress and 1.0 means that progress is complete. - /// The value will be clamped to be in the range 0.0-1.0. + /// The value will be clamped to be in the range, `[0.0, 1.0]`. /// /// ## Contract - /// Throws [AssertionError] if: - /// * [value] is NaN + /// Throws [AssertionError] if [value] is NaN final double value; - /// The style. Defaults to [FThemeData.progressStyle]. - final FProgressStyle? style; - /// Creates a [FProgress]. FProgress({ required this.value, this.style, super.key, - }) : assert(!value.isNaN, 'Cannot provide a NaN value'); + }) : assert(!value.isNaN, 'Cannot provide a NaN value.'); @override Widget build(BuildContext context) { @@ -68,8 +67,8 @@ class FProgress extends StatelessWidget { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DoubleProperty('value', value)) - ..add(DiagnosticsProperty('style', style)); + ..add(DiagnosticsProperty('style', style)) + ..add(DoubleProperty('value', value)); } } @@ -113,30 +112,7 @@ final class FProgressStyle with Diagnosticable { animationDuration = const Duration(milliseconds: 500), curve = Curves.ease; - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty('progressDecoration', progressDecoration)) - ..add(DiagnosticsProperty('backgroundDecoration', backgroundDecoration)) - ..add(DiagnosticsProperty('constraints', constraints)) - ..add(DiagnosticsProperty('animationDuration', animationDuration)) - ..add(DiagnosticsProperty('curve', curve)); - } - /// Returns a copy of this [FProgressStyle] with the given properties replaced. - /// - /// ```dart - /// final style = FProgressStyle( - /// backgroundDecoration: ..., - /// progressDecoration: ..., - /// ); - /// - /// final copy = style.copyWith(progressDecoration: ...); - /// - /// print(style.backgroundDecoration == copy.backgroundDecoration); // true - /// print(style.progressDecoration == copy.progressDecoration); // false - /// ``` @useResult FProgressStyle copyWith({ BoxDecoration? backgroundDecoration, @@ -153,6 +129,17 @@ final class FProgressStyle with Diagnosticable { curve: curve ?? this.curve, ); + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('progressDecoration', progressDecoration)) + ..add(DiagnosticsProperty('backgroundDecoration', backgroundDecoration)) + ..add(DiagnosticsProperty('constraints', constraints)) + ..add(DiagnosticsProperty('animationDuration', animationDuration)) + ..add(DiagnosticsProperty('curve', curve)); + } + @override bool operator ==(Object other) => identical(this, other) || diff --git a/forui/lib/src/widgets/scaffold.dart b/forui/lib/src/widgets/scaffold.dart index 8f1e6d967..44a34c85f 100644 --- a/forui/lib/src/widgets/scaffold.dart +++ b/forui/lib/src/widgets/scaffold.dart @@ -1,6 +1,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; +import 'package:meta/meta.dart'; + import 'package:forui/forui.dart'; /// A scaffold. @@ -62,8 +64,8 @@ class FScaffold extends StatelessWidget { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(FlagProperty('pad', value: contentPad, defaultValue: true, ifTrue: 'pad')) - ..add(DiagnosticsProperty('style', style)); + ..add(DiagnosticsProperty('style', style)) + ..add(FlagProperty('contentPad', value: contentPad, defaultValue: true, ifTrue: 'pad')); } } @@ -103,6 +105,21 @@ final class FScaffoldStyle with Diagnosticable { ), ); + /// Returns a copy of this style with the provided properties replaced. + @useResult + FScaffoldStyle copyWith({ + Color? backgroundColor, + EdgeInsets? contentPadding, + BoxDecoration? headerDecoration, + BoxDecoration? footerDecoration, + }) => + FScaffoldStyle( + backgroundColor: backgroundColor ?? this.backgroundColor, + contentPadding: contentPadding ?? this.contentPadding, + headerDecoration: headerDecoration ?? this.headerDecoration, + footerDecoration: footerDecoration ?? this.footerDecoration, + ); + @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); diff --git a/forui/lib/src/widgets/switch.dart b/forui/lib/src/widgets/switch.dart index 8419e0802..6b9fb3895 100644 --- a/forui/lib/src/widgets/switch.dart +++ b/forui/lib/src/widgets/switch.dart @@ -162,9 +162,9 @@ class FSwitch extends StatelessWidget { ..add(StringProperty('semanticLabel', semanticLabel)) ..add(FlagProperty('autofocus', value: autofocus, defaultValue: false, ifTrue: 'autofocus')) ..add(EnumProperty('dragStartBehavior', dragStartBehavior, defaultValue: DragStartBehavior.start)) - ..add(DiagnosticsProperty('onChange', onChange)) + ..add(ObjectFlagProperty.has('onChange', onChange)) ..add(DiagnosticsProperty('focusNode', focusNode)) - ..add(DiagnosticsProperty('onFocusChange', onFocusChange)) + ..add(ObjectFlagProperty.has('onFocusChange', onFocusChange)) ..add(ObjectFlagProperty.has('onSave', onSave)) ..add(ObjectFlagProperty.has('validator', validator)) ..add(DiagnosticsProperty('initialValue', initialValue)) @@ -203,30 +203,7 @@ final class FSwitchStyle with Diagnosticable { thumbColor = colorScheme.background, focusColor = colorScheme.primary; - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(ColorProperty('checkedColor', checkedColor)) - ..add(ColorProperty('uncheckedColor', uncheckedColor)) - ..add(ColorProperty('thumbColor', thumbColor)) - ..add(ColorProperty('focusColor', focusColor)); - } - /// Returns a copy of this [FSwitchStyle] with the given properties replaced. - /// - /// ```dart - /// final style = FSwitchStyle( - /// checkedColor: Colors.black, - /// uncheckedColor: Colors.white, - /// // Other arguments omitted for brevity - /// ); - /// - /// final copy = style.copyWith(uncheckedColor: Colors.blue); - /// - /// print(copy.checkedColor); // black - /// print(copy.uncheckedColor); // blue - /// ``` @useResult FSwitchStyle copyWith({ Color? checkedColor, @@ -241,6 +218,16 @@ final class FSwitchStyle with Diagnosticable { focusColor: focusColor ?? this.focusColor, ); + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(ColorProperty('checkedColor', checkedColor)) + ..add(ColorProperty('uncheckedColor', uncheckedColor)) + ..add(ColorProperty('thumbColor', thumbColor)) + ..add(ColorProperty('focusColor', focusColor)); + } + @override bool operator ==(Object other) => identical(this, other) || diff --git a/forui/lib/src/widgets/tabs/tab_controller.dart b/forui/lib/src/widgets/tabs/tab_controller.dart index 6ea0e1553..724ea6794 100644 --- a/forui/lib/src/widgets/tabs/tab_controller.dart +++ b/forui/lib/src/widgets/tabs/tab_controller.dart @@ -1,7 +1,7 @@ part of 'tabs.dart'; -/// An object that controls selection in a [FTabs] -class FTabController implements Listenable { +/// A controller that controls selection in a [FTabs]. +final class FTabController implements ChangeNotifier { final TabController _controller; /// Creates a [FTabController]. @@ -17,28 +17,34 @@ class FTabController implements Listenable { vsync: vsync, ); + /// Animates to the given [index]. + void animateTo( + int index, { + Duration? duration, + Curve curve = Curves.ease, + }) => + _controller.animateTo(index, duration: duration, curve: curve); + @override void addListener(VoidCallback listener) => _controller.addListener(listener); + @override + void notifyListeners() => _controller.notifyListeners(); + @override void removeListener(VoidCallback listener) => _controller.removeListener(listener); - /// Discards any resources used by the object. After this is called, the - /// object is not in a usable state and should be discarded (calls to - /// [addListener] will throw after the object is disposed). - /// - /// This method should only be called by the object's owner. - /// - /// This method does not notify listeners, and clears the listener list once - /// it is called. Consumers of this class must decide on whether to notify - /// listeners or not immediately before disposal. - void dispose() => _controller.dispose(); + /// The index of the selected tab. + int get index => _controller.index; + + set index(int value) => _controller.index = value; + + /// The number of tabs. + int get length => _controller.length; @override - bool operator ==(Object other) => - identical(this, other) || - other is FTabController && runtimeType == other.runtimeType && _controller == other._controller; + bool get hasListeners => _controller.hasListeners; @override - int get hashCode => _controller.hashCode; + void dispose() => _controller.dispose(); } diff --git a/forui/lib/src/widgets/tabs/tabs.dart b/forui/lib/src/widgets/tabs/tabs.dart index 8c3eab46d..57b319c7e 100644 --- a/forui/lib/src/widgets/tabs/tabs.dart +++ b/forui/lib/src/widgets/tabs/tabs.dart @@ -20,8 +20,8 @@ class FTabEntry { /// Creates a [FTabs]. const FTabEntry({ - required this.content, required this.label, + required this.content, }); @override @@ -37,32 +37,37 @@ class FTabEntry { /// /// See: /// * https://forui.dev/docs/tabs for working examples. -/// * [FTabsStyle] for customizing a switch's appearance. +/// * [FTabsStyle] for customizing tabs' appearance. class FTabs extends StatefulWidget { - /// The tab and it's corresponding view. - final List tabs; + /// The tab controller. + final FTabController? controller; + + /// The style. + final FTabsStyle? style; /// The initial tab that is selected. + /// + /// ## Contract + /// Throws [AssertionError] if: + /// * [initialIndex] is not within the range '0 <= initialIndex < tabs.length`. final int initialIndex; /// Whether this tab bar can be scrolled horizontally. /// - /// If [scrollable] is true, then each tab is as wide as needed for its label - /// and the entire [TabBar] is scrollable. Otherwise each tab gets an equal - /// share of the available space. + /// If [scrollable] is true, then each tab is as wide as needed for its label and the entire [TabBar] is scrollable. + /// Otherwise each tab gets an equal share of the available space. final bool scrollable; - /// The tab controller. - final FTabController? controller; - - /// The style. - final FTabsStyle? style; - /// A callback that returns the tab that was tapped. final ValueChanged? onPress; + /// The tab and it's corresponding view. + final List tabs; + /// Creates a [FTabs]. - const FTabs({ + /// + /// ## Throws + FTabs({ required this.tabs, this.initialIndex = 0, this.scrollable = false, @@ -70,18 +75,27 @@ class FTabs extends StatefulWidget { this.style, this.onPress, super.key, - }) : assert(0 < tabs.length, 'Must have at least 1 tab provided'); + }) : assert(tabs.isNotEmpty, 'Must have at least 1 tab provided.'), + assert(0 <= initialIndex && initialIndex < tabs.length, 'Initial index must be within the range of tabs.'), + assert( + controller == null || controller.index == initialIndex, + 'Controller index must match the initial index.', + ), + assert( + controller == null || controller.length == tabs.length, + 'Controller length must match the number of tabs.', + ); @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(IterableProperty('tabs', tabs)) - ..add(IntProperty('initialIndex', initialIndex)) - ..add(DiagnosticsProperty('controller', controller)) + ..add(ObjectFlagProperty.has('controller', controller)) ..add(DiagnosticsProperty('style', style)) + ..add(IntProperty('initialIndex', initialIndex)) + ..add(FlagProperty('scrollable', value: scrollable, ifTrue: 'scrollable')) ..add(ObjectFlagProperty.has('onPress', onPress)) - ..add(FlagProperty('scrollable', value: scrollable, ifTrue: 'scrollable')); + ..add(IterableProperty('tabs', tabs)); } @override @@ -89,24 +103,38 @@ class FTabs extends StatefulWidget { } class _FTabsState extends State with SingleTickerProviderStateMixin { - late int _index; - late final FTabController _controller; + late FTabController _controller; @override void initState() { super.initState(); - _index = widget.initialIndex; - _controller = FTabController(length: widget.tabs.length, vsync: this); + _controller = _createController(); } + @override + void didUpdateWidget(covariant FTabs old) { + super.didUpdateWidget(old); + if (widget.controller != old.controller) { + _controller.dispose(); + _controller = _createController(); + } + } + + FTabController _createController() => + widget.controller ?? + FTabController( + initialIndex: widget.initialIndex, + length: widget.tabs.length, + vsync: this, + ); + @override Widget build(BuildContext context) { final theme = context.theme; final style = widget.style ?? context.theme.tabsStyle; - final tabs = widget.tabs; - final materialLocalizations = Localizations.of(context, MaterialLocalizations); + final localizations = Localizations.of(context, MaterialLocalizations); - final child = Material( + final tabs = Material( color: Colors.transparent, child: Column( children: [ @@ -114,13 +142,13 @@ class _FTabsState extends State with SingleTickerProviderStateMixin { decoration: style.decoration, child: TabBar( tabs: [ - for (final tab in tabs) + for (final tab in widget.tabs) Tab( height: style.height, child: tab.label, ), ], - controller: (widget.controller ?? _controller)._controller, + controller: _controller._controller, isScrollable: widget.scrollable, padding: style.padding, indicator: style.indicator, @@ -128,37 +156,34 @@ class _FTabsState extends State with SingleTickerProviderStateMixin { dividerColor: Colors.transparent, labelStyle: style.selectedLabel, unselectedLabelStyle: style.unselectedLabel, - onTap: (index) { - setState(() => _index = index); - widget.onPress?.call(_index); - }, + onTap: (index) => widget.onPress?.call(index), ), ), SizedBox(height: style.spacing), - // A workaround to ensure any widgets under Tabs do not revert to material text style - // TODO: abstract out logic DefaultTextStyle( style: theme.typography.base.copyWith( fontFamily: theme.typography.defaultFontFamily, color: theme.colorScheme.foreground, ), - child: tabs[_index].content, + child: widget.tabs[_controller.index].content, ), ], ), ); - return materialLocalizations == null - ? Localizations( - locale: Localizations.maybeLocaleOf(context) ?? const Locale('en', 'US'), - delegates: const [ - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - child: child, - ) - : child; + if (localizations == null) { + return Localizations( + locale: Localizations.maybeLocaleOf(context) ?? const Locale('en', 'US'), + delegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + child: tabs, + ); + } + + return tabs; } @override diff --git a/forui/lib/src/widgets/tabs/tabs_style.dart b/forui/lib/src/widgets/tabs/tabs_style.dart index 7529d7a0f..7b2e28f8b 100644 --- a/forui/lib/src/widgets/tabs/tabs_style.dart +++ b/forui/lib/src/widgets/tabs/tabs_style.dart @@ -26,12 +26,12 @@ final class FTabsStyle with Diagnosticable { /// The padding. final EdgeInsets padding; - /// The [TextStyle] of the label - final TextStyle unselectedLabel; - - /// The [TextStyle] of the label + /// The [TextStyle] of the label. final TextStyle selectedLabel; + /// The [TextStyle] of the label. + final TextStyle unselectedLabel; + /// The indicator. final BoxDecoration indicator; @@ -48,8 +48,8 @@ final class FTabsStyle with Diagnosticable { FTabsStyle({ required this.decoration, required this.padding, - required this.unselectedLabel, required this.selectedLabel, + required this.unselectedLabel, required this.indicator, required this.indicatorSize, required this.height, @@ -64,15 +64,15 @@ final class FTabsStyle with Diagnosticable { color: colorScheme.muted, ), padding = const EdgeInsets.all(4), - unselectedLabel = typography.sm.copyWith( + selectedLabel = typography.sm.copyWith( fontWeight: FontWeight.w500, fontFamily: typography.defaultFontFamily, - color: colorScheme.mutedForeground, + color: colorScheme.foreground, ), - selectedLabel = typography.sm.copyWith( + unselectedLabel = typography.sm.copyWith( fontWeight: FontWeight.w500, fontFamily: typography.defaultFontFamily, - color: colorScheme.foreground, + color: colorScheme.mutedForeground, ), indicatorSize = FTabBarIndicatorSize.tab, indicator = BoxDecoration( @@ -111,8 +111,8 @@ final class FTabsStyle with Diagnosticable { properties ..add(DiagnosticsProperty('decoration', decoration)) ..add(DiagnosticsProperty('padding', padding)) - ..add(DiagnosticsProperty('unselectedLabel', unselectedLabel)) ..add(DiagnosticsProperty('selectedLabel', selectedLabel)) + ..add(DiagnosticsProperty('unselectedLabel', unselectedLabel)) ..add(EnumProperty('indicatorSize', indicatorSize)) ..add(DiagnosticsProperty('indicator', indicator)) ..add(DoubleProperty('height', height)) diff --git a/forui/lib/src/widgets/text_field/text_form_field.dart b/forui/lib/src/widgets/text_field/field.dart similarity index 97% rename from forui/lib/src/widgets/text_field/text_form_field.dart rename to forui/lib/src/widgets/text_field/field.dart index 8b934405a..861ca5fbb 100644 --- a/forui/lib/src/widgets/text_field/text_form_field.dart +++ b/forui/lib/src/widgets/text_field/field.dart @@ -1,6 +1,11 @@ -part of 'text_field.dart'; +import 'package:flutter/material.dart'; -class _Field extends FormField { +import 'package:meta/meta.dart'; + +import 'package:forui/forui.dart'; + +@internal +class Field extends FormField { static InputDecoration _decoration( _State state, FTextField parent, @@ -62,7 +67,7 @@ class _Field extends FormField { final FTextField parent; - _Field({ + Field({ required this.parent, required FTextFieldStyle style, super.key, @@ -204,7 +209,7 @@ class _State extends FormFieldState { } @override - void didUpdateWidget(_Field old) { + void didUpdateWidget(Field old) { super.didUpdateWidget(old); if (widget.parent.controller == old.parent.controller) { return; @@ -262,7 +267,7 @@ class _State extends FormFieldState { } @override - _Field get widget => super.widget as _Field; + Field get widget => super.widget as Field; TextEditingController get _effectiveController => widget.parent.controller ?? _controller!.value; } diff --git a/forui/lib/src/widgets/text_field/text_field.dart b/forui/lib/src/widgets/text_field/text_field.dart index 8829ea4fd..34914e5d8 100644 --- a/forui/lib/src/widgets/text_field/text_field.dart +++ b/forui/lib/src/widgets/text_field/text_field.dart @@ -5,13 +5,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; -import 'package:meta/meta.dart'; import 'package:forui/forui.dart'; - -part 'text_field_state_style.dart'; -part 'text_field_style.dart'; -part 'text_form_field.dart'; +import 'package:forui/src/widgets/text_field/field.dart'; /// A text field. /// @@ -738,7 +734,7 @@ final class FTextField extends StatelessWidget { primaryColor: style.cursorColor, ), ), - child: _Field( + child: Field( parent: this, style: style, key: key, diff --git a/forui/lib/src/widgets/text_field/text_field_state_style.dart b/forui/lib/src/widgets/text_field/text_field_state_style.dart index e55ebbcfd..b8e7d5c1c 100644 --- a/forui/lib/src/widgets/text_field/text_field_state_style.dart +++ b/forui/lib/src/widgets/text_field/text_field_state_style.dart @@ -1,4 +1,9 @@ -part of 'text_field.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +import 'package:meta/meta.dart'; + +import 'package:forui/forui.dart'; /// A [FTextField] state's style. sealed class FTextFieldStateStyle with Diagnosticable { @@ -89,21 +94,6 @@ final class FTextFieldNormalStyle extends FTextFieldStateStyle { }) : super.inherit(formFieldStateStyle: formFieldNormaStyle); /// Returns a copy of this [FTextFieldStateStyle] with the given properties replaced. - /// - /// ```dart - /// final style = FTextFieldNormalStyle( - /// labelTextStyle: ..., - /// contentTextStyle: ..., - /// // Other arguments omitted for brevity - /// ); - /// - /// final copy = style.copyWith( - /// contentTextStyle: ..., - /// ); - /// - /// print(style.labelTextStyle == copy.labelTextStyle); // true - /// print(style.contentTextStyle == copy.contentTextStyle); // false - /// ``` @useResult FTextFieldNormalStyle copyWith({ TextStyle? labelTextStyle, @@ -180,21 +170,6 @@ final class FTextFieldErrorStyle extends FTextFieldStateStyle { super.inherit(formFieldStateStyle: formFieldErrorStyle); /// Returns a copy of this [FTextFieldStateStyle] with the given properties replaced. - /// - /// ```dart - /// final style = FTextFieldErrorStyle( - /// labelTextStyle: ..., - /// contentTextStyle: ..., - /// // Other arguments omitted for brevity - /// ); - /// - /// final copy = style.copyWith( - /// contentTextStyle: ..., - /// ); - /// - /// print(style.labelTextStyle == copy.labelTextStyle); // true - /// print(style.contentTextStyle == copy.contentTextStyle); // false - /// ``` @useResult FTextFieldErrorStyle copyWith({ TextStyle? errorTextStyle, diff --git a/forui/lib/src/widgets/text_field/text_field_style.dart b/forui/lib/src/widgets/text_field/text_field_style.dart index 2502aca63..531b3b020 100644 --- a/forui/lib/src/widgets/text_field/text_field_style.dart +++ b/forui/lib/src/widgets/text_field/text_field_style.dart @@ -1,4 +1,9 @@ -part of 'text_field.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; + +import 'package:meta/meta.dart'; + +import 'package:forui/forui.dart'; /// [FTextFieldStyle]'s style. final class FTextFieldStyle with Diagnosticable { @@ -85,21 +90,6 @@ final class FTextFieldStyle with Diagnosticable { ); /// Returns a copy of this [FTextFieldStyle] with the given properties replaced. - /// - /// ```dart - /// final style = FTextFieldStyle( - /// enabledStyle: ..., - /// disabledStyle: ..., - /// // Other arguments omitted for brevity - /// ); - /// - /// final copy = style.copyWith( - /// disabledStyle: ..., - /// ); - /// - /// print(style.enabledStyle == copy.enabledStyle); // true - /// print(style.disabledStyle == copy.disabledStyle); // false - /// ``` @useResult FTextFieldStyle copyWith({ Brightness? keyboardAppearance, diff --git a/forui/lib/widgets/text_field.dart b/forui/lib/widgets/text_field.dart index d2b398a68..55e35828b 100644 --- a/forui/lib/widgets/text_field.dart +++ b/forui/lib/widgets/text_field.dart @@ -6,3 +6,5 @@ library forui.widgets.text_field; export '../src/widgets/text_field/text_field.dart'; +export '../src/widgets/text_field/text_field_style.dart'; +export '../src/widgets/text_field/text_field_state_style.dart'; diff --git a/forui/test/golden/divider/zinc-dark-horizontal.png b/forui/test/golden/divider/zinc-dark-Axis.horizontal.png similarity index 64% rename from forui/test/golden/divider/zinc-dark-horizontal.png rename to forui/test/golden/divider/zinc-dark-Axis.horizontal.png index 451c6b2ca..b369b559b 100644 Binary files a/forui/test/golden/divider/zinc-dark-horizontal.png and b/forui/test/golden/divider/zinc-dark-Axis.horizontal.png differ diff --git a/forui/test/golden/divider/zinc-dark-vertical.png b/forui/test/golden/divider/zinc-dark-Axis.vertical.png similarity index 60% rename from forui/test/golden/divider/zinc-dark-vertical.png rename to forui/test/golden/divider/zinc-dark-Axis.vertical.png index 694944d97..494e6ec7b 100644 Binary files a/forui/test/golden/divider/zinc-dark-vertical.png and b/forui/test/golden/divider/zinc-dark-Axis.vertical.png differ diff --git a/forui/test/golden/divider/zinc-light-horizontal.png b/forui/test/golden/divider/zinc-light-Axis.horizontal.png similarity index 66% rename from forui/test/golden/divider/zinc-light-horizontal.png rename to forui/test/golden/divider/zinc-light-Axis.horizontal.png index 3d8945095..302a8f1bb 100644 Binary files a/forui/test/golden/divider/zinc-light-horizontal.png and b/forui/test/golden/divider/zinc-light-Axis.horizontal.png differ diff --git a/forui/test/golden/divider/zinc-light-vertical.png b/forui/test/golden/divider/zinc-light-Axis.vertical.png similarity index 74% rename from forui/test/golden/divider/zinc-light-vertical.png rename to forui/test/golden/divider/zinc-light-Axis.vertical.png index 6de5a6251..bd20e37f4 100644 Binary files a/forui/test/golden/divider/zinc-light-vertical.png and b/forui/test/golden/divider/zinc-light-Axis.vertical.png differ diff --git a/forui/test/src/widgets/divider_golden_test.dart b/forui/test/src/widgets/divider_golden_test.dart index a2815eb13..3d1597153 100644 --- a/forui/test/src/widgets/divider_golden_test.dart +++ b/forui/test/src/widgets/divider_golden_test.dart @@ -11,8 +11,8 @@ import '../test_scaffold.dart'; void main() { group('FDivider', () { for (final (name, theme, _) in TestScaffold.themes) { - for (final (orientation, value) in [('horizontal', false), ('vertical', true)]) { - testWidgets('$name - $orientation', (tester) async { + for (final axis in Axis.values) { + testWidgets('$name - $axis', (tester) async { final children = [ Container( width: 100, @@ -22,7 +22,7 @@ void main() { border: Border.all(color: theme.colorScheme.secondary), ), ), - FDivider(vertical: value), + FDivider(axis: axis), Container( width: 100, height: 100, @@ -36,7 +36,7 @@ void main() { await tester.pumpWidget( TestScaffold( data: theme, - child: value + child: axis == Axis.vertical ? Row( mainAxisAlignment: MainAxisAlignment.center, children: children, @@ -50,7 +50,7 @@ void main() { await expectLater( find.byType(TestScaffold), - matchesGoldenFile('divider/$name-$orientation.png'), + matchesGoldenFile('divider/$name-$axis.png'), ); }); } diff --git a/samples/lib/widgets/divider.dart b/samples/lib/widgets/divider.dart index bf0412868..28b4a37ef 100644 --- a/samples/lib/widgets/divider.dart +++ b/samples/lib/widgets/divider.dart @@ -46,12 +46,12 @@ class DividerPage extends SampleScaffold { 'Blog', style: typography.sm.copyWith(color: colorScheme.foreground), ), - const FDivider(vertical: true), + const FDivider(axis: Axis.vertical), Text( 'Docs', style: typography.sm.copyWith(color: colorScheme.foreground), ), - const FDivider(vertical: true), + const FDivider(axis: Axis.vertical), Text( 'Source', style: typography.sm.copyWith(color: colorScheme.foreground),