Make the loan details work on mobile

This commit is contained in:
PapaTutuWawa 2025-05-12 22:01:53 +02:00
parent c5aa165424
commit 88c9991e0d
4 changed files with 156 additions and 77 deletions

View File

@ -14,6 +14,8 @@ import 'package:okane/ui/pages/transaction_details.dart';
import 'package:okane/ui/state/core.dart';
import 'package:okane/ui/state/settings.dart';
import 'ui/pages/loans/loan_details.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
LocaleSettings.useDeviceLocale();
@ -66,6 +68,7 @@ class MyApp extends StatelessWidget {
"/transactions/details" =>
TransactionDetailsPage.mobileRoute,
"/budgets/details" => BudgetDetailsPage.mobileRoute,
"/loans/details" => LoanDetailsPage.mobileRoute,
_ => MaterialPageRoute<void>(
builder: (_) => Text("Unknown!!"),
),

View File

@ -112,7 +112,7 @@ final _pages = <OkanePageItem>[
Icons.money_outlined,
"Loans",
LoanListPage(),
(_) => LoanDetailsPage(),
(_) => LoanDetailsPage(isPage: false),
false,
),
OkanePageItem(

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:grouped_list/grouped_list.dart';
import 'package:okane/database/collections/loan.dart';
import 'package:okane/database/database.dart';
import 'package:okane/ui/pages/loans/add_loan_change.dart';
import 'package:okane/ui/state/core.dart';
@ -7,91 +9,160 @@ import 'package:okane/ui/utils.dart';
import 'package:okane/ui/widgets/image_wrapper.dart';
class LoanDetailsPage extends StatelessWidget {
const LoanDetailsPage({super.key});
final bool isPage;
const LoanDetailsPage({super.key, required this.isPage});
static MaterialPageRoute<void> get mobileRoute =>
MaterialPageRoute(builder: (_) => LoanDetailsPage(isPage: true));
@override
Widget build(BuildContext context) {
return Stack(
children: [
BlocBuilder<CoreCubit, CoreState>(
builder: (context, state) {
if (state.activeLoan == null) {
return Text("No loan selected");
}
final loans = state.activeLoan!.changes.toList();
final loanSum = loans
.map((c) => c.amount)
.reduce((acc, val) => acc + val);
return CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Row(
children: [
ImageWrapper(
title: state.activeLoan!.beneficiary.value!.name,
path: state.activeLoan!.beneficiary.value!.imagePath,
),
Text(state.activeLoan!.beneficiary.value!.name),
],
return Scaffold(
body: Column(
children: [
if (isPage)
SizedBox(
height: 50,
child: Row(
children: [
IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
Navigator.of(context).pop();
},
),
),
],
),
),
Expanded(
child: BlocBuilder<CoreCubit, CoreState>(
builder: (context, state) {
if (state.activeLoan == null) {
return Text("No loan selected");
}
SliverToBoxAdapter(
child: Text("Total: ${formatCurrency(loanSum)}"),
),
SliverToBoxAdapter(
child: Row(
children: [
Text("Loan Transactions"),
IconButton(
onPressed: () {
showDialogOrModal(
context: context,
builder:
(_) => AddLoanChangePopup(
loan: state.activeLoan!,
onDone: () {
Navigator.of(context).pop();
},
),
);
},
icon: Icon(Icons.add),
final loanChanges = state.activeLoan!.changes.toList();
final loanSum =
loanChanges.isNotEmpty
? loanChanges
.map((c) => c.amount)
.reduce((acc, val) => acc + val)
: 0.0;
return CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Row(
children: [
ImageWrapper(
title: state.activeLoan!.beneficiary.value!.name,
path:
state.activeLoan!.beneficiary.value!.imagePath,
),
Text(state.activeLoan!.beneficiary.value!.name),
],
),
],
),
),
),
SliverList.builder(
itemCount: loans.length,
itemBuilder: (context, index) {
final item = loans[index];
SliverToBoxAdapter(
child: Text("Total: ${formatCurrency(loanSum)}"),
),
return ListTile(
leading:
item.amount > 0
? Icon(Icons.add, color: Colors.green)
: Icon(Icons.remove, color: Colors.red),
title: Text(formatCurrency(item.amount)),
trailing: IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () async {
state.activeLoan!.changes.remove(item);
await deleteLoanChange(item);
await upsertLoan(state.activeLoan!);
},
SliverToBoxAdapter(
child: Row(
children: [
Text("Loan Transactions"),
IconButton(
onPressed: () {
showDialogOrModal(
context: context,
builder:
(_) => AddLoanChangePopup(
loan: state.activeLoan!,
onDone: () {
Navigator.of(context).pop();
},
),
);
},
icon: Icon(Icons.add),
),
],
),
);
},
),
],
);
},
),
],
),
SliverToBoxAdapter(
child:
loanChanges.isNotEmpty
? GroupedListView(
elements: loanChanges,
shrinkWrap: true,
reverse: true,
groupBy:
(LoanChange loanChange) =>
formatDateTime(loanChange.date),
groupHeaderBuilder:
(item) => Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
DecoratedBox(
decoration: BoxDecoration(
color: Colors.black.withAlpha(170),
borderRadius: BorderRadius.circular(
8,
),
),
child: Padding(
padding: EdgeInsets.all(4),
child: Text(
formatDateTime(item.date),
style: TextStyle(
color: Colors.white,
),
),
),
),
],
),
indexedItemBuilder:
(ctx, item, idx) => ListTile(
leading:
item.amount > 0
? Icon(
Icons.add,
color: Colors.green,
)
: Icon(
Icons.remove,
color: Colors.red,
),
title: Text(formatCurrency(item.amount)),
trailing: IconButton(
icon: Icon(
Icons.delete,
color: Colors.red,
),
onPressed: () async {
state.activeLoan!.changes.remove(
item,
);
await deleteLoanChange(item);
await upsertLoan(state.activeLoan!);
},
),
),
)
: Text("No transactions available"),
),
],
);
},
),
),
],
),
);
}
}

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:get_it/get_it.dart';
import 'package:okane/database/database.dart';
import 'package:okane/screen.dart';
import 'package:okane/ui/pages/loans/add_loan.dart';
import 'package:okane/ui/state/core.dart';
import 'package:okane/ui/utils.dart';
@ -28,6 +29,10 @@ class LoanListPage extends StatelessWidget {
),
onTap: () {
GetIt.I.get<CoreCubit>().setActiveLoan(item);
if (getScreenSize(context) == ScreenSize.small) {
Navigator.of(context).pushNamed("/loans/details");
}
},
trailing: IconButton(
onPressed: () async {