diff --git a/lib/app.dart b/lib/app.dart index be358a24..da9a6d7a 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -48,7 +48,14 @@ class MyApp extends StatelessWidget { toolbarTextStyleCubit: appBarToolbarTextStyleCubit, ); - final tabBarThemeCubit = TabBarThemeCubit(); + final tabBarLabelTextStyleCubit = TabBarLabelTextStyleCubit(); + final tabBarUnselectedLabelTextStyleCubit = + TabBarUnselectedLabelTextStyleCubit(); + final tabBarThemeCubit = TabBarThemeCubit( + labelTextStyleCubit: tabBarLabelTextStyleCubit, + unselectedLabelTextStyleCubit: tabBarUnselectedLabelTextStyleCubit, + ); + final bottomNavigationBarThemeCubit = BottomNavigationBarThemeCubit(); final floatingActionButtonThemeCubit = FloatingActionButtonThemeCubit(); @@ -146,6 +153,8 @@ class MyApp extends StatelessWidget { BlocProvider(create: (_) => appBarTitleTextStyleCubit), BlocProvider(create: (_) => appBarToolbarTextStyleCubit), BlocProvider(create: (_) => tabBarThemeCubit), + BlocProvider(create: (_) => tabBarLabelTextStyleCubit), + BlocProvider(create: (_) => tabBarUnselectedLabelTextStyleCubit), BlocProvider(create: (_) => bottomNavigationBarThemeCubit), BlocProvider(create: (_) => floatingActionButtonThemeCubit), BlocProvider(create: (_) => elevatedButtonThemeCubit), diff --git a/lib/app_bar_theme/cubit/app_bar_theme_cubit.dart b/lib/app_bar_theme/cubit/app_bar_theme_cubit.dart index 7deacafb..d0b14cbb 100644 --- a/lib/app_bar_theme/cubit/app_bar_theme_cubit.dart +++ b/lib/app_bar_theme/cubit/app_bar_theme_cubit.dart @@ -147,16 +147,10 @@ class AppBarIconThemeCubit extends AbstractIconThemeCubit {} class AppBarTitleTextStyleCubit extends AbstractTextStyleCubit { AppBarTitleTextStyleCubit() - : super( - typeScale: _titleTypeScale, - isBaseStyleBlack: false, - ); + : super(typeScale: _titleTypeScale, isBaseStyleBlack: false); } class AppBarToolbarTextStyleCubit extends AbstractTextStyleCubit { AppBarToolbarTextStyleCubit() - : super( - typeScale: _toolbarTypeScale, - isBaseStyleBlack: false, - ); + : super(typeScale: _toolbarTypeScale, isBaseStyleBlack: false); } diff --git a/lib/app_bar_theme/view/app_bar_theme_editor.dart b/lib/app_bar_theme/view/app_bar_theme_editor.dart index 62b42ec7..f862ab49 100644 --- a/lib/app_bar_theme/view/app_bar_theme_editor.dart +++ b/lib/app_bar_theme/view/app_bar_theme_editor.dart @@ -38,7 +38,7 @@ class AppBarThemeEditor extends ExpansionPanelItem { TitleTextStyleCard(), ToolbarTextStyleCard(), ], - ) + ), ], ); } diff --git a/lib/tab_bar_theme/cubit/tab_bar_theme_cubit.dart b/lib/tab_bar_theme/cubit/tab_bar_theme_cubit.dart index 79eda9d7..cb2f123b 100644 --- a/lib/tab_bar_theme/cubit/tab_bar_theme_cubit.dart +++ b/lib/tab_bar_theme/cubit/tab_bar_theme_cubit.dart @@ -1,18 +1,63 @@ +import 'dart:async'; + +import 'package:appainter/abstract_text_style/abstract_text_style.dart'; +import 'package:appainter/services/services.dart'; import 'package:appainter_annotations/annotations.dart'; import 'package:bloc/bloc.dart'; import 'package:copy_with_extension/copy_with_extension.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; -import 'package:appainter/services/services.dart'; part 'tab_bar_theme_cubit.g.dart'; part 'tab_bar_theme_state.dart'; +const _labelTypeScale = TypeScale.bodyText1; + @ThemeDocs(extraPropertyTypes: {'TabBarIndicatorSize'}) class TabBarThemeCubit extends Cubit { - TabBarThemeCubit() : super(const TabBarThemeState()); + TabBarThemeCubit({ + required this.labelTextStyleCubit, + required this.unselectedLabelTextStyleCubit, + }) : super(const TabBarThemeState()) { + labelTextStyleCubitSubscription = labelTextStyleCubit.stream.listen( + (otherState) { + final theme = state.theme.copyWith(labelStyle: otherState.style); + emit(state.copyWith(theme: theme)); + }, + ); + unselectedLabelTextStyleCubitSubscription = + unselectedLabelTextStyleCubit.stream.listen( + (otherState) { + final theme = state.theme.copyWith( + unselectedLabelStyle: otherState.style, + ); + emit(state.copyWith(theme: theme)); + }, + ); + } + + final TabBarLabelTextStyleCubit labelTextStyleCubit; + final TabBarUnselectedLabelTextStyleCubit unselectedLabelTextStyleCubit; + + late final StreamSubscription labelTextStyleCubitSubscription; + late final StreamSubscription unselectedLabelTextStyleCubitSubscription; + + static final defaultLabelTextStyle = kWhiteTextStyles[_labelTypeScale]!; - void themeChanged(TabBarTheme theme) => emit(state.copyWith(theme: theme)); + @override + Future close() { + labelTextStyleCubitSubscription.cancel(); + unselectedLabelTextStyleCubitSubscription.cancel(); + return super.close(); + } + + void themeChanged(TabBarTheme theme) { + labelTextStyleCubit.styleChanged(theme.labelStyle ?? defaultLabelTextStyle); + unselectedLabelTextStyleCubit.styleChanged( + theme.unselectedLabelStyle ?? defaultLabelTextStyle, + ); + emit(state.copyWith(theme: theme)); + } void labelColorChanged(Color color) { final theme = state.theme.copyWith(labelColor: color); @@ -32,3 +77,13 @@ class TabBarThemeCubit extends Cubit { } } } + +class TabBarLabelTextStyleCubit extends AbstractTextStyleCubit { + TabBarLabelTextStyleCubit() + : super(typeScale: _labelTypeScale, isBaseStyleBlack: false); +} + +class TabBarUnselectedLabelTextStyleCubit extends AbstractTextStyleCubit { + TabBarUnselectedLabelTextStyleCubit() + : super(typeScale: _labelTypeScale, isBaseStyleBlack: false); +} diff --git a/lib/tab_bar_theme/view/tab_bar_theme_editor.dart b/lib/tab_bar_theme/view/tab_bar_theme_editor.dart index 15cc3c75..f86e8147 100644 --- a/lib/tab_bar_theme/view/tab_bar_theme_editor.dart +++ b/lib/tab_bar_theme/view/tab_bar_theme_editor.dart @@ -1,10 +1,11 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:appainter/abstract_text_style/abstract_text_style.dart'; import 'package:appainter/color_theme/color_theme.dart'; import 'package:appainter/common/common.dart'; import 'package:appainter/services/services.dart'; import 'package:appainter/tab_bar_theme/tab_bar_theme.dart'; import 'package:appainter/widgets/widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; class TabBarThemeEditor extends ExpansionPanelItem { const TabBarThemeEditor({Key? key}) : super(key: key); @@ -14,12 +15,26 @@ class TabBarThemeEditor extends ExpansionPanelItem { @override Widget build(BuildContext context) { - return SideBySideList( - padding: kPaddingAll, + return NestedListView( children: [ - _LabelColorPicker(), - _UnselectedLabelColorPicker(), - _IndicatorSizeDropdown(), + SideBySideList( + padding: kPaddingAll, + children: [ + _LabelColorPicker(), + _UnselectedLabelColorPicker(), + _IndicatorSizeDropdown(), + ], + ), + MyExpansionPanelList( + items: const [ + _LabelTextStyleCard( + key: Key('tabBarThemeEditor_labelTextStyleCard'), + ), + _UnselectedLabelTextStyleCard( + key: Key('tabBarThemeEditor_unselectedLabelTextStyleCard'), + ), + ], + ) ], ); } @@ -91,3 +106,25 @@ class _IndicatorSizeDropdown extends StatelessWidget { ); } } + +class _LabelTextStyleCard + extends AbstractTextStyleEditor { + const _LabelTextStyleCard({Key? key}) : super(key: key); + + @override + String get header => 'Label text style'; + + @override + String? get tooltip => TabBarThemeDocs.labelStyle; +} + +class _UnselectedLabelTextStyleCard + extends AbstractTextStyleEditor { + const _UnselectedLabelTextStyleCard({Key? key}) : super(key: key); + + @override + String get header => 'Unselected label text style'; + + @override + String? get tooltip => TabBarThemeDocs.unselectedLabelStyle; +} diff --git a/test/mocks.dart b/test/mocks.dart index 36d841d1..14cbebce 100644 --- a/test/mocks.dart +++ b/test/mocks.dart @@ -62,6 +62,12 @@ class MockTabBarThemeCubit extends MockCubit class FakeTabBarThemeState extends Fake implements TabBarThemeState {} +class MockTabBarLabelTextStyleCubit extends MockCubit + implements TabBarLabelTextStyleCubit {} + +class MockTabBarUnselectedLabelTextStyleCubit extends MockCubit + implements TabBarUnselectedLabelTextStyleCubit {} + class MockBottomNavigationBarThemeCubit extends MockCubit implements BottomNavigationBarThemeCubit {} diff --git a/test/pump_app.dart b/test/pump_app.dart index 6ecfe60c..fb43946d 100644 --- a/test/pump_app.dart +++ b/test/pump_app.dart @@ -52,6 +52,12 @@ extension PumpApp on WidgetTester { MockAppBarToolbarTextStyleCubit(); final TabBarThemeCubit tabBarThemeCubit = MockTabBarThemeCubit(); + final TabBarLabelTextStyleCubit tabBarLabelTextStyleCubit = + MockTabBarLabelTextStyleCubit(); + final TabBarUnselectedLabelTextStyleCubit + tabBarUnselectedLabelTextStyleCubit = + MockTabBarUnselectedLabelTextStyleCubit(); + final BottomNavigationBarThemeCubit bottomNavigationBarThemeCubit = MockBottomNavigationBarThemeCubit(); @@ -126,6 +132,13 @@ extension PumpApp on WidgetTester { ); when(() => tabBarThemeCubit.state).thenReturn(const TabBarThemeState()); + when(() => tabBarLabelTextStyleCubit.state).thenReturn( + TextStyleState(style: TabBarThemeCubit.defaultLabelTextStyle), + ); + when(() => tabBarUnselectedLabelTextStyleCubit.state).thenReturn( + TextStyleState(style: TabBarThemeCubit.defaultLabelTextStyle), + ); + when(() => bottomNavigationBarThemeCubit.state).thenReturn( const BottomNavigationBarThemeState(), ); @@ -185,6 +198,8 @@ extension PumpApp on WidgetTester { BlocProvider.value(value: appBarTitleTextStyleCubit), BlocProvider.value(value: appBarToolbarTextStyleCubit), BlocProvider.value(value: tabBarThemeCubit), + BlocProvider.value(value: tabBarLabelTextStyleCubit), + BlocProvider.value(value: tabBarUnselectedLabelTextStyleCubit), BlocProvider.value(value: bottomNavigationBarThemeCubit), BlocProvider.value(value: floatingActionButtonThemeCubit), BlocProvider.value(value: elevatedButtonThemeCubit), diff --git a/test/tab_bar_theme/tab_bar_theme_cubit_test.dart b/test/tab_bar_theme/tab_bar_theme_cubit_test.dart index 304f63c5..a6e3a0e8 100644 --- a/test/tab_bar_theme/tab_bar_theme_cubit_test.dart +++ b/test/tab_bar_theme/tab_bar_theme_cubit_test.dart @@ -1,19 +1,28 @@ +import 'package:appainter/abstract_text_style/abstract_text_style.dart'; +import 'package:appainter/services/services.dart'; +import 'package:appainter/tab_bar_theme/tab_bar_theme.dart'; import 'package:bloc_test/bloc_test.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:appainter/services/services.dart'; -import 'package:appainter/tab_bar_theme/tab_bar_theme.dart'; import 'package:random_color_scheme/random_color_scheme.dart'; import '../utils.dart'; void main() { - late TabBarThemeCubit cubit; + late TabBarThemeCubit themeCubit; + late TabBarLabelTextStyleCubit labelTextStyleCubit; + late TabBarUnselectedLabelTextStyleCubit unselectedLabelTextStyleCubit; + late TabBarTheme theme; late Color color; setUp(() { - cubit = TabBarThemeCubit(); + labelTextStyleCubit = TabBarLabelTextStyleCubit(); + unselectedLabelTextStyleCubit = TabBarUnselectedLabelTextStyleCubit(); + themeCubit = TabBarThemeCubit( + labelTextStyleCubit: labelTextStyleCubit, + unselectedLabelTextStyleCubit: unselectedLabelTextStyleCubit, + ); color = getRandomColor(); final colorScheme = randomColorSchemeLight(shouldPrint: false); @@ -21,22 +30,35 @@ void main() { }); blocTest( - 'should emit theme', - build: () => cubit, + 'emit theme', + build: () => themeCubit, act: (cubit) => cubit.themeChanged(theme), - expect: () => [TabBarThemeState(theme: theme)], + expect: () => [ + TabBarThemeState(theme: theme), + TabBarThemeState( + theme: theme.copyWith( + labelStyle: TabBarThemeCubit.defaultLabelTextStyle, + ), + ), + TabBarThemeState( + theme: theme.copyWith( + labelStyle: TabBarThemeCubit.defaultLabelTextStyle, + unselectedLabelStyle: TabBarThemeCubit.defaultLabelTextStyle, + ), + ), + ], ); blocTest( - 'should emit label color', - build: () => cubit, + 'emit label color', + build: () => themeCubit, act: (cubit) => cubit.labelColorChanged(color), expect: () => [TabBarThemeState(theme: TabBarTheme(labelColor: color))], ); blocTest( - 'should emit unselected label color', - build: () => cubit, + 'emit unselected label color', + build: () => themeCubit, act: (cubit) => cubit.unselectedLabelColorChanged(color), expect: () { return [ @@ -48,8 +70,8 @@ void main() { group('test indicator size', () { for (var size in TabBarIndicatorSize.values) { blocTest( - 'should emit $size', - build: () => cubit, + 'emit $size', + build: () => themeCubit, act: (cubit) { cubit.indicatorSizeChanged(UtilService.enumToString(size)); }, @@ -59,4 +81,16 @@ void main() { ); } }); + + test('initialise label text style cubit', () { + final cubit = TabBarLabelTextStyleCubit(); + expect(cubit.typeScale, equals(TypeScale.bodyText1)); + expect(cubit.isBaseStyleBlack, equals(false)); + }); + + test('initialise unselected label text style cubit', () { + final cubit = TabBarUnselectedLabelTextStyleCubit(); + expect(cubit.typeScale, equals(TypeScale.bodyText1)); + expect(cubit.isBaseStyleBlack, equals(false)); + }); } diff --git a/test/tab_bar_theme/tab_bar_theme_editor_test.dart b/test/tab_bar_theme/tab_bar_theme_editor_test.dart index 54f9d6d0..714655ff 100644 --- a/test/tab_bar_theme/tab_bar_theme_editor_test.dart +++ b/test/tab_bar_theme/tab_bar_theme_editor_test.dart @@ -1,3 +1,4 @@ +import 'package:appainter/abstract_text_style/abstract_text_style.dart'; import 'package:appainter/color_theme/color_theme.dart'; import 'package:appainter/services/util_service.dart'; import 'package:appainter/tab_bar_theme/tab_bar_theme.dart'; @@ -16,15 +17,26 @@ void main() { final widgetTesters = WidgetTesters(expandText: 'Tab bar'); late TabBarThemeCubit tabBarThemeCubit; + late TabBarLabelTextStyleCubit tabBarLabelTextStyleCubit; + late TabBarUnselectedLabelTextStyleCubit tabBarUnselectedLabelTextStyleCubit; late ColorThemeCubit colorThemeCubit; late Color color; setUp(() { tabBarThemeCubit = MockTabBarThemeCubit(); + tabBarLabelTextStyleCubit = MockTabBarLabelTextStyleCubit(); + tabBarUnselectedLabelTextStyleCubit = + MockTabBarUnselectedLabelTextStyleCubit(); colorThemeCubit = MockColorThemeCubit(); color = getRandomColor(); when(() => tabBarThemeCubit.state).thenReturn(const TabBarThemeState()); + when(() => tabBarLabelTextStyleCubit.state).thenReturn( + TextStyleState(style: TabBarThemeCubit.defaultLabelTextStyle), + ); + when(() => tabBarUnselectedLabelTextStyleCubit.state).thenReturn( + TextStyleState(style: TabBarThemeCubit.defaultLabelTextStyle), + ); when(() => colorThemeCubit.state).thenReturn(ColorThemeState()); }); @@ -38,6 +50,8 @@ void main() { MultiBlocProvider( providers: [ BlocProvider.value(value: tabBarThemeCubit), + BlocProvider.value(value: tabBarLabelTextStyleCubit), + BlocProvider.value(value: tabBarUnselectedLabelTextStyleCubit), BlocProvider.value(value: colorThemeCubit), ], child: MaterialApp( @@ -47,8 +61,20 @@ void main() { ); } + testWidgets('display nested editors', (tester) async { + await pumpApp(tester, const TabBarThemeState()); + expect( + find.byKey(const Key('tabBarThemeEditor_labelTextStyleCard')), + findsOneWidget, + ); + expect( + find.byKey(const Key('tabBarThemeEditor_unselectedLabelTextStyleCard')), + findsOneWidget, + ); + }); + testWidgets( - 'label color picker should update with selected color', + 'label color picker update with selected color', (tester) async { final state = TabBarThemeState(theme: TabBarTheme(labelColor: color)); @@ -64,7 +90,7 @@ void main() { ); testWidgets( - 'unselected label color picker should update with selected color', + 'unselected label color picker update with selected color', (tester) async { final color = getRandomColor(); final state = TabBarThemeState( @@ -88,7 +114,7 @@ void main() { for (var size in TabBarIndicatorSize.values) { final sizeStr = UtilService.enumToString(size); testWidgets( - 'should update to $size', + 'update to $size', (tester) async { final state = TabBarThemeState( theme: TabBarTheme(indicatorSize: size),