Skip to content

Commit

Permalink
Merge pull request #1 from VGVentures/refactor/app-flavors
Browse files Browse the repository at this point in the history
refactor: app flavors
  • Loading branch information
jolexxa authored Jul 24, 2024
2 parents d42c172 + fc3e393 commit df93b80
Show file tree
Hide file tree
Showing 16 changed files with 253 additions and 56 deletions.
65 changes: 32 additions & 33 deletions lib/demo/view/demo_page.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:financial_dashboard/demo/demo.dart';
import 'package:financial_dashboard/flavor_button/flavor_button.dart';
import 'package:financial_dashboard/l10n/l10n.dart';
import 'package:financial_dashboard/theme_button/theme_button.dart';
import 'package:financial_dashboard/ui/ui.dart';
Expand All @@ -10,8 +11,11 @@ class DemoPage extends StatelessWidget {

@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => ThemeModeCubit(),
return MultiBlocProvider(
providers: [
BlocProvider(create: (_) => ThemeModeCubit()),
BlocProvider(create: (_) => FlavorCubit()),
],
child: const DemoView(),
);
}
Expand All @@ -27,39 +31,34 @@ class DemoView extends StatelessWidget {
return Scaffold(
appBar: AppBar(
title: Text(l10n.appBarTitleText),
actions: const [ThemeButton()],
actions: const [
ThemeButton(),
SizedBox(width: AppSpacing.sm),
FlavorButton(flavor: AppFlavor.one),
FlavorButton(flavor: AppFlavor.two),
FlavorButton(flavor: AppFlavor.three),
],
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(AppSpacing.xlg),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Wrap(
spacing: AppSpacing.xlg,
runSpacing: AppSpacing.xlg,
alignment: WrapAlignment.center,
children: [
DeviceFrame(
lightTheme: const FlavorOneTheme().themeData,
darkTheme: const FlavorOneDarkTheme().themeData,
child: const AppOne(),
),
DeviceFrame(
lightTheme: const FlavorTwoTheme().themeData,
darkTheme: const FlavorTwoDarkTheme().themeData,
child: const AppTwo(),
),
DeviceFrame(
lightTheme: const FlavorThreeTheme().themeData,
darkTheme: const FlavorThreeDarkTheme().themeData,
child: const AppThree(),
),
],
body: BlocBuilder<FlavorCubit, AppFlavor>(
builder: (context, state) {
return switch (state) {
AppFlavor.one => DeviceFrame(
lightTheme: const FlavorOneTheme().themeData,
darkTheme: const FlavorOneDarkTheme().themeData,
child: const AppOne(),
),
],
),
),
AppFlavor.two => DeviceFrame(
lightTheme: const FlavorTwoTheme().themeData,
darkTheme: const FlavorTwoDarkTheme().themeData,
child: const AppTwo(),
),
AppFlavor.three => DeviceFrame(
lightTheme: const FlavorThreeTheme().themeData,
darkTheme: const FlavorThreeDarkTheme().themeData,
child: const AppThree(),
),
};
},
),
);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/demo/widgets/app_one.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class AppOne extends StatelessWidget {
children: [
CurrentSavings(savings: _currentSavings),
const SizedBox(height: AppSpacing.md),
RetirementPredicitionChart(
RetirementPredictionChart(
onCurrentSavings: (value) => _currentSavings.value = value,
showAnnotations: true,
),
Expand All @@ -47,7 +47,7 @@ class AppOne extends StatelessWidget {
const SizedBox(width: AppSpacing.xlg),
const Expanded(
child: MonthlyGoal(amount: r'$3,125.00'),
)
),
],
),
),
Expand Down
4 changes: 2 additions & 2 deletions lib/demo/widgets/app_three.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class AppThree extends StatelessWidget {
children: [
CurrentSavings(savings: _currentSavings),
const SizedBox(height: AppSpacing.md),
RetirementPredicitionChart(
RetirementPredictionChart(
showAreaElement: true,
selectedPointRadius: AppSpacing.xs,
onCurrentSavings: (value) => _currentSavings.value = value,
Expand All @@ -49,7 +49,7 @@ class AppThree extends StatelessWidget {
const SizedBox(width: AppSpacing.xlg),
const Expanded(
child: MonthlyGoal(amount: r'$1,125.00'),
)
),
],
),
),
Expand Down
6 changes: 3 additions & 3 deletions lib/demo/widgets/app_two.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class AppTwo extends StatelessWidget {
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.xlg,
),
child: RetirementPredicitionChart(
child: RetirementPredictionChart(
onCurrentSavings: (value) => _currentSavings.value = value,
),
),
Expand All @@ -55,7 +55,7 @@ class AppTwo extends StatelessWidget {
left: AppSpacing.xlg,
right: AppSpacing.xlg,
child: Material(
color: colorScheme.surfaceVariant,
color: colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(AppSpacing.xlg),
child: Container(
padding: const EdgeInsets.symmetric(
Expand All @@ -65,7 +65,7 @@ class AppTwo extends StatelessWidget {
child: CurrentSavings(savings: _currentSavings),
),
),
)
),
],
),
),
Expand Down
5 changes: 2 additions & 3 deletions lib/demo/widgets/device_frame.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:financial_dashboard/theme_button/theme_button.dart';
import 'package:financial_dashboard/ui/ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

Expand All @@ -9,8 +8,8 @@ class DeviceFrame extends StatelessWidget {
required this.darkTheme,
required this.child,
super.key,
this.borderRadius = AppSpacing.xlg,
this.elevation = AppSpacing.xxs,
this.borderRadius = 0,
this.elevation = 0,
});

final ThemeData lightTheme;
Expand Down
9 changes: 9 additions & 0 deletions lib/flavor_button/cubit/flavor_cubit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'package:bloc/bloc.dart';

enum AppFlavor { one, two, three }

class FlavorCubit extends Cubit<AppFlavor> {
FlavorCubit() : super(AppFlavor.one);

void select(AppFlavor flavor) => emit(flavor);
}
2 changes: 2 additions & 0 deletions lib/flavor_button/flavor_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export 'cubit/flavor_cubit.dart';
export 'view/flavor_button.dart';
40 changes: 40 additions & 0 deletions lib/flavor_button/view/flavor_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'package:financial_dashboard/flavor_button/flavor_button.dart';
import 'package:financial_dashboard/ui/ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class FlavorButton extends StatelessWidget {
const FlavorButton({required this.flavor, super.key});

final AppFlavor flavor;

@override
Widget build(BuildContext context) {
final currentFlavor = context.watch<FlavorCubit>().state;

final outlinedIcon = switch (flavor) {
AppFlavor.one => Icons.looks_one_outlined,
AppFlavor.two => Icons.looks_two_outlined,
AppFlavor.three => Icons.looks_3_outlined,
};

final solidIcon = switch (flavor) {
AppFlavor.one => Icons.looks_one,
AppFlavor.two => Icons.looks_two,
AppFlavor.three => Icons.looks_3,
};

final icon = currentFlavor == flavor ? solidIcon : outlinedIcon;

return Align(
child: InkWell(
onTap: () => context.read<FlavorCubit>().select(flavor),
customBorder: const CircleBorder(),
child: Padding(
padding: const EdgeInsets.all(AppSpacing.sm),
child: Icon(icon),
),
),
);
}
}
4 changes: 2 additions & 2 deletions lib/thumbnail/view/theme_potential_thumbnail.dart
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ class _AppView extends StatelessWidget {
return DeviceFrame(
lightTheme: lightTheme,
darkTheme: darkTheme,
borderRadius: 0,
elevation: 0,
borderRadius: AppSpacing.xlg,
elevation: AppSpacing.xxs,
child: child,
);
}
Expand Down
11 changes: 5 additions & 6 deletions lib/ui/widgets/charts/retirement_prediction_chart.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ List<LineChartPoint> _createSampleData() {
return data;
}

class RetirementPredicitionChart extends StatefulWidget {
const RetirementPredicitionChart({
class RetirementPredictionChart extends StatefulWidget {
const RetirementPredictionChart({
required this.onCurrentSavings,
super.key,
this.showAreaElement = false,
Expand All @@ -35,12 +35,11 @@ class RetirementPredicitionChart extends StatefulWidget {
final double selectedPointRadius;

@override
State<RetirementPredicitionChart> createState() =>
_RetirementPredicitionChartState();
State<RetirementPredictionChart> createState() =>
_RetirementPredictionChartState();
}

class _RetirementPredicitionChartState
extends State<RetirementPredicitionChart> {
class _RetirementPredictionChartState extends State<RetirementPredictionChart> {
late final List<LineChartPoint> _sampleData;

final _showTooltip = ValueNotifier<bool>(false);
Expand Down
50 changes: 48 additions & 2 deletions test/demo/demo_page_test.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,59 @@
import 'package:financial_dashboard/demo/demo.dart';
import 'package:financial_dashboard/flavor_button/flavor_button.dart';
import 'package:financial_dashboard/theme_button/theme_button.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';

import '../helpers/helpers.dart';

void main() {
group('DemoPage', () {
testWidgets('renders three DeviceFrames', (tester) async {
testWidgets('renders DemoView', (tester) async {
await tester.pumpExperience(const DemoPage());
expect(find.byType(DeviceFrame), findsNWidgets(3));
expect(find.byType(DemoView), findsOneWidget);
});
});

group('DemoView', () {
late FlavorCubit flavorCubit;
late ThemeModeCubit themeModeCubit;

setUp(() {
flavorCubit = MockFlavorCubit();
themeModeCubit = MockThemeModeCubit();

when(() => themeModeCubit.state).thenReturn(ThemeMode.light);
});

testWidgets('renders AppOne when AppFlavor is one', (tester) async {
when(() => flavorCubit.state).thenReturn(AppFlavor.one);
await tester.pumpExperience(
const DemoView(),
flavorCubit: flavorCubit,
themeModeCubit: themeModeCubit,
);
expect(find.byType(AppOne), findsOneWidget);
});

testWidgets('renders AppTwo when AppFlavor is two', (tester) async {
when(() => flavorCubit.state).thenReturn(AppFlavor.two);
await tester.pumpExperience(
const DemoView(),
flavorCubit: flavorCubit,
themeModeCubit: themeModeCubit,
);
expect(find.byType(AppTwo), findsOneWidget);
});

testWidgets('renders AppThree when AppFlavor is three', (tester) async {
when(() => flavorCubit.state).thenReturn(AppFlavor.three);
await tester.pumpExperience(
const DemoView(),
flavorCubit: flavorCubit,
themeModeCubit: themeModeCubit,
);
expect(find.byType(AppThree), findsOneWidget);
});
});
}
11 changes: 9 additions & 2 deletions test/helpers/pump_experience.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:bloc_test/bloc_test.dart';
import 'package:financial_dashboard/flavor_button/cubit/flavor_cubit.dart';
import 'package:financial_dashboard/l10n/l10n.dart';
import 'package:financial_dashboard/theme_button/theme_button.dart';
import 'package:flutter/material.dart';
Expand All @@ -8,14 +9,20 @@ import 'package:flutter_test/flutter_test.dart';
class MockThemeModeCubit extends MockCubit<ThemeMode>
implements ThemeModeCubit {}

class MockFlavorCubit extends MockCubit<AppFlavor> implements FlavorCubit {}

extension PumpExperience on WidgetTester {
Future<void> pumpExperience(
Widget widget, {
ThemeModeCubit? themeModeCubit,
FlavorCubit? flavorCubit,
}) {
return pumpWidget(
BlocProvider(
create: (_) => themeModeCubit ?? MockThemeModeCubit(),
MultiBlocProvider(
providers: [
BlocProvider(create: (_) => themeModeCubit ?? MockThemeModeCubit()),
BlocProvider(create: (_) => flavorCubit ?? MockFlavorCubit()),
],
child: MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
Expand Down
20 changes: 20 additions & 0 deletions test/src/flavor_button/cubit/flavor_cubit_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'package:bloc_test/bloc_test.dart';
import 'package:financial_dashboard/flavor_button/cubit/flavor_cubit.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('FlavorCubit', () {
blocTest<FlavorCubit, AppFlavor>(
'initial state is AppFlavor.one',
build: FlavorCubit.new,
verify: (cubit) => expect(cubit.state, AppFlavor.one),
);

blocTest<FlavorCubit, AppFlavor>(
'select method changes AppFlavor',
build: FlavorCubit.new,
act: (cubit) => cubit.select(AppFlavor.two),
expect: () => [AppFlavor.two],
);
});
}
Loading

0 comments on commit df93b80

Please sign in to comment.