diff --git a/forui/example/lib/main.dart b/forui/example/lib/main.dart index 72e5c49d7..0e4cdb590 100644 --- a/forui/example/lib/main.dart +++ b/forui/example/lib/main.dart @@ -82,11 +82,39 @@ class ExampleWidget extends StatelessWidget { ), const SizedBox(height: 10), FTabs( + height: 35, tabs: [ - ('Account', FTabContent(child: Column(children: [Container(color: Colors.red, height: 30,)],),)), - ('Password', FTabContent(child: Column(children: [Container(color: Colors.blue, height: 30,)],),)), + MapEntry( + 'Account', + FTabContent( + title: 'Account', + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + color: Colors.red, + height: 100, + ), + ], + ), + ), + ), + MapEntry( + 'Password', + FTabContent( + title: 'Password', + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + color: Colors.blue, + height: 100, + ) + ], + ), + ), + ), ], - ) ], ); diff --git a/forui/lib/src/theme/theme_data.dart b/forui/lib/src/theme/theme_data.dart index 298ed24b9..f855845f5 100644 --- a/forui/lib/src/theme/theme_data.dart +++ b/forui/lib/src/theme/theme_data.dart @@ -1,7 +1,6 @@ import 'package:flutter/foundation.dart'; import 'package:forui/forui.dart'; -import 'package:forui/src/widgets/tabs/tabs.dart'; /// The color scheme, fonts, overarching style, and widget specific styles used to configure child Forui widgets. class FThemeData with Diagnosticable { diff --git a/forui/lib/src/widgets/tabs/tab_content.dart b/forui/lib/src/widgets/tabs/tab_content.dart index e685bde67..1cc00fe63 100644 --- a/forui/lib/src/widgets/tabs/tab_content.dart +++ b/forui/lib/src/widgets/tabs/tab_content.dart @@ -4,24 +4,30 @@ final class FTabContent extends StatelessWidget { final String? title; final String? subtitle; final Widget? child; - final FCardContentStyle? style; + final FTabContentStyle? style; - const FTabContent({this.title, this.subtitle, this.child, this.style, super.key}); + const FTabContent( + {this.title, this.subtitle, this.child, this.style, super.key}); @override Widget build(BuildContext context) { final font = context.theme.font; - final style = this.style ?? context.theme.cardStyle.content; - return Padding( + final style = this.style ?? context.theme.tabsStyle.content; + return Container( + decoration: style.decoration, padding: style.padding, child: Column( + mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ if (title != null) Text(title!, style: style.title.withFont(font)), - if (subtitle != null) Text(subtitle!, style: style.subtitle.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), + padding: (title == null && subtitle == null) + ? const EdgeInsets.only(top: 4) + : const EdgeInsets.only(top: 10), child: child!, ), ], @@ -41,6 +47,8 @@ final class FTabContent extends StatelessWidget { /// A card content's style. final class FTabContentStyle with Diagnosticable { + /// The decoration. + final BoxDecoration decoration; /// The padding. final EdgeInsets padding; @@ -52,10 +60,17 @@ final class FTabContentStyle with Diagnosticable { final TextStyle subtitle; /// Creates a [FTabContentStyle]. - const FTabContentStyle({required this.padding, required this.title, required this.subtitle}); + const FTabContentStyle({ + required this.decoration, + required this.padding, + required this.title, + required this.subtitle, + }); - /// Creates a [FCardContentStyle] that inherits its properties from [colorScheme] and [font]. - FTabContentStyle.inherit({required FColorScheme colorScheme, required FFont font}): + /// Creates a [FTabContentStyle] that inherits its properties from [colorScheme] and [font]. + FTabContentStyle.inherit( + {required FColorScheme colorScheme, required FFont font,required FStyle style}) + : decoration = BoxDecoration(borderRadius: style.borderRadius,border: Border.all(color: colorScheme.border)), padding = const EdgeInsets.fromLTRB(16, 12, 16, 16), title = TextStyle( fontSize: font.base, @@ -68,11 +83,17 @@ final class FTabContentStyle with Diagnosticable { ); /// Creates a copy of this [FCardContentStyle] with the given properties replaced. - FTabContentStyle copyWith({EdgeInsets? padding, TextStyle? title, TextStyle? subtitle}) => FTabContentStyle( - padding: padding ?? this.padding, - title: title ?? this.title, - subtitle: subtitle ?? this.subtitle, - ); + FTabContentStyle copyWith( + {BoxDecoration? decoration, + EdgeInsets? padding, + TextStyle? title, + TextStyle? subtitle}) => + FTabContentStyle( + decoration: decoration ?? this.decoration, + padding: padding ?? this.padding, + title: title ?? this.title, + subtitle: subtitle ?? this.subtitle, + ); @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { @@ -80,16 +101,24 @@ final class FTabContentStyle with Diagnosticable { properties ..add(DiagnosticsProperty('padding', padding)) ..add(DiagnosticsProperty('title', title)) - ..add(DiagnosticsProperty('subtitle', subtitle)); + ..add(DiagnosticsProperty('subtitle', subtitle)) + ..add(DiagnosticsProperty('decoration', decoration)); } @override - bool operator ==(Object other) => identical(this, other) || other is FCardContentStyle && - runtimeType == other.runtimeType && - padding == other.padding && - title == other.title && - subtitle == other.subtitle; + bool operator ==(Object other) => + identical(this, other) || + other is FTabContentStyle && + runtimeType == other.runtimeType && + decoration == other.decoration && + padding == other.padding && + title == other.title && + subtitle == other.subtitle; @override - int get hashCode => padding.hashCode ^ title.hashCode ^ subtitle.hashCode; + int get hashCode => + decoration.hashCode ^ + padding.hashCode ^ + title.hashCode ^ + subtitle.hashCode; } diff --git a/forui/lib/src/widgets/tabs/tabs.dart b/forui/lib/src/widgets/tabs/tabs.dart index 46e568b67..e9cbdeb4d 100644 --- a/forui/lib/src/widgets/tabs/tabs.dart +++ b/forui/lib/src/widgets/tabs/tabs.dart @@ -7,9 +7,9 @@ part 'tabs_style.dart'; part 'tab_content.dart'; /// A [FTabs] that allows switching between tabs. -class FTabs extends StatelessWidget { +class FTabs extends StatefulWidget { /// The tab and it's corresponding view. - final List<(String, Widget)> tabs; + final List> tabs; /// The height of the tab button. final double? height; @@ -30,65 +30,76 @@ class FTabs extends StatelessWidget { const FTabs({ required this.tabs, this.height, - this.spacing = 2, + this.spacing = 10, this.onTap, this.initialIndex = 0, this.style, super.key, }); + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(IterableProperty>('tabs', tabs)) + ..add(DoubleProperty('height', height)) + ..add(DiagnosticsProperty('spacing', spacing)) + ..add(ObjectFlagProperty?>.has('onTap', onTap)) + ..add(IntProperty('initialIndex', initialIndex)) + ..add(DiagnosticsProperty('style', style)); + } + + @override + State createState() => _FTabsState(); +} + +class _FTabsState extends State with SingleTickerProviderStateMixin { + late int _selectedTab; + + @override + void initState() { + super.initState(); + _selectedTab = widget.initialIndex; + } + @override Widget build(BuildContext context) { - final style = this.style ?? context.theme.tabsStyle; + final style = widget.style ?? context.theme.tabsStyle; + final tabs = widget.tabs; return DefaultTabController( - initialIndex: initialIndex, + initialIndex: widget.initialIndex, length: tabs.length, child: Column( children: [ DecoratedBox( decoration: style.decoration, child: TabBar( - indicatorSize: TabBarIndicatorSize.tab, - onTap: onTap, padding: style.padding, + indicatorSize: style.indicatorSize, + indicator: style.indicator, unselectedLabelStyle: style.unselectedLabel, - unselectedLabelColor: style.unselectedColor, labelStyle: style.selectedLabel, - labelColor: style.selectedColor, - indicatorColor: Colors.transparent, dividerColor: Colors.transparent, - indicator: style.indicator, tabs: [ - for (final (text, _) in tabs) + for (final child in tabs) Tab( - height: height, - child: Text(text), + height: widget.height, + child: Text(child.key), ) ], + onTap: (index) { + setState(() { + _selectedTab = index; + }); + widget.onTap?.call(_selectedTab); + }, ), ), - SizedBox(height: spacing), - Flexible( - child: TabBarView( - physics: const BouncingScrollPhysics(), - children: [for (final (_, widget) in tabs) widget], - ), - ), + SizedBox(height: widget.spacing), + tabs[_selectedTab].value, ], ), ); } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(IterableProperty<(String, Widget)>('tabs', tabs)) - ..add(DoubleProperty('height', height)) - ..add(DiagnosticsProperty('spacing', spacing)) - ..add(ObjectFlagProperty?>.has('onTap', onTap)) - ..add(IntProperty('initialIndex', initialIndex)) - ..add(DiagnosticsProperty('style', style)); - } } diff --git a/forui/lib/src/widgets/tabs/tabs_style.dart b/forui/lib/src/widgets/tabs/tabs_style.dart index 52c053432..700c6aa14 100644 --- a/forui/lib/src/widgets/tabs/tabs_style.dart +++ b/forui/lib/src/widgets/tabs/tabs_style.dart @@ -8,21 +8,18 @@ final class FTabsStyle with Diagnosticable { /// The padding. final EdgeInsets padding; - /// The color of the unselected tabs. - final Color unselectedColor; - /// The [TextStyle] of the label final TextStyle unselectedLabel; - /// The color of the selected tabs. - final Color selectedColor; - /// The [TextStyle] of the label final TextStyle selectedLabel; - /// The decoration. + /// The indicator. final BoxDecoration indicator; + /// The indicator size. + final TabBarIndicatorSize indicatorSize; + /// The [FTabContent] style. final FTabContentStyle content; @@ -31,10 +28,9 @@ final class FTabsStyle with Diagnosticable { required this.decoration, required this.padding, required this.unselectedLabel, - required this.unselectedColor, required this.selectedLabel, - required this.selectedColor, required this.indicator, + required this.indicatorSize, required this.content, }); @@ -46,59 +42,56 @@ final class FTabsStyle with Diagnosticable { : decoration = BoxDecoration( border: Border.all(color: colorScheme.border), borderRadius: style.borderRadius, - color: colorScheme.background, + color: colorScheme.border, ), - padding = const EdgeInsets.all(5), - unselectedColor = colorScheme.muted, + padding = const EdgeInsets.all(4), unselectedLabel = TextStyle( fontSize: font.sm, fontWeight: FontWeight.w600, - color: colorScheme.foreground.withOpacity(0.5), + color: colorScheme.mutedForeground, ), - selectedColor = colorScheme.background, selectedLabel = TextStyle( fontSize: font.sm, fontWeight: FontWeight.w600, - color: colorScheme.foreground, + color: colorScheme.primary, ), + indicatorSize = TabBarIndicatorSize.tab, indicator = BoxDecoration( - color: colorScheme.primary, - borderRadius: BorderRadius.circular(50), + color: colorScheme.background, + borderRadius: style.borderRadius, ), - content = - FTabContentStyle.inherit(colorScheme: colorScheme, font: font); + content = FTabContentStyle.inherit( + colorScheme: colorScheme, font: font, style: style); /// Creates a copy of this [FCardStyle] with the given properties replaced. FTabsStyle copyWith({ - Color? selectedColor, - Color? unselectedColor, EdgeInsets? padding, BoxDecoration? decoration, TextStyle? selectedLabel, TextStyle? unselectedLabel, BoxDecoration? indicator, + TabBarIndicatorSize? indicatorSize, FTabContentStyle? content, }) => FTabsStyle( - selectedColor: selectedColor ?? this.selectedColor, - unselectedColor: unselectedColor ?? this.unselectedColor, padding: padding ?? this.padding, decoration: decoration ?? this.decoration, selectedLabel: selectedLabel ?? this.selectedLabel, unselectedLabel: unselectedLabel ?? this.unselectedLabel, + indicatorSize: indicatorSize ?? this.indicatorSize, indicator: indicator ?? this.indicator, content: content ?? this.content, ); + @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); - properties..add(DiagnosticsProperty('decoration', decoration)) - ..add(DiagnosticsProperty('padding', padding)) - ..add(ColorProperty('unselectedColor', unselectedColor)) - ..add(DiagnosticsProperty('unselectedLabel', unselectedLabel)) - ..add(ColorProperty('selectedColor', selectedColor)) - ..add(DiagnosticsProperty('selectedLabel', selectedLabel)) - ..add(DiagnosticsProperty('indicator', indicator)) - ..add(DiagnosticsProperty('content', content)); + properties + ..add(DiagnosticsProperty('decoration', decoration)) + ..add(DiagnosticsProperty('padding', padding)) + ..add(DiagnosticsProperty('unselectedLabel', unselectedLabel)) + ..add(DiagnosticsProperty('selectedLabel', selectedLabel)) + ..add(DiagnosticsProperty('indicator', indicator)) + ..add(DiagnosticsProperty('content', content)); } }