Compare commits
2 Commits
cbaf94d866
...
1a7f28703a
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a7f28703a | |||
| a51ca14041 |
11
lib/app.dart
11
lib/app.dart
@@ -4,6 +4,7 @@ import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:tunas/clients/storage/storage_client.dart';
|
||||
import 'package:tunas/pages/home/home_page.dart';
|
||||
import 'package:tunas/repositories/account/account_repository.dart';
|
||||
import 'package:tunas/theme.dart';
|
||||
|
||||
class App extends StatefulWidget {
|
||||
const App({super.key});
|
||||
@@ -44,13 +45,9 @@ class _AppViewState extends State<AppView> {
|
||||
providers: [RepositoryProvider.value(value: _accountRepository)],
|
||||
child: MaterialApp(
|
||||
title: 'Tunas',
|
||||
theme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: const Color.fromARGB(255, 55, 55, 55),
|
||||
brightness: Brightness.dark
|
||||
),
|
||||
useMaterial3: true
|
||||
),
|
||||
theme: ThemeData(useMaterial3: true, colorScheme: lightColorScheme),
|
||||
darkTheme: ThemeData(useMaterial3: true, colorScheme: darkColorScheme),
|
||||
themeMode: ThemeMode.dark,
|
||||
initialRoute: '/home',
|
||||
routes: {
|
||||
'/home':(context) => const HomePage(),
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:tunas/domains/charts/models/month_totals.dart';
|
||||
import 'package:tunas/domains/transaction/models/transaction_line.dart';
|
||||
import 'package:tunas/domains/charts/models/chart_item.dart';
|
||||
import 'package:tunas/repositories/account/account_repository.dart';
|
||||
import 'package:tunas/repositories/account/models/category.dart';
|
||||
import 'package:tunas/repositories/account/models/transaction.dart';
|
||||
|
||||
part 'chart_event.dart';
|
||||
@@ -15,20 +17,30 @@ class ChartBloc extends Bloc<ChartEvent, ChartState> {
|
||||
ChartBloc({required AccountRepository accountRepository}) :
|
||||
_accountRepository = accountRepository, super(const ChartState()) {
|
||||
|
||||
on<ChartAccountLoad>(_onAccountLoad);
|
||||
on<ChartTransactionsLoad>(_onChartTransactionsLoad);
|
||||
on<ChartCategoriesLoad>(_onChartCategoriesLoad);
|
||||
on<ChartNextYear>(_onNextYear);
|
||||
on<ChartPreviousYear>(_onPreviousYear);
|
||||
|
||||
_accountRepository
|
||||
.getTransactionsStream()
|
||||
.listen((transactions) => add(ChartAccountLoad(transactions)));
|
||||
.listen((transactions) => add(ChartTransactionsLoad(transactions)));
|
||||
|
||||
_accountRepository
|
||||
.getCategoriesStream()
|
||||
.listen((categories) => add(ChartCategoriesLoad(categories)));
|
||||
}
|
||||
|
||||
_onAccountLoad(ChartAccountLoad event, Emitter<ChartState> emit) {
|
||||
_onChartTransactionsLoad(ChartTransactionsLoad event, Emitter<ChartState> emit) {
|
||||
ChartState localState = state.copyWith(transactions: event.transactions);
|
||||
emit(_computeStateStats(localState));
|
||||
}
|
||||
|
||||
_onChartCategoriesLoad(ChartCategoriesLoad event, Emitter<ChartState> emit) {
|
||||
ChartState localState = state.copyWith(categories: Map.fromEntries(event.categories.map((category) => MapEntry(category.label, category))));
|
||||
emit(_computeStateStats(localState));
|
||||
}
|
||||
|
||||
_onNextYear(ChartNextYear event, Emitter<ChartState> emit) {
|
||||
if (state.lastDate!.year >= state.currentYear + 1) {
|
||||
ChartState localState = state.copyWith(currentYear: state.currentYear + 1);
|
||||
@@ -100,7 +112,10 @@ class ChartBloc extends Bloc<ChartEvent, ChartState> {
|
||||
List<ChartItem> scopedCategoriesPositiveTotals = [];
|
||||
List<ChartItem> scopedCategoriesNegativeTotals = [];
|
||||
Map<int, FlSpot> scopedMonthlyTotals = {};
|
||||
Map<int, Map<String, double>> scopedCategoriesMonthlyTotals = {};
|
||||
Map<int, MonthTotals> scopedCategoriesMonthlyTotals = {};
|
||||
|
||||
Map<int, double> scopedMonthlyPostitiveTotals = {};
|
||||
Map<int, double> scopedMonthlyNegativeTotals = {};
|
||||
|
||||
for(var transaction in state.transactions) {
|
||||
globalTotal += transaction.value;
|
||||
@@ -119,10 +134,17 @@ class ChartBloc extends Bloc<ChartEvent, ChartState> {
|
||||
|
||||
scopedMonthlyTotals[transactionDateDay] = FlSpot(transactionDateDay.toDouble(), transactionLine.subTotal);
|
||||
|
||||
if (transaction.category.isEmpty) {
|
||||
final category = state.categories[transaction.category];
|
||||
if (category == null || category.saving) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MonthTotals? categoryMonthTotal = scopedCategoriesMonthlyTotals[transaction.date.month];
|
||||
if (categoryMonthTotal == null) {
|
||||
categoryMonthTotal = MonthTotals(negatives: {}, positives: {});
|
||||
scopedCategoriesMonthlyTotals[transaction.date.month] = categoryMonthTotal;
|
||||
}
|
||||
|
||||
if (transaction.value >= 0) {
|
||||
ChartItem? chartItem = scopedCategoriesPositiveTotals.firstWhere(
|
||||
(item) => item.label == transaction.category,
|
||||
@@ -133,6 +155,9 @@ class ChartBloc extends Bloc<ChartEvent, ChartState> {
|
||||
}
|
||||
);
|
||||
chartItem.value += transaction.value;
|
||||
|
||||
scopedMonthlyPostitiveTotals[transaction.date.month] = transaction.value + (scopedMonthlyPostitiveTotals[transaction.date.month] ?? 0);
|
||||
categoryMonthTotal.positives[transaction.category] = transaction.value.abs() + (categoryMonthTotal.positives[transaction.category] ?? 0);
|
||||
}
|
||||
|
||||
if (transaction.value < 0) {
|
||||
@@ -146,13 +171,8 @@ class ChartBloc extends Bloc<ChartEvent, ChartState> {
|
||||
);
|
||||
chartItem.value += transaction.value;
|
||||
|
||||
Map<String, double>? a = scopedCategoriesMonthlyTotals[transaction.date.month];
|
||||
if (scopedCategoriesMonthlyTotals[transaction.date.month] == null) {
|
||||
a = {};
|
||||
}
|
||||
|
||||
a?[transaction.category] = transaction.value.abs() + (a[transaction.category] ?? 0);
|
||||
scopedCategoriesMonthlyTotals[transaction.date.month] = a!;
|
||||
scopedMonthlyNegativeTotals[transaction.date.month] = transaction.value + (scopedMonthlyPostitiveTotals[transaction.date.month] ?? 0);
|
||||
categoryMonthTotal.negatives[transaction.category] = transaction.value.abs() + (categoryMonthTotal.negatives[transaction.category] ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,15 +213,20 @@ class ChartBloc extends Bloc<ChartEvent, ChartState> {
|
||||
List<ChartItem> scopedSimplifiedCategoriesPositiveTotals = [];
|
||||
List<ChartItem> scopedSimplifiedCategoriesNegativeTotals = [];
|
||||
|
||||
scopedCategoriesPositiveTotals.sort((a, b) => a.value.compareTo(b.value));
|
||||
scopedCategoriesPositiveTotalsPercents.sort((a, b) => a.value.compareTo(b.value));
|
||||
scopedCategoriesPositiveTotals.sort((a, b) => b.value.compareTo(a.value));
|
||||
scopedCategoriesPositiveTotalsPercents.sort((a, b) => b.value.compareTo(a.value));
|
||||
scopedCategoriesNegativeTotals.sort((a, b) => a.value.compareTo(b.value));
|
||||
scopedCategoriesNegativeTotalsPercents.sort((a, b) => a.value.compareTo(b.value));
|
||||
scopedSimplifiedCategoriesPositiveTotals.sort((a, b) => a.value.compareTo(b.value));
|
||||
scopedSimplifiedCategoriesPositiveTotalsPercents.sort((a, b) => a.value.compareTo(b.value));
|
||||
scopedSimplifiedCategoriesPositiveTotals.sort((a, b) => b.value.compareTo(a.value));
|
||||
scopedSimplifiedCategoriesPositiveTotalsPercents.sort((a, b) => b.value.compareTo(a.value));
|
||||
scopedSimplifiedCategoriesNegativeTotals.sort((a, b) => a.value.compareTo(b.value));
|
||||
scopedSimplifiedCategoriesNegativeTotalsPercents.sort((a, b) => a.value.compareTo(b.value));
|
||||
|
||||
for (var monthTotals in scopedCategoriesMonthlyTotals.values) {
|
||||
_sortMapByValues(monthTotals.positives);
|
||||
_sortMapByValues(monthTotals.negatives);
|
||||
}
|
||||
|
||||
return state.copyWith(
|
||||
scoppedProfit: scoppedTotal,
|
||||
scopedCategoriesPositiveTotals: scopedCategoriesPositiveTotals,
|
||||
@@ -214,6 +239,19 @@ class ChartBloc extends Bloc<ChartEvent, ChartState> {
|
||||
scopedSimplifiedCategoriesNegativeTotalsPercents: scopedSimplifiedCategoriesNegativeTotalsPercents,
|
||||
scopedMonthlyTotals: scopedMonthlyTotals.values.toList(),
|
||||
scopedCategoriesMonthlyTotals: scopedCategoriesMonthlyTotals,
|
||||
scopedMonthlyPostitiveTotals: scopedMonthlyPostitiveTotals,
|
||||
scopedMonthlyNegativeTotals: scopedMonthlyNegativeTotals,
|
||||
);
|
||||
}
|
||||
|
||||
Map<dynamic, dynamic> _sortMapByValues(Map<dynamic, dynamic> map) {
|
||||
final sortedEntries = map.entries.toList()..sort((a, b) {
|
||||
var diff = b.value.compareTo(a.value);
|
||||
return diff;
|
||||
});
|
||||
|
||||
return map
|
||||
..clear()
|
||||
..addEntries(sortedEntries);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,21 @@ sealed class ChartEvent extends Equatable {
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class ChartAccountLoad extends ChartEvent {
|
||||
final class ChartTransactionsLoad extends ChartEvent {
|
||||
final List<Transaction> transactions;
|
||||
const ChartAccountLoad(this.transactions);
|
||||
const ChartTransactionsLoad(this.transactions);
|
||||
|
||||
@override
|
||||
List<Object> get props => [transactions];
|
||||
}
|
||||
|
||||
final class ChartCategoriesLoad extends ChartEvent {
|
||||
final List<Category> categories;
|
||||
const ChartCategoriesLoad(this.categories);
|
||||
|
||||
@override
|
||||
List<Object> get props => [categories];
|
||||
}
|
||||
|
||||
final class ChartNextYear extends ChartEvent {}
|
||||
final class ChartPreviousYear extends ChartEvent {}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
part of 'chart_bloc.dart';
|
||||
|
||||
final class ChartState extends Equatable {
|
||||
final Map<String, Category> categories;
|
||||
final List<Transaction> transactions;
|
||||
final List<TransactionLine> transactionsLines;
|
||||
|
||||
@@ -27,11 +28,14 @@ final class ChartState extends Equatable {
|
||||
final List<ChartItem> scopedSimplifiedCategoriesNegativeTotalsPercents;
|
||||
|
||||
final List<FlSpot> scopedMonthlyTotals;
|
||||
final Map<int, Map<String, double>> scopedCategoriesMonthlyTotals;
|
||||
final Map<int, MonthTotals> scopedCategoriesMonthlyTotals;
|
||||
final Map<int, double> scopedMonthlyPostitiveTotals;
|
||||
final Map<int, double> scopedMonthlyNegativeTotals;
|
||||
|
||||
final double scoppedProfit;
|
||||
|
||||
const ChartState({
|
||||
this.categories = const {},
|
||||
this.transactions = const [],
|
||||
this.transactionsLines = const [],
|
||||
this.globalTotal = 0,
|
||||
@@ -53,9 +57,12 @@ final class ChartState extends Equatable {
|
||||
this.scopedMonthlyTotals = const [],
|
||||
this.scopedCategoriesMonthlyTotals = const {},
|
||||
this.scoppedProfit = 0,
|
||||
this.scopedMonthlyPostitiveTotals = const {},
|
||||
this.scopedMonthlyNegativeTotals = const {},
|
||||
});
|
||||
|
||||
ChartState copyWith({
|
||||
Map<String, Category>? categories,
|
||||
List<Transaction>? transactions,
|
||||
List<TransactionLine>? transactionsLines,
|
||||
double? globalTotal,
|
||||
@@ -75,10 +82,13 @@ final class ChartState extends Equatable {
|
||||
List<ChartItem>? scopedSimplifiedCategoriesNegativeTotals,
|
||||
List<ChartItem>? scopedSimplifiedCategoriesNegativeTotalsPercents,
|
||||
List<FlSpot>? scopedMonthlyTotals,
|
||||
Map<int, Map<String, double>>? scopedCategoriesMonthlyTotals,
|
||||
Map<int, MonthTotals>? scopedCategoriesMonthlyTotals,
|
||||
double? scoppedProfit,
|
||||
Map<int, double>? scopedMonthlyPostitiveTotals,
|
||||
Map<int, double>? scopedMonthlyNegativeTotals,
|
||||
}) {
|
||||
return ChartState(
|
||||
categories: categories ?? this.categories,
|
||||
transactions: transactions ?? this.transactions,
|
||||
transactionsLines: transactionsLines ?? this.transactionsLines,
|
||||
globalTotal: globalTotal ?? this.globalTotal,
|
||||
@@ -100,11 +110,14 @@ final class ChartState extends Equatable {
|
||||
scopedMonthlyTotals: scopedMonthlyTotals ?? this.scopedMonthlyTotals,
|
||||
scopedCategoriesMonthlyTotals: scopedCategoriesMonthlyTotals ?? this.scopedCategoriesMonthlyTotals,
|
||||
scoppedProfit: scoppedProfit ?? this.scoppedProfit,
|
||||
scopedMonthlyPostitiveTotals: scopedMonthlyPostitiveTotals ?? this.scopedMonthlyPostitiveTotals,
|
||||
scopedMonthlyNegativeTotals: scopedMonthlyNegativeTotals ?? this.scopedMonthlyNegativeTotals,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object> get props => [
|
||||
categories,
|
||||
transactions,
|
||||
transactionsLines,
|
||||
globalTotal,
|
||||
@@ -124,6 +137,8 @@ final class ChartState extends Equatable {
|
||||
scopedMonthlyTotals,
|
||||
scopedCategoriesMonthlyTotals,
|
||||
scoppedProfit,
|
||||
scopedMonthlyPostitiveTotals,
|
||||
scopedMonthlyNegativeTotals,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
29
lib/domains/charts/models/month_totals.dart
Normal file
29
lib/domains/charts/models/month_totals.dart
Normal file
@@ -0,0 +1,29 @@
|
||||
class MonthTotals {
|
||||
Map<String, double> positives;
|
||||
Map<String, double> negatives;
|
||||
|
||||
MonthTotals({
|
||||
required this.positives,
|
||||
required this.negatives,
|
||||
});
|
||||
|
||||
double maxValue() {
|
||||
double max = 0.0;
|
||||
|
||||
if (positives.isNotEmpty) {
|
||||
double localMax = positives.values.reduce((value, element) => value + element);
|
||||
if (localMax > max) {
|
||||
max = localMax;
|
||||
}
|
||||
}
|
||||
|
||||
if (negatives.isNotEmpty) {
|
||||
double localMax2 = negatives.values.reduce((value, element) => value + element);
|
||||
if (localMax2 > max) {
|
||||
max = localMax2;
|
||||
}
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
}
|
||||
57
lib/pages/common/titled_container.dart
Normal file
57
lib/pages/common/titled_container.dart
Normal file
@@ -0,0 +1,57 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TitledContainer extends StatelessWidget {
|
||||
final String title;
|
||||
final Widget child;
|
||||
|
||||
const TitledContainer({super.key, required this.title, required this.child});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Theme.of(context).colorScheme.shadow,
|
||||
blurRadius: 10,
|
||||
offset: const Offset(2, 2),
|
||||
spreadRadius: 0.1,
|
||||
blurStyle: BlurStyle.normal,
|
||||
)
|
||||
]
|
||||
),
|
||||
margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||
borderRadius: const BorderRadius.only(topLeft: Radius.circular(15), topRight: Radius.circular(15)),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 15),
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w300,
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
|
||||
child: child,
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -15,24 +15,25 @@ class DataPage extends StatelessWidget {
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 9, horizontal: 10),
|
||||
margin: const EdgeInsets.symmetric(vertical: 2, horizontal: 10),
|
||||
child: const Column(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
const Text(
|
||||
'Settings',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 35,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ImportSettings(),
|
||||
CategoriesSettings(),
|
||||
AccountSettings(),
|
||||
],
|
||||
)
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: const [
|
||||
ImportSettings(),
|
||||
CategoriesSettings(),
|
||||
AccountSettings(),
|
||||
]
|
||||
)
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:tunas/domains/account/account_bloc.dart';
|
||||
import 'package:tunas/pages/common/titled_container.dart';
|
||||
|
||||
class AccountSettings extends StatelessWidget {
|
||||
const AccountSettings({super.key});
|
||||
@@ -16,34 +17,15 @@ class AccountSettings extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<AccountBloc, AccountState>(
|
||||
builder: (context, state) => Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
builder: (context, state) => TitledContainer(
|
||||
title: "Accounts",
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _computeCategoryList(state.subAccounts),
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
|
||||
margin: const EdgeInsets.all(5),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
"Accounts",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _computeCategoryList(state.subAccounts),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,42 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:tunas/domains/category/category_bloc.dart';
|
||||
import 'package:tunas/pages/common/titled_container.dart';
|
||||
import 'package:tunas/repositories/account/models/category.dart';
|
||||
|
||||
class CategoriesSettings extends StatelessWidget {
|
||||
const CategoriesSettings({super.key});
|
||||
|
||||
List<Widget> _computeCategoryList(List<Category> categories) {
|
||||
List<Widget> _computeCategoryList(BuildContext context, List<Category> categories) {
|
||||
return categories.map((category) => Row(
|
||||
children: [
|
||||
Container(
|
||||
height: 10,
|
||||
width: 10,
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.palette),
|
||||
color: category.rgbToColor(),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.savings),
|
||||
color: category.saving ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.error,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.foundation),
|
||||
color: category.essential ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.error,
|
||||
),
|
||||
Container(width: 5),
|
||||
Text(category.label),
|
||||
Expanded(
|
||||
child: Text(category.label)
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.edit),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.delete),
|
||||
),
|
||||
],
|
||||
)).toList();
|
||||
}
|
||||
@@ -23,35 +44,16 @@ class CategoriesSettings extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<CategoryBloc, CategoryState>(
|
||||
builder: (context, state) => Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
builder: (context, state) => TitledContainer(
|
||||
title: "Categories",
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _computeCategoryList(context, state.categories),
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
|
||||
margin: const EdgeInsets.all(5),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
"Categories",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _computeCategoryList(state.categories),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:tunas/domains/account/account_bloc.dart';
|
||||
import 'package:tunas/pages/common/titled_container.dart';
|
||||
|
||||
class ImportSettings extends StatelessWidget {
|
||||
const ImportSettings({super.key});
|
||||
@@ -8,25 +9,10 @@ class ImportSettings extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<AccountBloc, AccountState>(
|
||||
builder: (context, state) => Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(5)
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
|
||||
margin: const EdgeInsets.all(5),
|
||||
builder: (context, state) => TitledContainer(
|
||||
title: "Import",
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
"Import",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
FilledButton(
|
||||
onPressed: () => context.read<AccountBloc>().add(const AccountImportCSV()),
|
||||
child: const Text('Import CSV')
|
||||
|
||||
@@ -3,9 +3,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:tunas/domains/charts/chart_bloc.dart';
|
||||
import 'package:tunas/pages/stats/widgets/account_counters.dart';
|
||||
import 'package:tunas/pages/stats/widgets/categories_totals_chart.dart';
|
||||
import 'package:tunas/pages/stats/widgets/main_counter.dart';
|
||||
import 'package:tunas/pages/stats/widgets/global_counter.dart';
|
||||
import 'package:tunas/pages/stats/widgets/monthly_categories_total_chart.dart';
|
||||
import 'package:tunas/pages/stats/widgets/monthly_total_chart.dart';
|
||||
import 'package:tunas/pages/stats/widgets/global_total_chart.dart';
|
||||
import 'package:tunas/pages/stats/widgets/profit_indicator.dart';
|
||||
import 'package:tunas/pages/stats/widgets/year_selector.dart';
|
||||
import 'package:tunas/repositories/account/account_repository.dart';
|
||||
@@ -31,7 +31,7 @@ class StatsPage extends StatelessWidget {
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: MainCounter(value: state.globalTotal)
|
||||
child: GlobalCounter(value: state.globalTotal)
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
|
||||
@@ -6,7 +6,7 @@ class AccountCounter extends StatelessWidget {
|
||||
|
||||
const AccountCounter({super.key, required this.accountsTotals});
|
||||
|
||||
List<Row> _renderAccountTotals() {
|
||||
List<Row> _renderAccountTotals(BuildContext context) {
|
||||
return accountsTotals.entries.toList().map((entry) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
@@ -21,7 +21,7 @@ class AccountCounter extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
fontFamily: 'NovaMono',
|
||||
fontSize: 15,
|
||||
color: entry.value > 0 ? const Color.fromARGB(255, 0, 255, 8) : Colors.red
|
||||
color: entry.value > 0 ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.error
|
||||
)),
|
||||
],
|
||||
)).toList();
|
||||
@@ -33,13 +33,22 @@ class AccountCounter extends StatelessWidget {
|
||||
padding: const EdgeInsets.all(10),
|
||||
margin: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: Colors.blue
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Theme.of(context).colorScheme.shadow,
|
||||
blurRadius: 10,
|
||||
offset: const Offset(2, 2),
|
||||
spreadRadius: 0.1,
|
||||
blurStyle: BlurStyle.normal,
|
||||
)
|
||||
]
|
||||
),
|
||||
alignment: Alignment.centerRight,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: _renderAccountTotals(),
|
||||
children: _renderAccountTotals(context),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ class CategoriesTotalsChart extends StatelessWidget {
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w300
|
||||
),
|
||||
titlePositionPercentageOffset: 0.8,
|
||||
titlePositionPercentageOffset: 0.5,
|
||||
borderSide: const BorderSide(width: 0),
|
||||
radius: 150,
|
||||
radius: 40,
|
||||
color: categoriesColors[item.label]
|
||||
))
|
||||
.toList();
|
||||
@@ -39,7 +39,10 @@ class CategoriesTotalsChart extends StatelessWidget {
|
||||
Container(
|
||||
height: 10,
|
||||
width: 10,
|
||||
color: categoriesColors[item.label],
|
||||
decoration: BoxDecoration(
|
||||
color: categoriesColors[item.label],
|
||||
borderRadius: BorderRadius.circular(15)
|
||||
),
|
||||
),
|
||||
Container(width: 5),
|
||||
Text(item.label),
|
||||
@@ -60,12 +63,21 @@ class CategoriesTotalsChart extends StatelessWidget {
|
||||
return BlocBuilder<CategoryBloc, CategoryState>(
|
||||
builder: (context, state) => Container(
|
||||
height: 320,
|
||||
width: 600,
|
||||
width: 500,
|
||||
padding: const EdgeInsets.all(10),
|
||||
margin: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: Colors.blue
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Theme.of(context).colorScheme.shadow,
|
||||
blurRadius: 10,
|
||||
offset: const Offset(2, 2),
|
||||
spreadRadius: 0.1,
|
||||
blurStyle: BlurStyle.normal,
|
||||
)
|
||||
]
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
@@ -76,8 +88,8 @@ class CategoriesTotalsChart extends StatelessWidget {
|
||||
borderData: FlBorderData(
|
||||
show: false
|
||||
),
|
||||
centerSpaceRadius: 0,
|
||||
sectionsSpace: 2
|
||||
centerSpaceRadius: 50,
|
||||
sectionsSpace: 4
|
||||
)
|
||||
),
|
||||
),
|
||||
@@ -87,7 +99,7 @@ class CategoriesTotalsChart extends StatelessWidget {
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: Colors.blueGrey
|
||||
color: Theme.of(context).colorScheme.secondaryContainer
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
|
||||
39
lib/pages/stats/widgets/global_counter.dart
Normal file
39
lib/pages/stats/widgets/global_counter.dart
Normal file
@@ -0,0 +1,39 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class GlobalCounter extends StatelessWidget {
|
||||
final double value;
|
||||
|
||||
const GlobalCounter({super.key, required this.value});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
margin: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Theme.of(context).colorScheme.shadow,
|
||||
blurRadius: 10,
|
||||
offset: const Offset(2, 2),
|
||||
spreadRadius: 0.1,
|
||||
blurStyle: BlurStyle.normal,
|
||||
)
|
||||
]
|
||||
),
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text(
|
||||
NumberFormat('000.00 €', 'fr_FR').format(value),
|
||||
style: TextStyle(
|
||||
fontFamily: 'NovaMono',
|
||||
fontSize: 60,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: value > 0 ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.error
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,7 @@ class GlobalTotalChart extends StatelessWidget {
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 12,
|
||||
bottom: 12,
|
||||
right: 20,
|
||||
top: 20,
|
||||
),
|
||||
child: AspectRatio(
|
||||
@@ -31,7 +29,7 @@ class GlobalTotalChart extends StatelessWidget {
|
||||
lineTouchData: LineTouchData(
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
maxContentWidth: 100,
|
||||
tooltipBgColor: Colors.black,
|
||||
tooltipBgColor: Theme.of(context).colorScheme.primaryContainer,
|
||||
getTooltipItems: (touchedSpots) {
|
||||
return touchedSpots.map((LineBarSpot touchedSpot) {
|
||||
final textStyle = TextStyle(
|
||||
@@ -52,13 +50,18 @@ class GlobalTotalChart extends StatelessWidget {
|
||||
),
|
||||
lineBarsData: [
|
||||
LineChartBarData(
|
||||
color: Colors.pink,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
spots: monthlyTotals,
|
||||
isCurved: true,
|
||||
isStrokeCapRound: true,
|
||||
barWidth: 3,
|
||||
belowBarData: BarAreaData(
|
||||
show: false,
|
||||
show: true,
|
||||
gradient: LinearGradient(
|
||||
colors: [Theme.of(context).colorScheme.primary, Theme.of(context).colorScheme.secondary]
|
||||
.map((color) => color.withOpacity(0.2))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
dotData: const FlDotData(show: false),
|
||||
),
|
||||
@@ -1,30 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class MainCounter extends StatelessWidget {
|
||||
final double value;
|
||||
|
||||
const MainCounter({super.key, required this.value});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
margin: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: Colors.blue
|
||||
),
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text(
|
||||
NumberFormat('000.00 €', 'fr_FR').format(value),
|
||||
style: TextStyle(
|
||||
fontFamily: 'NovaMono',
|
||||
fontSize: 60,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: value > 0 ? const Color.fromARGB(255, 0, 255, 8) : Colors.red
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,28 @@
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:tunas/domains/category/category_bloc.dart';
|
||||
import 'package:tunas/domains/charts/models/month_totals.dart';
|
||||
|
||||
class MonthlyCategoriesTotalChart extends StatelessWidget {
|
||||
final Map<int, Map<String, double>> categoriesMonthlyTotals;
|
||||
final Map<int, MonthTotals> categoriesMonthlyTotals;
|
||||
|
||||
const MonthlyCategoriesTotalChart({super.key, required this.categoriesMonthlyTotals});
|
||||
|
||||
BarChartRodData _computeStack(double barsWidth, MapEntry<int, Map<String, double>> entry, Map<String, Color> categoriesColors) {
|
||||
BarChartRodData _computeStack(double barsWidth, Map<String, double> entry, Map<String, Color> categoriesColors) {
|
||||
var subcounter = 0.0;
|
||||
var a = entry.value.entries.map((subEntry) => BarChartRodStackItem(subcounter, subcounter += subEntry.value, categoriesColors[subEntry.key] ?? Colors.red)).toList();
|
||||
var items = entry.entries.map((subEntry) => BarChartRodStackItem(
|
||||
subcounter, subcounter += subEntry.value,
|
||||
categoriesColors[subEntry.key] ?? Colors.red,
|
||||
)).toList();
|
||||
return BarChartRodData(
|
||||
color: Colors.transparent,
|
||||
fromY: 0,
|
||||
toY: subcounter,
|
||||
width: barsWidth,
|
||||
borderRadius: BorderRadius.zero,
|
||||
rodStackItems: a
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
rodStackItems: items
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,7 +32,11 @@ class MonthlyCategoriesTotalChart extends StatelessWidget {
|
||||
return BarChartGroupData(
|
||||
x: entry.key,
|
||||
barsSpace: barsSpace,
|
||||
barRods: [_computeStack(barsWidth, entry, categoriesColors)]
|
||||
barRods: [
|
||||
_computeStack(barsWidth, entry.value.positives, categoriesColors),
|
||||
_computeStack(barsWidth, entry.value.negatives, categoriesColors),
|
||||
],
|
||||
showingTooltipIndicators: [0, 1]
|
||||
);
|
||||
})
|
||||
.toList();
|
||||
@@ -46,7 +56,7 @@ class MonthlyCategoriesTotalChart extends StatelessWidget {
|
||||
double _computeMaxValue() {
|
||||
double max = 0.0;
|
||||
categoriesMonthlyTotals.forEach((monthKey, value) {
|
||||
double localMax = value.values.reduce((value, element) => value + element);
|
||||
double localMax = value.maxValue();
|
||||
if (localMax > max) {
|
||||
max = localMax;
|
||||
}
|
||||
@@ -61,8 +71,8 @@ class MonthlyCategoriesTotalChart extends StatelessWidget {
|
||||
aspectRatio: 1.66,
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final barsSpace = 4.0 * constraints.maxWidth / 100;
|
||||
final barsWidth = 8.0 * constraints.maxWidth / 130;
|
||||
final barsSpace = 4.0 * constraints.maxWidth / 300;
|
||||
final barsWidth = 8.0 * constraints.maxWidth / 500;
|
||||
|
||||
return BarChart(
|
||||
BarChartData(
|
||||
@@ -78,6 +88,41 @@ class MonthlyCategoriesTotalChart extends StatelessWidget {
|
||||
getTitlesWidget: _computeBottomTitles
|
||||
)
|
||||
)
|
||||
),
|
||||
barTouchData: BarTouchData(
|
||||
enabled: true,
|
||||
handleBuiltInTouches: false,
|
||||
touchTooltipData: BarTouchTooltipData(
|
||||
tooltipBgColor: Colors.transparent,
|
||||
tooltipMargin: 0,
|
||||
getTooltipItem:(group, groupIndex, rod, rodIndex) {
|
||||
String value = NumberFormat("#00").format(rod.toY);
|
||||
Color color = Colors.black;
|
||||
if (rodIndex == 0) {
|
||||
value = "+$value";
|
||||
color = Theme.of(context).colorScheme.primary;
|
||||
} else {
|
||||
value = "-$value";
|
||||
color = Theme.of(context).colorScheme.error;
|
||||
}
|
||||
|
||||
return BarTooltipItem(
|
||||
value,
|
||||
TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: color,
|
||||
fontSize: 12,
|
||||
fontFamily: 'NovaMono',
|
||||
shadows: const [
|
||||
Shadow(
|
||||
color: Colors.black26,
|
||||
blurRadius: 12,
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -13,7 +13,7 @@ class ProfitIndicator extends StatelessWidget {
|
||||
margin: const EdgeInsets.fromLTRB(0, 0, 20, 0),
|
||||
padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
borderRadius: BorderRadius.circular(5)
|
||||
),
|
||||
child: Text(
|
||||
@@ -22,7 +22,7 @@ class ProfitIndicator extends StatelessWidget {
|
||||
fontFamily: 'NovaMono',
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: profit > 0 ? const Color.fromARGB(255, 0, 255, 8) : Colors.red
|
||||
color: profit > 0 ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.error
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
@@ -12,8 +12,8 @@ class YearSelector extends StatelessWidget {
|
||||
margin: const EdgeInsets.fromLTRB(20, 0, 0, 0),
|
||||
padding: const EdgeInsets.fromLTRB(5, 0, 5, 0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(5)
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
|
||||
@@ -48,13 +48,13 @@ class TransactionLine extends StatelessWidget {
|
||||
Text(
|
||||
NumberFormat(transaction.value > 0 ? '+#######.00 €' : '#######.00 €', 'fr_FR').format(transaction.value),
|
||||
style: TextStyle(
|
||||
color: transaction.value > 0 ? Colors.green : Colors.red
|
||||
color: transaction.value > 0 ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.error
|
||||
)
|
||||
),
|
||||
Text(
|
||||
NumberFormat('#######.00 €', 'fr_FR').format(subTotal),
|
||||
style: TextStyle(
|
||||
color: subTotal > 0 ? Colors.green : Colors.red
|
||||
color: subTotal > 0 ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.error
|
||||
)
|
||||
),
|
||||
],
|
||||
|
||||
@@ -3,22 +3,30 @@ import 'dart:ui';
|
||||
class Category {
|
||||
String label;
|
||||
String color;
|
||||
bool essential;
|
||||
bool saving;
|
||||
|
||||
Category({
|
||||
this.label = '',
|
||||
this.color = '',
|
||||
this.essential = false,
|
||||
this.saving = false,
|
||||
});
|
||||
|
||||
factory Category.fromJson(Map<String, dynamic> json) {
|
||||
return Category(
|
||||
label: json['label'],
|
||||
color: json['color']
|
||||
color: json['color'],
|
||||
essential: bool.parse(json['essential']),
|
||||
saving: bool.parse(json['saving']),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, String> toJson() => {
|
||||
'label': label,
|
||||
'color': color,
|
||||
'essential': essential.toString(),
|
||||
'saving': saving.toString(),
|
||||
};
|
||||
|
||||
Color rgbToColor() {
|
||||
|
||||
69
lib/theme.dart
Normal file
69
lib/theme.dart
Normal file
@@ -0,0 +1,69 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const lightColorScheme = ColorScheme(
|
||||
brightness: Brightness.light,
|
||||
primary: Color(0xFF006C45),
|
||||
onPrimary: Color(0xFFFFFFFF),
|
||||
primaryContainer: Color(0xFF51FFB1),
|
||||
onPrimaryContainer: Color(0xFF002112),
|
||||
secondary: Color(0xFF4E6355),
|
||||
onSecondary: Color(0xFFFFFFFF),
|
||||
secondaryContainer: Color(0xFFD0E8D7),
|
||||
onSecondaryContainer: Color(0xFF0B1F15),
|
||||
tertiary: Color(0xFF3C6472),
|
||||
onTertiary: Color(0xFFFFFFFF),
|
||||
tertiaryContainer: Color(0xFFBFE9F9),
|
||||
onTertiaryContainer: Color(0xFF001F28),
|
||||
error: Color(0xFFBA1A1A),
|
||||
errorContainer: Color(0xFFFFDAD6),
|
||||
onError: Color(0xFFFFFFFF),
|
||||
onErrorContainer: Color(0xFF410002),
|
||||
background: Color(0xFFFBFDF8),
|
||||
onBackground: Color(0xFF191C1A),
|
||||
surface: Color(0xFFFBFDF8),
|
||||
onSurface: Color(0xFF191C1A),
|
||||
surfaceVariant: Color(0xFFDCE5DC),
|
||||
onSurfaceVariant: Color(0xFF404943),
|
||||
outline: Color(0xFF707972),
|
||||
onInverseSurface: Color(0xFFEFF1ED),
|
||||
inverseSurface: Color(0xFF2E312E),
|
||||
inversePrimary: Color(0xFF21E297),
|
||||
shadow: Color(0xFF000000),
|
||||
surfaceTint: Color(0xFF006C45),
|
||||
outlineVariant: Color(0xFFC0C9C1),
|
||||
scrim: Color(0xFF000000),
|
||||
);
|
||||
|
||||
const darkColorScheme = ColorScheme(
|
||||
brightness: Brightness.dark,
|
||||
primary: Color(0xFF21E297),
|
||||
onPrimary: Color(0xFF003822),
|
||||
primaryContainer: Color(0xFF005233),
|
||||
onPrimaryContainer: Color(0xFF51FFB1),
|
||||
secondary: Color(0xFFB4CCBB),
|
||||
onSecondary: Color(0xFF203529),
|
||||
secondaryContainer: Color(0xFF364B3E),
|
||||
onSecondaryContainer: Color(0xFFD0E8D7),
|
||||
tertiary: Color(0xFFA4CDDD),
|
||||
onTertiary: Color(0xFF043542),
|
||||
tertiaryContainer: Color(0xFF234C59),
|
||||
onTertiaryContainer: Color(0xFFBFE9F9),
|
||||
error: Color(0xFFFFB4AB),
|
||||
errorContainer: Color(0xFF93000A),
|
||||
onError: Color(0xFF690005),
|
||||
onErrorContainer: Color(0xFFFFDAD6),
|
||||
background: Color(0xFF191C1A),
|
||||
onBackground: Color(0xFFE1E3DF),
|
||||
surface: Color(0xFF191C1A),
|
||||
onSurface: Color(0xFFE1E3DF),
|
||||
surfaceVariant: Color(0xFF404943),
|
||||
onSurfaceVariant: Color(0xFFC0C9C1),
|
||||
outline: Color(0xFF8A938B),
|
||||
onInverseSurface: Color(0xFF191C1A),
|
||||
inverseSurface: Color(0xFFE1E3DF),
|
||||
inversePrimary: Color(0xFF006C45),
|
||||
shadow: Color(0xFF000000),
|
||||
surfaceTint: Color(0xFF21E297),
|
||||
outlineVariant: Color(0xFF404943),
|
||||
scrim: Color(0xFF000000),
|
||||
);
|
||||
20
pubspec.lock
20
pubspec.lock
@@ -121,6 +121,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.65.0"
|
||||
flex_color_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flex_color_picker
|
||||
sha256: "0871edc170153cfc3de316d30625f40a85daecfa76ce541641f3cc0ec7757cbf"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.1"
|
||||
flex_seed_scheme:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flex_seed_scheme
|
||||
sha256: "29c12aba221eb8a368a119685371381f8035011d18de5ba277ad11d7dfb8657f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@@ -130,10 +146,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_bloc
|
||||
sha256: e74efb89ee6945bcbce74a5b3a5a3376b088e5f21f55c263fc38cbdc6237faae
|
||||
sha256: "87325da1ac757fcc4813e6b34ed5dd61169973871fdf181d6c2109dd6935ece1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.3"
|
||||
version: "8.1.4"
|
||||
flutter_keyboard_visibility:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -15,7 +15,7 @@ dependencies:
|
||||
cupertino_icons: ^1.0.2
|
||||
fl_chart: ^0.65.0
|
||||
logging: ^1.2.0
|
||||
flutter_bloc: ^8.1.3
|
||||
flutter_bloc: ^8.1.4
|
||||
path_provider: ^2.1.1
|
||||
equatable: ^2.0.5
|
||||
rxdart: ^0.27.7
|
||||
@@ -27,6 +27,7 @@ dependencies:
|
||||
formz: ^0.6.1
|
||||
uuid: ^4.3.2
|
||||
flutter_typeahead: ^5.2.0
|
||||
flex_color_picker: ^3.3.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user