diff --git a/lib/app.dart b/lib/app.dart index da9a6d7a..a3395e5d 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -56,7 +56,15 @@ class MyApp extends StatelessWidget { unselectedLabelTextStyleCubit: tabBarUnselectedLabelTextStyleCubit, ); - final bottomNavigationBarThemeCubit = BottomNavigationBarThemeCubit(); + final bottomNavigationBarLabelTextStyleCubit = + BottomNavigationBarLabelTextStyleCubit(); + final bottomNavigationBarUnselectedLabelTextStyleCubit = + BottomNavigationBarUnselectedLabelTextStyleCubit(); + final bottomNavigationBarThemeCubit = BottomNavigationBarThemeCubit( + labelTextStyleCubit: bottomNavigationBarLabelTextStyleCubit, + unselectedLabelTextStyleCubit: + bottomNavigationBarUnselectedLabelTextStyleCubit, + ); final floatingActionButtonThemeCubit = FloatingActionButtonThemeCubit(); final elevatedButtonThemeCubit = ElevatedButtonThemeCubit( @@ -156,6 +164,10 @@ class MyApp extends StatelessWidget { BlocProvider(create: (_) => tabBarLabelTextStyleCubit), BlocProvider(create: (_) => tabBarUnselectedLabelTextStyleCubit), BlocProvider(create: (_) => bottomNavigationBarThemeCubit), + BlocProvider(create: (_) => bottomNavigationBarLabelTextStyleCubit), + BlocProvider( + create: (_) => bottomNavigationBarUnselectedLabelTextStyleCubit, + ), BlocProvider(create: (_) => floatingActionButtonThemeCubit), BlocProvider(create: (_) => elevatedButtonThemeCubit), BlocProvider(create: (_) => outlinedButtonThemeCubit), diff --git a/lib/bottom_navigation_bar_theme/cubit/bottom_navigation_bar_theme_cubit.dart b/lib/bottom_navigation_bar_theme/cubit/bottom_navigation_bar_theme_cubit.dart index 2d008cb8..37d1d9d2 100644 --- a/lib/bottom_navigation_bar_theme/cubit/bottom_navigation_bar_theme_cubit.dart +++ b/lib/bottom_navigation_bar_theme/cubit/bottom_navigation_bar_theme_cubit.dart @@ -1,23 +1,70 @@ +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/util_service.dart'; part 'bottom_navigation_bar_theme_cubit.g.dart'; part 'bottom_navigation_bar_theme_state.dart'; +const _labelTypeScale = TypeScale.bodyText2; + @ThemeDocs( apiClassName: 'BottomNavigationBarThemeData', extraPropertyTypes: {'BottomNavigationBarType'}, ) class BottomNavigationBarThemeCubit extends Cubit { - BottomNavigationBarThemeCubit() - : super(const BottomNavigationBarThemeState()); + BottomNavigationBarThemeCubit({ + required this.labelTextStyleCubit, + required this.unselectedLabelTextStyleCubit, + }) : super(const BottomNavigationBarThemeState()) { + labelTextStyleCubitSubscription = labelTextStyleCubit.stream.listen( + (otherState) { + final theme = state.theme.copyWith( + selectedLabelStyle: 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 BottomNavigationBarLabelTextStyleCubit labelTextStyleCubit; + final BottomNavigationBarUnselectedLabelTextStyleCubit + unselectedLabelTextStyleCubit; + + late final StreamSubscription labelTextStyleCubitSubscription; + late final StreamSubscription unselectedLabelTextStyleCubitSubscription; + + static final defaultLabelTextStyle = kWhiteTextStyles[_labelTypeScale]!; + + @override + Future close() { + labelTextStyleCubitSubscription.cancel(); + unselectedLabelTextStyleCubitSubscription.cancel(); + return super.close(); + } void themeChanged(BottomNavigationBarThemeData theme) { + labelTextStyleCubit.styleChanged( + theme.selectedLabelStyle ?? defaultLabelTextStyle, + ); + unselectedLabelTextStyleCubit.styleChanged( + theme.unselectedLabelStyle ?? defaultLabelTextStyle, + ); emit(state.copyWith(theme: theme)); } @@ -69,3 +116,14 @@ class BottomNavigationBarThemeCubit } } } + +class BottomNavigationBarLabelTextStyleCubit extends AbstractTextStyleCubit { + BottomNavigationBarLabelTextStyleCubit() + : super(typeScale: _labelTypeScale, isBaseStyleBlack: false); +} + +class BottomNavigationBarUnselectedLabelTextStyleCubit + extends AbstractTextStyleCubit { + BottomNavigationBarUnselectedLabelTextStyleCubit() + : super(typeScale: _labelTypeScale, isBaseStyleBlack: false); +} diff --git a/lib/bottom_navigation_bar_theme/view/bottom_navigation_bar_theme_editor.dart b/lib/bottom_navigation_bar_theme/view/bottom_navigation_bar_theme_editor.dart index 127bc703..ee9332c7 100644 --- a/lib/bottom_navigation_bar_theme/view/bottom_navigation_bar_theme_editor.dart +++ b/lib/bottom_navigation_bar_theme/view/bottom_navigation_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/bottom_navigation_bar_theme/bottom_navigation_bar_theme.dart'; import 'package:appainter/color_theme/color_theme.dart'; import 'package:appainter/common/common.dart'; import 'package:appainter/services/services.dart'; import 'package:appainter/widgets/widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; class BottomNavigationBarThemeEditor extends ExpansionPanelItem { const BottomNavigationBarThemeEditor({Key? key}) : super(key: key); @@ -14,16 +15,32 @@ class BottomNavigationBarThemeEditor extends ExpansionPanelItem { @override Widget build(BuildContext context) { - return SideBySideList( - padding: kPaddingAll, + return NestedListView( children: [ - _TypeDropdown(), - _BackgroundColorPicker(), - _SelectedItemColorPicker(), - _UnselectedItemColorPicker(), - _ShowSelectedLabelsSwitch(), - _ShowUnselectedLabelsSwitch(), - _ElevationTextField(), + SideBySideList( + padding: kPaddingAll, + children: [ + _TypeDropdown(), + _BackgroundColorPicker(), + _SelectedItemColorPicker(), + _UnselectedItemColorPicker(), + _ShowSelectedLabelsSwitch(), + _ShowUnselectedLabelsSwitch(), + _ElevationTextField(), + ], + ), + MyExpansionPanelList( + items: const [ + _LabelTextStyleCard( + key: Key('bottomNavigationBarThemeEditor_labelTextStyleCard'), + ), + _UnselectedLabelTextStyleCard( + key: Key( + 'bottomNavigationBarThemeEditor_unselectedLabelTextStyleCard', + ), + ), + ], + ) ], ); } @@ -211,3 +228,25 @@ class _ElevationTextField extends StatelessWidget { ); } } + +class _LabelTextStyleCard + extends AbstractTextStyleEditor { + const _LabelTextStyleCard({Key? key}) : super(key: key); + + @override + String get header => 'Label text style'; + + @override + String? get tooltip => BottomNavigationBarThemeDocs.selectedLabelStyle; +} + +class _UnselectedLabelTextStyleCard extends AbstractTextStyleEditor< + BottomNavigationBarUnselectedLabelTextStyleCubit> { + const _UnselectedLabelTextStyleCard({Key? key}) : super(key: key); + + @override + String get header => 'Unselected label text style'; + + @override + String? get tooltip => BottomNavigationBarThemeDocs.unselectedLabelStyle; +} 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 f86e8147..b2ff5336 100644 --- a/lib/tab_bar_theme/view/tab_bar_theme_editor.dart +++ b/lib/tab_bar_theme/view/tab_bar_theme_editor.dart @@ -34,7 +34,7 @@ class TabBarThemeEditor extends ExpansionPanelItem { key: Key('tabBarThemeEditor_unselectedLabelTextStyleCard'), ), ], - ) + ), ], ); } diff --git a/test/bottom_navigation_bar_theme/bottom_navigation_bar_theme_cubit_test.dart b/test/bottom_navigation_bar_theme/bottom_navigation_bar_theme_cubit_test.dart index 9038dc30..0cf08f0b 100644 --- a/test/bottom_navigation_bar_theme/bottom_navigation_bar_theme_cubit_test.dart +++ b/test/bottom_navigation_bar_theme/bottom_navigation_bar_theme_cubit_test.dart @@ -1,22 +1,33 @@ import 'dart:math'; +import 'package:appainter/abstract_text_style/abstract_text_style.dart'; +import 'package:appainter/bottom_navigation_bar_theme/bottom_navigation_bar_theme.dart'; +import 'package:appainter/services/services.dart'; import 'package:bloc_test/bloc_test.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:appainter/bottom_navigation_bar_theme/bottom_navigation_bar_theme.dart'; -import 'package:appainter/services/services.dart'; import 'package:random_color_scheme/random_color_scheme.dart'; import '../utils.dart'; void main() { late BottomNavigationBarThemeCubit cubit; + late BottomNavigationBarLabelTextStyleCubit labelTextStyleCubit; + late BottomNavigationBarUnselectedLabelTextStyleCubit + unselectedLabelTextStyleCubit; + late BottomNavigationBarThemeData theme; late Color color; late double doubleValue; setUp(() { - cubit = BottomNavigationBarThemeCubit(); + labelTextStyleCubit = BottomNavigationBarLabelTextStyleCubit(); + unselectedLabelTextStyleCubit = + BottomNavigationBarUnselectedLabelTextStyleCubit(); + cubit = BottomNavigationBarThemeCubit( + labelTextStyleCubit: labelTextStyleCubit, + unselectedLabelTextStyleCubit: unselectedLabelTextStyleCubit, + ); color = getRandomColor(); doubleValue = Random().nextDouble(); @@ -25,10 +36,26 @@ void main() { }); blocTest( - 'should emit theme', + 'emit theme', build: () => cubit, act: (cubit) => cubit.themeChanged(theme), - expect: () => [BottomNavigationBarThemeState(theme: theme)], + expect: () => [ + BottomNavigationBarThemeState(theme: theme), + BottomNavigationBarThemeState( + theme: theme.copyWith( + selectedLabelStyle: + BottomNavigationBarThemeCubit.defaultLabelTextStyle, + ), + ), + BottomNavigationBarThemeState( + theme: theme.copyWith( + selectedLabelStyle: + BottomNavigationBarThemeCubit.defaultLabelTextStyle, + unselectedLabelStyle: + BottomNavigationBarThemeCubit.defaultLabelTextStyle, + ), + ), + ], ); group('test type', () { @@ -37,7 +64,7 @@ void main() { type == BottomNavigationBarType.shifting ? false : true; blocTest( - 'should emit $type', + 'emit $type', build: () => cubit, act: (cubit) => cubit.typeChanged(UtilService.enumToString(type)), expect: () { @@ -55,7 +82,7 @@ void main() { }); blocTest( - 'should emit background color', + 'emit background color', build: () => cubit, act: (cubit) => cubit.backgroundColorChanged(color), expect: () { @@ -68,7 +95,7 @@ void main() { ); blocTest( - 'should emit selected item color', + 'emit selected item color', build: () => cubit, act: (cubit) => cubit.selectedItemColorChanged(color), expect: () { @@ -81,7 +108,7 @@ void main() { ); blocTest( - 'should emit unselected item color', + 'emit unselected item color', build: () => cubit, act: (cubit) => cubit.unselectedItemColorChanged(color), expect: () { @@ -96,7 +123,7 @@ void main() { group('test show selected labels', () { for (var isShow in [true, false]) { blocTest( - 'should emit $isShow', + 'emit $isShow', build: () => cubit, act: (cubit) => cubit.showSelectedLabelsChanged(isShow), expect: () { @@ -113,7 +140,7 @@ void main() { group('test show unselected labels', () { for (var isShow in [true, false]) { blocTest( - 'should emit $isShow', + 'emit $isShow', build: () => cubit, act: (cubit) => cubit.showUnselectedLabelsChanged(isShow), expect: () { @@ -128,7 +155,7 @@ void main() { }); blocTest( - 'should emit elevation', + 'emit elevation', build: () => cubit, act: (cubit) => cubit.elevationChanged(doubleValue.toString()), expect: () { @@ -139,4 +166,16 @@ void main() { ]; }, ); + + test('initialise label text style cubit', () { + final cubit = BottomNavigationBarLabelTextStyleCubit(); + expect(cubit.typeScale, equals(TypeScale.bodyText2)); + expect(cubit.isBaseStyleBlack, equals(false)); + }); + + test('initialise unselected label text style cubit', () { + final cubit = BottomNavigationBarUnselectedLabelTextStyleCubit(); + expect(cubit.typeScale, equals(TypeScale.bodyText2)); + expect(cubit.isBaseStyleBlack, equals(false)); + }); } diff --git a/test/bottom_navigation_bar_theme/bottom_navigation_bar_theme_editor_test.dart b/test/bottom_navigation_bar_theme/bottom_navigation_bar_theme_editor_test.dart index 00b773da..9ef730ce 100644 --- a/test/bottom_navigation_bar_theme/bottom_navigation_bar_theme_editor_test.dart +++ b/test/bottom_navigation_bar_theme/bottom_navigation_bar_theme_editor_test.dart @@ -1,5 +1,6 @@ import 'dart:math'; +import 'package:appainter/abstract_text_style/cubit/abstract_text_style_cubit.dart'; import 'package:appainter/bottom_navigation_bar_theme/bottom_navigation_bar_theme.dart'; import 'package:appainter/color_theme/color_theme.dart'; import 'package:appainter/services/util_service.dart'; @@ -18,19 +19,39 @@ void main() { final widgetTesters = WidgetTesters(expandText: 'Bottom navigation bar'); late BottomNavigationBarThemeCubit bottomNavigationBarThemeCubit; + late BottomNavigationBarLabelTextStyleCubit + bottomNavigationBarLabelTextStyleCubit; + late BottomNavigationBarUnselectedLabelTextStyleCubit + bottomNavigationBarUnselectedLabelTextStyleCubit; late ColorThemeCubit colorThemeCubit; late Color color; late double doubleValue; setUp(() { bottomNavigationBarThemeCubit = MockBottomNavigationBarThemeCubit(); + bottomNavigationBarLabelTextStyleCubit = + MockBottomNavigationBarLabelTextStyleCubit(); + bottomNavigationBarUnselectedLabelTextStyleCubit = + MockBottomNavigationBarUnselectedLabelTextStyleCubit(); colorThemeCubit = MockColorThemeCubit(); color = getRandomColor(); doubleValue = Random().nextDouble(); + when(() => bottomNavigationBarThemeCubit.state).thenReturn( + const BottomNavigationBarThemeState(), + ); + when(() => bottomNavigationBarLabelTextStyleCubit.state).thenReturn( + TextStyleState( + style: BottomNavigationBarThemeCubit.defaultLabelTextStyle, + ), + ); when(() { - return bottomNavigationBarThemeCubit.state; - }).thenReturn(const BottomNavigationBarThemeState()); + return bottomNavigationBarUnselectedLabelTextStyleCubit.state; + }).thenReturn( + TextStyleState( + style: BottomNavigationBarThemeCubit.defaultLabelTextStyle, + ), + ); when(() => colorThemeCubit.state).thenReturn(ColorThemeState()); }); @@ -47,6 +68,10 @@ void main() { MultiBlocProvider( providers: [ BlocProvider.value(value: bottomNavigationBarThemeCubit), + BlocProvider.value(value: bottomNavigationBarLabelTextStyleCubit), + BlocProvider.value( + value: bottomNavigationBarUnselectedLabelTextStyleCubit, + ), BlocProvider.value(value: colorThemeCubit), ], child: MaterialApp( @@ -58,8 +83,25 @@ void main() { ); } + testWidgets('display nested editors', (tester) async { + await pumpApp(tester, const BottomNavigationBarThemeState()); + expect( + find.byKey( + const Key('bottomNavigationBarThemeEditor_labelTextStyleCard'), + ), + findsOneWidget, + ); + expect( + find.byKey( + const Key( + 'bottomNavigationBarThemeEditor_unselectedLabelTextStyleCard', + ), + ), + findsOneWidget, + ); + }); testWidgets( - 'background color picker should update with selected color', + 'background color picker update with selected color', (tester) async { final state = BottomNavigationBarThemeState( theme: BottomNavigationBarThemeData(backgroundColor: color), @@ -79,7 +121,7 @@ void main() { ); testWidgets( - 'selected item color picker should update with selected color', + 'selected item color picker update with selected color', (tester) async { final state = BottomNavigationBarThemeState( theme: BottomNavigationBarThemeData(selectedItemColor: color), @@ -99,7 +141,7 @@ void main() { ); testWidgets( - 'unselected item color picker should update with selected color', + 'unselected item color picker update with selected color', (tester) async { final state = BottomNavigationBarThemeState( theme: BottomNavigationBarThemeData(unselectedItemColor: color), @@ -121,7 +163,7 @@ void main() { group('test show selected labels switch', () { for (var isShow in [true, false]) { testWidgets( - 'should be toggled to $isShow', + 'be toggled to $isShow', (tester) async { final state = BottomNavigationBarThemeState( theme: BottomNavigationBarThemeData(showSelectedLabels: isShow), @@ -145,7 +187,7 @@ void main() { group('test show unselected labels switch', () { for (var isShow in [true, false]) { testWidgets( - 'should be toggled to $isShow', + 'be toggled to $isShow', (tester) async { final state = BottomNavigationBarThemeState( theme: BottomNavigationBarThemeData(showUnselectedLabels: isShow), @@ -167,7 +209,7 @@ void main() { }); testWidgets( - 'elevation text field should update with value', + 'elevation text field update with value', (tester) async { final state = BottomNavigationBarThemeState( theme: BottomNavigationBarThemeData(elevation: doubleValue), @@ -191,7 +233,7 @@ void main() { final typeStr = UtilService.enumToString(type); testWidgets( - 'should update to $type', + 'update to $type', (tester) async { final state = BottomNavigationBarThemeState( theme: BottomNavigationBarThemeData(type: type), diff --git a/test/mocks.dart b/test/mocks.dart index 14cbebce..517e9e7b 100644 --- a/test/mocks.dart +++ b/test/mocks.dart @@ -75,6 +75,14 @@ class MockBottomNavigationBarThemeCubit class FakeBottomNavigationBarThemeState extends Fake implements BottomNavigationBarThemeState {} +class MockBottomNavigationBarLabelTextStyleCubit + extends MockCubit + implements BottomNavigationBarLabelTextStyleCubit {} + +class MockBottomNavigationBarUnselectedLabelTextStyleCubit + extends MockCubit + implements BottomNavigationBarUnselectedLabelTextStyleCubit {} + class MockFloatingActionButtonThemeCubit extends MockCubit implements FloatingActionButtonThemeCubit {} diff --git a/test/pump_app.dart b/test/pump_app.dart index fb43946d..f67f96f5 100644 --- a/test/pump_app.dart +++ b/test/pump_app.dart @@ -60,6 +60,12 @@ extension PumpApp on WidgetTester { final BottomNavigationBarThemeCubit bottomNavigationBarThemeCubit = MockBottomNavigationBarThemeCubit(); + final BottomNavigationBarLabelTextStyleCubit + bottomNavigationBarLabelTextStyleCubit = + MockBottomNavigationBarLabelTextStyleCubit(); + final BottomNavigationBarUnselectedLabelTextStyleCubit + bottomNavigationBarUnselectedLabelTextStyleCubit = + MockBottomNavigationBarUnselectedLabelTextStyleCubit(); final FloatingActionButtonThemeCubit floatingActionButtonThemeCubit = MockFloatingActionButtonThemeCubit(); @@ -142,6 +148,19 @@ extension PumpApp on WidgetTester { when(() => bottomNavigationBarThemeCubit.state).thenReturn( const BottomNavigationBarThemeState(), ); + when(() => bottomNavigationBarLabelTextStyleCubit.state).thenReturn( + TextStyleState( + style: BottomNavigationBarThemeCubit.defaultLabelTextStyle, + ), + ); + when(() { + return bottomNavigationBarUnselectedLabelTextStyleCubit.state; + }).thenReturn( + TextStyleState( + style: BottomNavigationBarThemeCubit.defaultLabelTextStyle, + ), + ); + when(() => floatingActionButtonThemeCubit.state).thenReturn( const FloatingActionButtonThemeState(), ); @@ -201,6 +220,10 @@ extension PumpApp on WidgetTester { BlocProvider.value(value: tabBarLabelTextStyleCubit), BlocProvider.value(value: tabBarUnselectedLabelTextStyleCubit), BlocProvider.value(value: bottomNavigationBarThemeCubit), + BlocProvider.value(value: bottomNavigationBarLabelTextStyleCubit), + BlocProvider.value( + value: bottomNavigationBarUnselectedLabelTextStyleCubit, + ), BlocProvider.value(value: floatingActionButtonThemeCubit), BlocProvider.value(value: elevatedButtonThemeCubit), BlocProvider.value(value: outlinedButtonThemeCubit),