Allow including other spendings in a budget
This commit is contained in:
parent
abc44eddc2
commit
3cd6fd6759
@ -29,6 +29,8 @@ class Budget {
|
|||||||
|
|
||||||
late double income;
|
late double income;
|
||||||
|
|
||||||
|
late bool includeOtherSpendings;
|
||||||
|
|
||||||
final account = IsarLink<Account>();
|
final account = IsarLink<Account>();
|
||||||
|
|
||||||
final items = IsarLinks<BudgetItem>();
|
final items = IsarLinks<BudgetItem>();
|
||||||
|
@ -394,18 +394,23 @@ const BudgetSchema = CollectionSchema(
|
|||||||
name: r'Budget',
|
name: r'Budget',
|
||||||
id: -3383598594604670326,
|
id: -3383598594604670326,
|
||||||
properties: {
|
properties: {
|
||||||
r'income': PropertySchema(
|
r'includeOtherSpendings': PropertySchema(
|
||||||
id: 0,
|
id: 0,
|
||||||
|
name: r'includeOtherSpendings',
|
||||||
|
type: IsarType.bool,
|
||||||
|
),
|
||||||
|
r'income': PropertySchema(
|
||||||
|
id: 1,
|
||||||
name: r'income',
|
name: r'income',
|
||||||
type: IsarType.double,
|
type: IsarType.double,
|
||||||
),
|
),
|
||||||
r'name': PropertySchema(
|
r'name': PropertySchema(
|
||||||
id: 1,
|
id: 2,
|
||||||
name: r'name',
|
name: r'name',
|
||||||
type: IsarType.string,
|
type: IsarType.string,
|
||||||
),
|
),
|
||||||
r'period': PropertySchema(
|
r'period': PropertySchema(
|
||||||
id: 2,
|
id: 3,
|
||||||
name: r'period',
|
name: r'period',
|
||||||
type: IsarType.byte,
|
type: IsarType.byte,
|
||||||
enumMap: _BudgetperiodEnumValueMap,
|
enumMap: _BudgetperiodEnumValueMap,
|
||||||
@ -454,9 +459,10 @@ void _budgetSerialize(
|
|||||||
List<int> offsets,
|
List<int> offsets,
|
||||||
Map<Type, List<int>> allOffsets,
|
Map<Type, List<int>> allOffsets,
|
||||||
) {
|
) {
|
||||||
writer.writeDouble(offsets[0], object.income);
|
writer.writeBool(offsets[0], object.includeOtherSpendings);
|
||||||
writer.writeString(offsets[1], object.name);
|
writer.writeDouble(offsets[1], object.income);
|
||||||
writer.writeByte(offsets[2], object.period.index);
|
writer.writeString(offsets[2], object.name);
|
||||||
|
writer.writeByte(offsets[3], object.period.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
Budget _budgetDeserialize(
|
Budget _budgetDeserialize(
|
||||||
@ -467,10 +473,11 @@ Budget _budgetDeserialize(
|
|||||||
) {
|
) {
|
||||||
final object = Budget();
|
final object = Budget();
|
||||||
object.id = id;
|
object.id = id;
|
||||||
object.income = reader.readDouble(offsets[0]);
|
object.includeOtherSpendings = reader.readBool(offsets[0]);
|
||||||
object.name = reader.readString(offsets[1]);
|
object.income = reader.readDouble(offsets[1]);
|
||||||
|
object.name = reader.readString(offsets[2]);
|
||||||
object.period =
|
object.period =
|
||||||
_BudgetperiodValueEnumMap[reader.readByteOrNull(offsets[2])] ??
|
_BudgetperiodValueEnumMap[reader.readByteOrNull(offsets[3])] ??
|
||||||
BudgetPeriod.month;
|
BudgetPeriod.month;
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
@ -483,10 +490,12 @@ P _budgetDeserializeProp<P>(
|
|||||||
) {
|
) {
|
||||||
switch (propertyId) {
|
switch (propertyId) {
|
||||||
case 0:
|
case 0:
|
||||||
return (reader.readDouble(offset)) as P;
|
return (reader.readBool(offset)) as P;
|
||||||
case 1:
|
case 1:
|
||||||
return (reader.readString(offset)) as P;
|
return (reader.readDouble(offset)) as P;
|
||||||
case 2:
|
case 2:
|
||||||
|
return (reader.readString(offset)) as P;
|
||||||
|
case 3:
|
||||||
return (_BudgetperiodValueEnumMap[reader.readByteOrNull(offset)] ??
|
return (_BudgetperiodValueEnumMap[reader.readByteOrNull(offset)] ??
|
||||||
BudgetPeriod.month) as P;
|
BudgetPeriod.month) as P;
|
||||||
default:
|
default:
|
||||||
@ -643,6 +652,16 @@ extension BudgetQueryFilter on QueryBuilder<Budget, Budget, QFilterCondition> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<Budget, Budget, QAfterFilterCondition>
|
||||||
|
includeOtherSpendingsEqualTo(bool value) {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addFilterCondition(FilterCondition.equalTo(
|
||||||
|
property: r'includeOtherSpendings',
|
||||||
|
value: value,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<Budget, Budget, QAfterFilterCondition> incomeEqualTo(
|
QueryBuilder<Budget, Budget, QAfterFilterCondition> incomeEqualTo(
|
||||||
double value, {
|
double value, {
|
||||||
double epsilon = Query.epsilon,
|
double epsilon = Query.epsilon,
|
||||||
@ -962,6 +981,18 @@ extension BudgetQueryLinks on QueryBuilder<Budget, Budget, QFilterCondition> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension BudgetQuerySortBy on QueryBuilder<Budget, Budget, QSortBy> {
|
extension BudgetQuerySortBy on QueryBuilder<Budget, Budget, QSortBy> {
|
||||||
|
QueryBuilder<Budget, Budget, QAfterSortBy> sortByIncludeOtherSpendings() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'includeOtherSpendings', Sort.asc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<Budget, Budget, QAfterSortBy> sortByIncludeOtherSpendingsDesc() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'includeOtherSpendings', Sort.desc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<Budget, Budget, QAfterSortBy> sortByIncome() {
|
QueryBuilder<Budget, Budget, QAfterSortBy> sortByIncome() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addSortBy(r'income', Sort.asc);
|
return query.addSortBy(r'income', Sort.asc);
|
||||||
@ -1012,6 +1043,18 @@ extension BudgetQuerySortThenBy on QueryBuilder<Budget, Budget, QSortThenBy> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<Budget, Budget, QAfterSortBy> thenByIncludeOtherSpendings() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'includeOtherSpendings', Sort.asc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryBuilder<Budget, Budget, QAfterSortBy> thenByIncludeOtherSpendingsDesc() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addSortBy(r'includeOtherSpendings', Sort.desc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<Budget, Budget, QAfterSortBy> thenByIncome() {
|
QueryBuilder<Budget, Budget, QAfterSortBy> thenByIncome() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addSortBy(r'income', Sort.asc);
|
return query.addSortBy(r'income', Sort.asc);
|
||||||
@ -1050,6 +1093,12 @@ extension BudgetQuerySortThenBy on QueryBuilder<Budget, Budget, QSortThenBy> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension BudgetQueryWhereDistinct on QueryBuilder<Budget, Budget, QDistinct> {
|
extension BudgetQueryWhereDistinct on QueryBuilder<Budget, Budget, QDistinct> {
|
||||||
|
QueryBuilder<Budget, Budget, QDistinct> distinctByIncludeOtherSpendings() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addDistinctBy(r'includeOtherSpendings');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<Budget, Budget, QDistinct> distinctByIncome() {
|
QueryBuilder<Budget, Budget, QDistinct> distinctByIncome() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addDistinctBy(r'income');
|
return query.addDistinctBy(r'income');
|
||||||
@ -1077,6 +1126,12 @@ extension BudgetQueryProperty on QueryBuilder<Budget, Budget, QQueryProperty> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryBuilder<Budget, bool, QQueryOperations> includeOtherSpendingsProperty() {
|
||||||
|
return QueryBuilder.apply(this, (query) {
|
||||||
|
return query.addPropertyName(r'includeOtherSpendings');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QueryBuilder<Budget, double, QQueryOperations> incomeProperty() {
|
QueryBuilder<Budget, double, QQueryOperations> incomeProperty() {
|
||||||
return QueryBuilder.apply(this, (query) {
|
return QueryBuilder.apply(this, (query) {
|
||||||
return query.addPropertyName(r'income');
|
return query.addPropertyName(r'income');
|
||||||
|
@ -27,7 +27,8 @@ class MyApp extends StatelessWidget {
|
|||||||
// This widget is the root of your application.
|
// This widget is the root of your application.
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiBlocProvider(
|
return SafeArea(
|
||||||
|
child: MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
BlocProvider<CoreCubit>(create: (_) => GetIt.I.get<CoreCubit>()),
|
BlocProvider<CoreCubit>(create: (_) => GetIt.I.get<CoreCubit>()),
|
||||||
],
|
],
|
||||||
@ -44,6 +45,7 @@ class MyApp extends StatelessWidget {
|
|||||||
_ => MaterialPageRoute<void>(builder: (_) => Text("Unknown!!")),
|
_ => MaterialPageRoute<void>(builder: (_) => Text("Unknown!!")),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ class AddBudgetState extends State<AddBudgetPopup> {
|
|||||||
final budget = Budget()
|
final budget = Budget()
|
||||||
..name = _budgetNameEditController.text
|
..name = _budgetNameEditController.text
|
||||||
..period = BudgetPeriod.month
|
..period = BudgetPeriod.month
|
||||||
|
..includeOtherSpendings = false
|
||||||
..income = double.parse(_budgetIncomeEditController.text)
|
..income = double.parse(_budgetIncomeEditController.text)
|
||||||
..account.value = bloc.activeAccount!;
|
..account.value = bloc.activeAccount!;
|
||||||
await upsertBudget(budget);
|
await upsertBudget(budget);
|
||||||
|
@ -33,7 +33,7 @@ class BudgetDetailsPage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Column(
|
body: ListView(
|
||||||
children: [
|
children: [
|
||||||
if (isPage)
|
if (isPage)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
@ -113,20 +113,34 @@ class BudgetDetailsPage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final categories =
|
||||||
|
state.activeBudget!.items
|
||||||
|
.map((i) => i.expenseCategory.value!.name)
|
||||||
|
.toList();
|
||||||
final spending = <String, double>{};
|
final spending = <String, double>{};
|
||||||
for (final t in snapshot.data!) {
|
for (final t in snapshot.data!) {
|
||||||
if (t.expenseCategory.value == null) {
|
String categoryName;
|
||||||
|
if (!categories.contains(t.expenseCategory.value?.name)) {
|
||||||
|
if (!state.activeBudget!.includeOtherSpendings) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
categoryName = "Other";
|
||||||
|
} else {
|
||||||
|
categoryName = t.expenseCategory.value!.name;
|
||||||
|
}
|
||||||
|
|
||||||
spending.update(
|
spending.update(
|
||||||
t.expenseCategory.value!.name,
|
categoryName,
|
||||||
(value) => value + t.amount,
|
(value) => value + t.amount,
|
||||||
ifAbsent: () => t.amount,
|
ifAbsent: () => t.amount,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final totalSpent = spending.isEmpty ? 0 : spending.values.reduce((acc, val) => acc + val);
|
final totalSpent =
|
||||||
|
spending.isEmpty
|
||||||
|
? 0
|
||||||
|
: spending.values.reduce((acc, val) => acc + val);
|
||||||
final budgetTotal = state.activeBudget!.items
|
final budgetTotal = state.activeBudget!.items
|
||||||
.map((i) => i.amount)
|
.map((i) => i.amount)
|
||||||
.reduce((acc, val) => acc + val);
|
.reduce((acc, val) => acc + val);
|
||||||
@ -191,7 +205,9 @@ class BudgetDetailsPage extends StatelessWidget {
|
|||||||
).textTheme.titleLarge,
|
).textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
formatCurrency(budgetTotal + totalSpent),
|
formatCurrency(
|
||||||
|
budgetTotal + totalSpent,
|
||||||
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style:
|
style:
|
||||||
Theme.of(
|
Theme.of(
|
||||||
@ -239,19 +255,24 @@ class BudgetDetailsPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
Wrap(
|
||||||
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(8),
|
padding: EdgeInsets.all(8),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
child: Card(
|
child: Card(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 8),
|
padding: EdgeInsets.only(top: 8),
|
||||||
child: Text(
|
child: Text(
|
||||||
"Budget breakdown",
|
"Budget breakdown",
|
||||||
style:
|
style:
|
||||||
Theme.of(context).textTheme.titleLarge,
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.titleLarge,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -273,18 +294,25 @@ class BudgetDetailsPage extends StatelessWidget {
|
|||||||
sectionsSpace: 0,
|
sectionsSpace: 0,
|
||||||
centerSpaceRadius: 35,
|
centerSpaceRadius: 35,
|
||||||
sections:
|
sections:
|
||||||
state.activeBudget!.items
|
state
|
||||||
|
.activeBudget!
|
||||||
|
.items
|
||||||
.map(
|
.map(
|
||||||
(
|
(
|
||||||
i,
|
i,
|
||||||
) => PieChartSectionData(
|
) => PieChartSectionData(
|
||||||
value: i.amount.abs(),
|
value:
|
||||||
title: formatCurrency(
|
i.amount
|
||||||
i.amount.abs(),
|
.abs(),
|
||||||
|
title:
|
||||||
|
formatCurrency(
|
||||||
|
i.amount
|
||||||
|
.abs(),
|
||||||
),
|
),
|
||||||
titleStyle: TextStyle(
|
titleStyle: TextStyle(
|
||||||
fontWeight:
|
fontWeight:
|
||||||
FontWeight.bold,
|
FontWeight
|
||||||
|
.bold,
|
||||||
),
|
),
|
||||||
radius: 40,
|
radius: 40,
|
||||||
color: colorHash(
|
color: colorHash(
|
||||||
@ -337,6 +365,103 @@ class BudgetDetailsPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
child: SizedBox(
|
||||||
|
child: Card(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 8),
|
||||||
|
child: Text(
|
||||||
|
"Spending breakdown",
|
||||||
|
style:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).textTheme.titleLarge,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
child: SizedBox(
|
||||||
|
width: 150,
|
||||||
|
height: 150,
|
||||||
|
child: AspectRatio(
|
||||||
|
aspectRatio: 1,
|
||||||
|
child: PieChart(
|
||||||
|
PieChartData(
|
||||||
|
borderData: FlBorderData(
|
||||||
|
show: false,
|
||||||
|
),
|
||||||
|
sectionsSpace: 0,
|
||||||
|
centerSpaceRadius: 35,
|
||||||
|
sections:
|
||||||
|
spending.entries
|
||||||
|
.map(
|
||||||
|
(
|
||||||
|
e,
|
||||||
|
) => PieChartSectionData(
|
||||||
|
value:
|
||||||
|
e.value
|
||||||
|
.abs(),
|
||||||
|
title:
|
||||||
|
formatCurrency(
|
||||||
|
e.value
|
||||||
|
.abs(),
|
||||||
|
),
|
||||||
|
titleStyle: TextStyle(
|
||||||
|
fontWeight:
|
||||||
|
FontWeight
|
||||||
|
.bold,
|
||||||
|
),
|
||||||
|
radius: 40,
|
||||||
|
color:
|
||||||
|
colorHash(
|
||||||
|
e.key,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 8,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.start,
|
||||||
|
children:
|
||||||
|
spending.keys
|
||||||
|
.map(
|
||||||
|
(k) => LegendItem(
|
||||||
|
text: k,
|
||||||
|
color: colorHash(k),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(8),
|
padding: EdgeInsets.all(8),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:okane/screen.dart';
|
import 'package:okane/screen.dart';
|
||||||
import 'package:okane/ui/pages/budgets/add_budget.dart';
|
import 'package:okane/ui/pages/budgets/add_budget.dart';
|
||||||
|
import 'package:okane/ui/pages/budgets/edit_budget.dart';
|
||||||
import 'package:okane/ui/state/core.dart';
|
import 'package:okane/ui/state/core.dart';
|
||||||
import 'package:okane/ui/utils.dart';
|
import 'package:okane/ui/utils.dart';
|
||||||
|
|
||||||
@ -42,15 +43,31 @@ class BudgetListPage extends StatelessWidget {
|
|||||||
(context, index) => ListTile(
|
(context, index) => ListTile(
|
||||||
title: Text(state.budgets[index].name),
|
title: Text(state.budgets[index].name),
|
||||||
selected: state.budgets[index] == state.activeBudget,
|
selected: state.budgets[index] == state.activeBudget,
|
||||||
trailing: IconButton(
|
trailing: Row(
|
||||||
icon: Icon(
|
mainAxisSize: MainAxisSize.min,
|
||||||
Icons.delete,
|
children: [
|
||||||
color: Colors.redAccent,
|
IconButton(
|
||||||
|
icon: Icon(Icons.edit),
|
||||||
|
onPressed: () {
|
||||||
|
showDialogOrModal(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => EditBudgetPopup(
|
||||||
|
budget: state.activeBudget!,
|
||||||
|
onDone: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.delete, color: Colors.redAccent),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// TODO
|
// TODO
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
GetIt.I.get<CoreCubit>().setActiveBudget(
|
GetIt.I.get<CoreCubit>().setActiveBudget(
|
||||||
state.budgets[index],
|
state.budgets[index],
|
||||||
|
77
lib/ui/pages/budgets/edit_budget.dart
Normal file
77
lib/ui/pages/budgets/edit_budget.dart
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:okane/database/collections/budget.dart';
|
||||||
|
import 'package:okane/database/database.dart';
|
||||||
|
|
||||||
|
class EditBudgetPopup extends StatefulWidget {
|
||||||
|
final Budget budget;
|
||||||
|
|
||||||
|
final VoidCallback onDone;
|
||||||
|
|
||||||
|
const EditBudgetPopup({required this.budget, required this.onDone, super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
EditBudgetState createState() => EditBudgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class EditBudgetState extends State<EditBudgetPopup> {
|
||||||
|
final _budgetNameEditController = TextEditingController();
|
||||||
|
|
||||||
|
late bool _includeOtherSpendings;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
_budgetNameEditController.text = widget.budget.name;
|
||||||
|
_includeOtherSpendings = widget.budget.includeOtherSpendings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: "Name",
|
||||||
|
),
|
||||||
|
controller: _budgetNameEditController,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text("Include other spendings"),
|
||||||
|
Switch(
|
||||||
|
value: _includeOtherSpendings,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => _includeOtherSpendings = value);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
OutlinedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
if (_budgetNameEditController.text.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_budgetNameEditController.text == widget.budget.name && _includeOtherSpendings == widget.budget.includeOtherSpendings) {
|
||||||
|
widget.onDone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
widget.budget
|
||||||
|
..name = _budgetNameEditController.text
|
||||||
|
..includeOtherSpendings = _includeOtherSpendings;
|
||||||
|
await upsertBudget(widget.budget);
|
||||||
|
widget.onDone();
|
||||||
|
},
|
||||||
|
child: Text("Save"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,9 @@ import 'package:flutter/material.dart';
|
|||||||
class AccountIndicator extends StatelessWidget {
|
class AccountIndicator extends StatelessWidget {
|
||||||
final String accountName;
|
final String accountName;
|
||||||
|
|
||||||
const AccountIndicator({super.key, required this.accountName});
|
final Widget? trailing;
|
||||||
|
|
||||||
|
const AccountIndicator({super.key, this.trailing, required this.accountName});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -12,12 +14,18 @@ class AccountIndicator extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Padding(
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
child: Text(
|
||||||
accountName,
|
accountName,
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
if (trailing != null)
|
||||||
|
trailing!,
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user