Feat: Macht die Kontoauswahl funktional

This commit is contained in:
2025-12-25 16:23:01 +01:00
parent c11515d447
commit 05a5bddf09
4 changed files with 162 additions and 81 deletions

View File

@@ -12,8 +12,11 @@ import '../Repositories/account_repository.dart';
/// Steuert die Interaktion mit den Accounts /// Steuert die Interaktion mit den Accounts
class AccountController { class AccountController {
/// Gibt die aktuell gültige Instanz der Klasse zurück
factory AccountController() => _instance;
/// Erstellt eine neue Instanz dieser Klasse /// Erstellt eine neue Instanz dieser Klasse
AccountController() { AccountController._internal() {
_newAccountDialog = DynamicDialog( _newAccountDialog = DynamicDialog(
title: 'Neues Konto erstellen', title: 'Neues Konto erstellen',
icon: Icons.account_balance_wallet, icon: Icons.account_balance_wallet,
@@ -45,14 +48,32 @@ class AccountController {
); );
} }
static final AccountController _instance = AccountController._internal();
BuildContext? _buildContext; BuildContext? _buildContext;
final AccountRepository _accountRepository = AccountRepository(); final AccountRepository _accountRepository = AccountRepository();
DynamicDialog? _newAccountDialog; DynamicDialog? _newAccountDialog;
DynamicDialog? _errorNameEmptyDialog; DynamicDialog? _errorNameEmptyDialog;
DynamicDialog? _accountCreatedDialog; DynamicDialog? _accountCreatedDialog;
Account? _selected;
/// Stellt das ausgewählte Konto dar, das angezeigt wird
Future<Account?> get selected async => _selected ??= (await getAccounts())[0];
set selected(final Account selected) {
_selected = selected;
}
/// Gibt die gespeicherten Konten als Liste zurück
Future<List<Account>> getAccounts() async {
final List<Account> accounts = await _accountRepository.findBy(
orderBy: 'nameAsc',
);
return accounts;
}
/// Startet den Prozess um ein neues Konto anzulegen /// Startet den Prozess um ein neues Konto anzulegen
void newAccountHandler(final BuildContext buildContext) { void newAccountHandler(final BuildContext buildContext) {
_buildContext = buildContext; _buildContext = buildContext;

View File

@@ -0,0 +1,66 @@
import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart';
import '../../Controller/account_controller.dart';
import '../../Entities/drift_database.dart';
/// Ein Dropdown, mit welchem man das Konto auswählen kann
class AccountSelect extends StatefulWidget {
/// Erstellt eine neue Instanz dieser Klasse
const AccountSelect({super.key});
@override
State<AccountSelect> createState() => _AccountSelectState();
}
class _AccountSelectState extends State<AccountSelect> {
final AccountController _accountController = AccountController();
@override
Widget build(final BuildContext context) {
final Future<Account?> selected = _accountController.selected;
return FutureBuilder(
future: selected,
builder:
(final BuildContext context, final AsyncSnapshot<Account?> snapshot) {
if (snapshot.hasData) {
return DropdownSearch<Account>(
items: (final f, final cs) => _accountController.getAccounts(),
selectedItem: snapshot.data,
onChanged: (final Account? account) {
if (account != null) {
_accountController.selected = account;
}
},
itemAsString: (final Account account) => account.name,
compareFn: (final Account a1, final Account a2) =>
a1.id == a2.id,
popupProps: const PopupProps<Account>.menu(
showSearchBox: true,
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: 'Konto suchen...',
contentPadding: EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
),
),
),
);
} else if (snapshot.hasError) {
return const Row(
children: [
Icon(Icons.error, color: Colors.red),
Text('Fehler beim Laden der Konten!'),
],
);
} else {
return const CircularProgressIndicator();
}
},
);
}
}

View File

@@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
import '../../Controller/account_controller.dart';
/// Ein Floating Action Button, der beim Klicken ein expandierendes Menü öffnet,
/// um neue Transaktionen oder Konten anzulegen.
class FloatingCreationButton extends StatefulWidget {
/// Erstellt eine neue Instanz dieser Klasse
const FloatingCreationButton({super.key});
@override
State<FloatingCreationButton> createState() => _FloatingCreationButtonState();
}
class _FloatingCreationButtonState extends State<FloatingCreationButton> {
final AccountController _accountController = AccountController();
@override
Widget build(final BuildContext context) => ExpandableFab(
openButtonBuilder: RotateFloatingActionButtonBuilder(
child: const Icon(Icons.add),
),
type: ExpandableFabType.up,
childrenAnimation: ExpandableFabAnimation.none,
distance: 70,
children: <Widget>[
_expandableButton(
label: 'Neue Transaktion',
icon: Icons.add,
onPressed: () {},
),
_expandableButton(
label: 'Neues Konto',
icon: Icons.account_balance_wallet,
onPressed: () {
_accountController.newAccountHandler(context);
},
),
],
);
Widget _expandableButton({
required final String label,
required final IconData icon,
required final VoidCallback onPressed,
}) => GestureDetector(
onTap: onPressed,
child: Row(
children: <Widget>[
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
child: Text(label),
),
const SizedBox(width: 12),
FloatingActionButton.small(
heroTag: null,
onPressed: onPressed,
child: Icon(icon),
),
],
),
);
}

View File

@@ -1,28 +1,19 @@
import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart'; import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
import 'package:routemaster/routemaster.dart'; import 'package:routemaster/routemaster.dart';
import '../Controller/account_controller.dart'; import 'Misc/account_select.dart';
import 'Misc/floating_creation_button.dart';
/// Eine Seite, die als Container für die verschiedenen Tabs der App dient. /// Eine Seite, die als Container für die verschiedenen Tabs der App dient.
/// ///
/// Diese Seite enthält eine App-Bar mit einer Kontoauswahl sowie ein /// Diese Seite enthält eine App-Bar mit einer Kontoauswahl sowie ein
/// Bottom-Navigation-Bar für die Navigation zwischen /// Bottom-Navigation-Bar für die Navigation zwischen
/// Dashboard, Verlauf und Einstellungen. /// Dashboard, Verlauf und Einstellungen.
class HomePage extends StatefulWidget { class HomePage extends StatelessWidget {
/// Erstellt eine neue Instanz der HomePage. /// Erstellt eine neue Instanz dieser Klasse
const HomePage({super.key}); const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final AccountController _accountController = AccountController();
String selected = 'Konto 1';
@override @override
Widget build(final BuildContext context) { Widget build(final BuildContext context) {
final TabPageState tabPage = TabPage.of(context); final TabPageState tabPage = TabPage.of(context);
@@ -31,9 +22,9 @@ class _HomePageState extends State<HomePage> {
appBar: AppBar( appBar: AppBar(
centerTitle: true, centerTitle: true,
titleSpacing: 0, titleSpacing: 0,
title: Padding( title: const Padding(
padding: const EdgeInsets.symmetric(horizontal: 12), padding: EdgeInsets.symmetric(horizontal: 12),
child: _accountSelect(), child: AccountSelect(),
), ),
), ),
@@ -53,7 +44,7 @@ class _HomePageState extends State<HomePage> {
bottomNavigationBar: _bottomNav(tabPage), bottomNavigationBar: _bottomNav(tabPage),
floatingActionButtonLocation: ExpandableFab.location, floatingActionButtonLocation: ExpandableFab.location,
floatingActionButton: _floatingActionButton(), floatingActionButton: const FloatingCreationButton(),
); );
} }
@@ -69,66 +60,4 @@ class _HomePageState extends State<HomePage> {
), ),
], ],
); );
Widget _accountSelect() => DropdownSearch<String>(
items: (final String filter, final LoadProps? infiniteScrollProps) =>
<String>['Konto 1', 'Konto 2', 'Konto 3', 'Konto 4'],
selectedItem: selected,
onChanged: (final String? value) => setState(() => selected = value!),
popupProps: const PopupProps<String>.menu(
showSearchBox: true,
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: 'Suchen...',
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
),
),
),
);
Widget _floatingActionButton() => ExpandableFab(
openButtonBuilder: RotateFloatingActionButtonBuilder(
child: const Icon(Icons.add),
),
type: ExpandableFabType.up,
childrenAnimation: ExpandableFabAnimation.none,
distance: 70,
children: <Widget>[
_expandableButton(
label: 'Neue Transaktion',
icon: Icons.add,
onPressed: () {},
),
_expandableButton(
label: 'Neues Konto',
icon: Icons.account_balance_wallet,
onPressed: () {
_accountController.newAccountHandler(context);
},
),
],
);
Widget _expandableButton({
required final String label,
required final IconData icon,
required final VoidCallback onPressed,
}) => GestureDetector(
onTap: onPressed,
child: Row(
children: <Widget>[
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)),
child: Text(label),
),
const SizedBox(width: 12),
FloatingActionButton.small(
heroTag: null,
onPressed: onPressed,
child: Icon(icon),
),
],
),
);
} }