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

View File

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

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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/database/database.dart';
import 'package:okane/ui/pages/loans/add_loan_change.dart'; import 'package:okane/ui/pages/loans/add_loan_change.dart';
import 'package:okane/ui/state/core.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'; import 'package:okane/ui/widgets/image_wrapper.dart';
class LoanDetailsPage extends StatelessWidget { 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Stack( return Scaffold(
children: [ body: Column(
BlocBuilder<CoreCubit, CoreState>( children: [
builder: (context, state) { if (isPage)
if (state.activeLoan == null) { SizedBox(
return Text("No loan selected"); height: 50,
} child: Row(
children: [
final loans = state.activeLoan!.changes.toList(); IconButton(
final loanSum = loans icon: Icon(Icons.arrow_back),
.map((c) => c.amount) onPressed: () {
.reduce((acc, val) => acc + val); Navigator.of(context).pop();
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),
],
), ),
), ],
),
),
Expanded(
child: BlocBuilder<CoreCubit, CoreState>(
builder: (context, state) {
if (state.activeLoan == null) {
return Text("No loan selected");
}
SliverToBoxAdapter( final loanChanges = state.activeLoan!.changes.toList();
child: Text("Total: ${formatCurrency(loanSum)}"), final loanSum =
), loanChanges.isNotEmpty
? loanChanges
SliverToBoxAdapter( .map((c) => c.amount)
child: Row( .reduce((acc, val) => acc + val)
children: [ : 0.0;
Text("Loan Transactions"), return CustomScrollView(
slivers: [
IconButton( SliverToBoxAdapter(
onPressed: () { child: Row(
showDialogOrModal( children: [
context: context, ImageWrapper(
builder: title: state.activeLoan!.beneficiary.value!.name,
(_) => AddLoanChangePopup( path:
loan: state.activeLoan!, state.activeLoan!.beneficiary.value!.imagePath,
onDone: () { ),
Navigator.of(context).pop(); Text(state.activeLoan!.beneficiary.value!.name),
}, ],
),
);
},
icon: Icon(Icons.add),
), ),
], ),
),
),
SliverList.builder( SliverToBoxAdapter(
itemCount: loans.length, child: Text("Total: ${formatCurrency(loanSum)}"),
itemBuilder: (context, index) { ),
final item = loans[index];
return ListTile( SliverToBoxAdapter(
leading: child: Row(
item.amount > 0 children: [
? Icon(Icons.add, color: Colors.green) Text("Loan Transactions"),
: Icon(Icons.remove, color: Colors.red),
title: Text(formatCurrency(item.amount)), IconButton(
trailing: IconButton( onPressed: () {
icon: Icon(Icons.delete, color: Colors.red), showDialogOrModal(
onPressed: () async { context: context,
state.activeLoan!.changes.remove(item); builder:
await deleteLoanChange(item); (_) => AddLoanChangePopup(
await upsertLoan(state.activeLoan!); 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:flutter_bloc/flutter_bloc.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'package:okane/database/database.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/pages/loans/add_loan.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';
@ -28,6 +29,10 @@ class LoanListPage extends StatelessWidget {
), ),
onTap: () { onTap: () {
GetIt.I.get<CoreCubit>().setActiveLoan(item); GetIt.I.get<CoreCubit>().setActiveLoan(item);
if (getScreenSize(context) == ScreenSize.small) {
Navigator.of(context).pushNamed("/loans/details");
}
}, },
trailing: IconButton( trailing: IconButton(
onPressed: () async { onPressed: () async {