Skip to content

Commit

Permalink
Merge pull request #5 from VGVentures/feat/add-financial-bloc
Browse files Browse the repository at this point in the history
feat: add financial bloc
  • Loading branch information
jsgalarraga authored Aug 5, 2024
2 parents 439b460 + f450ea8 commit bc757c4
Show file tree
Hide file tree
Showing 12 changed files with 261 additions and 0 deletions.
2 changes: 2 additions & 0 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/financial_data/financial_data.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';
Expand All @@ -15,6 +16,7 @@ class DemoPage extends StatelessWidget {
providers: [
BlocProvider(create: (_) => ThemeModeCubit()),
BlocProvider(create: (_) => FlavorCubit()),
BlocProvider(create: (_) => FinancialDataBloc()),
],
child: const DemoView(),
);
Expand Down
44 changes: 44 additions & 0 deletions lib/financial_data/bloc/financial_data_bloc.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';

part 'financial_data_event.dart';
part 'financial_data_state.dart';

class FinancialDataBloc extends Bloc<FinancialDataEvent, FinancialDataState> {
FinancialDataBloc() : super(const FinancialDataState()) {
on<FinancialDataRequested>(_onLoadFinancialData);
}

Future<void> _onLoadFinancialData(
FinancialDataRequested event,
Emitter<FinancialDataState> emit,
) async {
emit(
state.copyWith(
currentSavings: 234567.91,
savingsDataPoints: createSampleData(),
monthlySpendingGoal: 3210.55,
transactions: [
const Transaction(name: 'Paycheck', amount: 3000),
const Transaction(name: 'Rent', amount: 1050.20),
const Transaction(name: 'Food', amount: 670.50),
],
),
);
}
}

@visibleForTesting
List<SavingsDataPoint> createSampleData() {
final data = <SavingsDataPoint>[];
var value = 100000.0;
for (var age = 25; age <= 90; age++) {
const toAdd = 10000;
value += toAdd;
data.add(
SavingsDataPoint(age: age, value: value),
);
}
return data;
}
12 changes: 12 additions & 0 deletions lib/financial_data/bloc/financial_data_event.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
part of 'financial_data_bloc.dart';

sealed class FinancialDataEvent extends Equatable {
const FinancialDataEvent();

@override
List<Object> get props => [];
}

class FinancialDataRequested extends FinancialDataEvent {
const FinancialDataRequested();
}
63 changes: 63 additions & 0 deletions lib/financial_data/bloc/financial_data_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
part of 'financial_data_bloc.dart';

class FinancialDataState extends Equatable {
const FinancialDataState({
this.currentSavings = 0,
this.savingsDataPoints = const [],
this.monthlySpendingGoal = 0,
this.transactions = const [],
});

final double currentSavings;
final List<SavingsDataPoint> savingsDataPoints;
final double monthlySpendingGoal;
final List<Transaction> transactions;

FinancialDataState copyWith({
double? currentSavings,
List<SavingsDataPoint>? savingsDataPoints,
double? monthlySpendingGoal,
List<Transaction>? transactions,
}) {
return FinancialDataState(
currentSavings: currentSavings ?? this.currentSavings,
savingsDataPoints: savingsDataPoints ?? this.savingsDataPoints,
monthlySpendingGoal: monthlySpendingGoal ?? this.monthlySpendingGoal,
transactions: transactions ?? this.transactions,
);
}

@override
List<Object> get props => [
currentSavings,
savingsDataPoints,
monthlySpendingGoal,
transactions,
];
}

class SavingsDataPoint extends Equatable {
const SavingsDataPoint({
required this.age,
required this.value,
});

final int age;
final double value;

@override
List<Object?> get props => [age, value];
}

class Transaction extends Equatable {
const Transaction({
required this.name,
required this.amount,
});

final String name;
final double amount;

@override
List<Object?> get props => [name, amount];
}
1 change: 1 addition & 0 deletions lib/financial_data/extensions/extensions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export 'financial_formatter.dart';
9 changes: 9 additions & 0 deletions lib/financial_data/extensions/financial_formatter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'package:intl/intl.dart';

extension FinancialFormatterX on double {
String toCurrencyWithoutDecimal() =>
'\$${NumberFormat('###,###').format(this)}';

String toCurrencyWithDecimals() =>
'\$${NumberFormat('###,###.00').format(this)}';
}
2 changes: 2 additions & 0 deletions lib/financial_data/financial_data.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export 'bloc/financial_data_bloc.dart';
export 'extensions/extensions.dart';
6 changes: 6 additions & 0 deletions test/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
include: package:very_good_analysis/analysis_options.6.0.0.yaml
linter:
rules:
public_member_api_docs: false
prefer_const_constructors: false
prefer_const_literals_to_create_immutables: false
25 changes: 25 additions & 0 deletions test/financial_data/bloc/financial_data_bloc_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:bloc_test/bloc_test.dart';
import 'package:financial_dashboard/financial_data/financial_data.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('FinancialDataBloc', () {
blocTest<FinancialDataBloc, FinancialDataState>(
'loads financial data when $FinancialDataRequested is added',
build: FinancialDataBloc.new,
act: (bloc) => bloc.add(FinancialDataRequested()),
expect: () => [
FinancialDataState(
currentSavings: 234567.91,
savingsDataPoints: createSampleData(),
monthlySpendingGoal: 3210.55,
transactions: [
const Transaction(name: 'Paycheck', amount: 3000),
const Transaction(name: 'Rent', amount: 1050.20),
const Transaction(name: 'Food', amount: 670.50),
],
),
],
);
});
}
13 changes: 13 additions & 0 deletions test/financial_data/bloc/financial_data_event_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'package:financial_dashboard/financial_data/financial_data.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('FinancialDataRequested', () {
test('supports value equality', () {
final eventA = FinancialDataRequested();
final eventB = FinancialDataRequested();

expect(eventA, equals(eventB));
});
});
}
64 changes: 64 additions & 0 deletions test/financial_data/bloc/financial_data_state_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import 'package:financial_dashboard/financial_data/financial_data.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('FinancialDataState', () {
test('supports value equality', () {
expect(FinancialDataState(), equals(FinancialDataState()));
});

group('copyWith', () {
test('copies currentSavings', () {
final state = FinancialDataState();
final newState = state.copyWith(currentSavings: 123.45);

expect(newState.currentSavings, 123.45);
});

test('copies savingsDataPoints', () {
final state = FinancialDataState();
final dataPoint = SavingsDataPoint(age: 25, value: 100);
final newState = state.copyWith(savingsDataPoints: [dataPoint]);

expect(newState.savingsDataPoints, equals([dataPoint]));
});

test('copies monthlySpendingGoal', () {
final state = FinancialDataState();
final newState = state.copyWith(monthlySpendingGoal: 123.45);

expect(newState.monthlySpendingGoal, 123.45);
});

test('copies transactions', () {
final state = FinancialDataState();
final transaction = Transaction(name: 'test', amount: 123.45);
final newState = state.copyWith(transactions: [transaction]);

expect(newState.transactions, equals([transaction]));
});
});
});

group('SavingsDataPoint', () {
test('supports value equality', () {
const pointA = SavingsDataPoint(age: 25, value: 100);
const secondPointA = SavingsDataPoint(age: 25, value: 100);
const pointB = SavingsDataPoint(age: 52, value: 200);

expect(pointA, equals(secondPointA));
expect(pointA, isNot(equals(pointB)));
});
});

group('Transaction', () {
test('supports value equality', () {
const pointA = Transaction(name: 'test', amount: 100);
const secondPointA = Transaction(name: 'test', amount: 100);
const pointB = Transaction(name: 'test-two', amount: 200);

expect(pointA, equals(secondPointA));
expect(pointA, isNot(equals(pointB)));
});
});
}
20 changes: 20 additions & 0 deletions test/financial_data/extensions/financial_formatter_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'package:financial_dashboard/financial_data/financial_data.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('FinancialFormatterX', () {
test('converts double to currency without decimals', () {
expect(
123456.789.toCurrencyWithoutDecimal(),
r'$123,457',
);
});

test('converts double to currency with decimals', () {
expect(
123456.789.toCurrencyWithDecimals(),
r'$123,456.79',
);
});
});
}

0 comments on commit bc757c4

Please sign in to comment.