From bb3d4ebe2ab94257fef7b73a6eebed4c5a5bd2c1 Mon Sep 17 00:00:00 2001 From: alexis Date: Sun, 12 Jul 2026 01:00:52 +0200 Subject: [PATCH] =?UTF-8?q?dialogue=20transaction:=20AlertDialog=20?= =?UTF-8?q?=E2=86=92=20BottomSheet=20M3=20expressive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/app.dart | 132 +++++++++++++++++++-------------------- lib/theme/app_theme.dart | 1 + 2 files changed, 66 insertions(+), 67 deletions(-) diff --git a/lib/app.dart b/lib/app.dart index 56733f8..1c03dcd 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'theme/app_theme.dart'; import 'screens/dashboard_screen.dart'; import 'screens/budget_screen.dart'; import 'screens/accounts_screen.dart'; @@ -61,76 +60,75 @@ class _YnabAppState extends ConsumerState { DateTime date = DateTime.now(); bool isExpense = true; - await showDialog( + await showModalBottomSheet( context: context, + isScrollControlled: true, + useSafeArea: true, builder: (ctx) => StatefulBuilder( - builder: (context, setDialogState) => AlertDialog( - title: const Text('Nouvelle transaction'), - content: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - controller: payeeCtrl, - decoration: const InputDecoration(labelText: 'Bénéficiaire', hintText: 'Ex: Supermarché'), - autofocus: true, + builder: (context, setSheetState) => Padding( + padding: EdgeInsets.fromLTRB(24, 0, 24, MediaQuery.of(context).viewInsets.bottom + 24), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox(height: 8), + Text('Nouvelle transaction', style: Theme.of(context).textTheme.titleLarge), + const SizedBox(height: 20), + TextField( + controller: payeeCtrl, + decoration: const InputDecoration(labelText: 'Bénéficiaire', hintText: 'Ex: Supermarché'), + autofocus: true, + ), + const SizedBox(height: 12), + TextField( + controller: amountCtrl, + decoration: const InputDecoration(labelText: 'Montant', hintText: '0,00'), + keyboardType: const TextInputType.numberWithOptions(decimal: true), + ), + const SizedBox(height: 12), + SegmentedButton( + segments: const [ + ButtonSegment(value: true, label: Text('Dépense'), icon: Icon(Icons.arrow_upward)), + ButtonSegment(value: false, label: Text('Revenu'), icon: Icon(Icons.arrow_downward)), + ], + selected: {isExpense}, + onSelectionChanged: (v) => setSheetState(() => isExpense = v.first), + style: ButtonStyle( + visualDensity: VisualDensity.compact, ), - const SizedBox(height: 12), - TextField( - controller: amountCtrl, - decoration: const InputDecoration(labelText: 'Montant', hintText: '0,00'), - keyboardType: const TextInputType.numberWithOptions(decimal: true), - ), - const SizedBox(height: 12), - SegmentedButton( - segments: const [ - ButtonSegment(value: true, label: Text('Dépense'), icon: Icon(Icons.arrow_upward)), - ButtonSegment(value: false, label: Text('Revenu'), icon: Icon(Icons.arrow_downward)), - ], - selected: {isExpense}, - onSelectionChanged: (v) => setDialogState(() => isExpense = v.first), - style: ButtonStyle( - visualDensity: VisualDensity.compact, - ), - ), - const SizedBox(height: 12), - DropdownMenu( - initialSelection: accountId, - label: const Text('Compte'), - expandedInsets: EdgeInsets.zero, - dropdownMenuEntries: accounts - .map((a) => DropdownMenuEntry(value: a.id, label: a.name)) - .toList(), - onSelected: (v) { - if (v != null) accountId = v; - }, - ), - ], - ), + ), + const SizedBox(height: 12), + DropdownMenu( + initialSelection: accountId, + label: const Text('Compte'), + expandedInsets: EdgeInsets.zero, + dropdownMenuEntries: accounts + .map((a) => DropdownMenuEntry(value: a.id, label: a.name)) + .toList(), + onSelected: (v) { + if (v != null) accountId = v; + }, + ), + const SizedBox(height: 24), + FilledButton( + onPressed: () async { + final payee = payeeCtrl.text.trim(); + final amountStr = amountCtrl.text.trim().replaceAll(',', '.'); + final raw = double.tryParse(amountStr); + if (payee.isEmpty || raw == null || accountId == null) return; + final amount = isExpense ? -raw.abs() : raw.abs(); + await db.into(db.transactions).insert(TransactionsCompanion.insert( + accountId: accountId!, + payee: payee, + amount: amount, + date: date, + )); + if (context.mounted) Navigator.pop(context); + }, + child: const Text('Ajouter'), + ), + ], ), - actions: [ - FilledButton.tonal( - onPressed: () => Navigator.pop(ctx), - child: const Text('Annuler'), - ), - FilledButton( - onPressed: () async { - final payee = payeeCtrl.text.trim(); - final amountStr = amountCtrl.text.trim().replaceAll(',', '.'); - final raw = double.tryParse(amountStr); - if (payee.isEmpty || raw == null || accountId == null) return; - final amount = isExpense ? -raw.abs() : raw.abs(); - await db.into(db.transactions).insert(TransactionsCompanion.insert( - accountId: accountId!, - payee: payee, - amount: amount, - date: date, - )); - if (ctx.mounted) Navigator.pop(ctx); - }, - child: const Text('Ajouter'), - ), - ], ), ), ); diff --git a/lib/theme/app_theme.dart b/lib/theme/app_theme.dart index a20f920..9cf4222 100644 --- a/lib/theme/app_theme.dart +++ b/lib/theme/app_theme.dart @@ -109,6 +109,7 @@ final ThemeData ynabTheme = ThemeData( ), bottomSheetTheme: const BottomSheetThemeData( backgroundColor: kSurfaceCard, + showDragHandle: true, shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ),