From 83523b1811c49c9304e4a6f153884d9042e173a6 Mon Sep 17 00:00:00 2001 From: alexis Date: Sun, 12 Jul 2026 00:20:41 +0200 Subject: [PATCH] search on budget + transactions; extended FAB for add tx; M3 expressive SearchBar --- lib/app.dart | 25 +++++++++++++++---------- lib/screens/accounts_screen.dart | 2 +- lib/screens/budget_screen.dart | 21 ++++++++++++++++++--- lib/screens/dashboard_screen.dart | 2 +- lib/theme/app_theme.dart | 11 +++++++++++ 5 files changed, 46 insertions(+), 15 deletions(-) diff --git a/lib/app.dart b/lib/app.dart index fbb96d4..6e8668e 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -93,7 +93,7 @@ class _YnabAppState extends ConsumerState { ), ), actions: [ - TextButton( + FilledButton.tonal( onPressed: () => Navigator.pop(ctx), child: const Text('Annuler'), ), @@ -139,14 +139,12 @@ class _YnabAppState extends ConsumerState { return Scaffold( appBar: AppBar( title: _searchActive - ? TextField( + ? SearchBar( controller: _searchController, - autofocus: true, - decoration: const InputDecoration( - hintText: 'Rechercher…', - border: InputBorder.none, - isDense: true, - ), + hintText: 'Rechercher…', + leading: const Icon(Icons.search, size: 20), + padding: const WidgetStatePropertyAll(EdgeInsets.symmetric(horizontal: 4)), + constraints: BoxConstraints.tightFor(height: 44), onChanged: (v) => ref.read(searchQueryProvider.notifier).state = v, ) : Text(_navItems[selectedIndex].label), @@ -158,13 +156,13 @@ class _YnabAppState extends ConsumerState { tooltip: 'Fermer la recherche', ) else ...[ - if (selectedIndex == 0 || selectedIndex == 3) + if (selectedIndex == 0) IconButton( icon: Icon(Icons.add, color: colorScheme.primary), onPressed: _addTransaction, tooltip: 'Ajouter une transaction', ), - if (selectedIndex == 2 || selectedIndex == 3) + if (selectedIndex >= 1) IconButton( icon: Icon(Icons.search, color: colorScheme.onSurfaceVariant), onPressed: _toggleSearch, @@ -177,6 +175,13 @@ class _YnabAppState extends ConsumerState { duration: const Duration(milliseconds: 200), child: _screens[selectedIndex], ), + floatingActionButton: selectedIndex == 3 + ? FloatingActionButton.extended( + icon: const Icon(Icons.add), + label: const Text('Transaction'), + onPressed: _addTransaction, + ) + : null, bottomNavigationBar: NavigationBar( selectedIndex: selectedIndex, onDestinationSelected: (int index) { diff --git a/lib/screens/accounts_screen.dart b/lib/screens/accounts_screen.dart index f49002f..c66bc92 100644 --- a/lib/screens/accounts_screen.dart +++ b/lib/screens/accounts_screen.dart @@ -126,7 +126,7 @@ class _AccountCard extends ConsumerWidget { child: const Text('Supprimer'), ), const SizedBox(width: 8), - TextButton( + FilledButton.tonal( onPressed: () => Navigator.pop(ctx), child: const Text('Annuler'), ), diff --git a/lib/screens/budget_screen.dart b/lib/screens/budget_screen.dart index 26035e8..46d962d 100644 --- a/lib/screens/budget_screen.dart +++ b/lib/screens/budget_screen.dart @@ -12,10 +12,25 @@ class BudgetScreen extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final groups = ref.watch(categoryGroupsProvider).valueOrNull ?? []; final catsByGroup = ref.watch(categoriesByGroupProvider); + final query = ref.watch(searchQueryProvider).toLowerCase(); final now = DateTime.now(); final months = ['', 'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre']; final monthLabel = '${months[now.month]} ${now.year}'; + // filter categories and groups by search query + final filteredGroups = query.isEmpty + ? groups + : groups.where((g) { + final cats = catsByGroup[g.id] ?? []; + return cats.any((c) => c.name.toLowerCase().contains(query)); + }).toList(); + final filteredCatsByGroup = query.isEmpty + ? catsByGroup + : catsByGroup.map((key, value) => MapEntry( + key, + value.where((c) => c.name.toLowerCase().contains(query)).toList(), + )); + return ListView( padding: const EdgeInsets.fromLTRB(16, 8, 16, 90), children: [ @@ -33,8 +48,8 @@ class BudgetScreen extends ConsumerWidget { ), ), const SizedBox(height: 16), - ...groups.map((group) { - final cats = catsByGroup[group.id] ?? []; + ...filteredGroups.map((group) { + final cats = filteredCatsByGroup[group.id] ?? []; if (cats.isEmpty) return const SizedBox.shrink(); return _CategoryGroupTile(group: group, categoryIds: cats.map((c) => c.id).toList()); }), @@ -119,7 +134,7 @@ class _CategoryRow extends ConsumerWidget { autofocus: true, ), actions: [ - TextButton(onPressed: () => Navigator.pop(ctx), child: const Text('Annuler')), + FilledButton.tonal(onPressed: () => Navigator.pop(ctx), child: const Text('Annuler')), FilledButton( onPressed: () { final v = double.tryParse(ctrl.text.trim().replaceAll(',', '.')); diff --git a/lib/screens/dashboard_screen.dart b/lib/screens/dashboard_screen.dart index e9de735..468d2de 100644 --- a/lib/screens/dashboard_screen.dart +++ b/lib/screens/dashboard_screen.dart @@ -31,7 +31,7 @@ class DashboardScreen extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Transactions récentes', style: Theme.of(context).textTheme.titleMedium), - TextButton.icon( + FilledButton.tonalIcon( onPressed: () {}, icon: const Icon(Icons.arrow_forward_ios, size: 14), label: const Text('Voir tout'), diff --git a/lib/theme/app_theme.dart b/lib/theme/app_theme.dart index 7e83b63..a20f920 100644 --- a/lib/theme/app_theme.dart +++ b/lib/theme/app_theme.dart @@ -123,6 +123,17 @@ final ThemeData ynabTheme = ThemeData( backgroundColor: kSurfaceCard, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), ), + searchBarTheme: SearchBarThemeData( + elevation: WidgetStatePropertyAll(0), + backgroundColor: WidgetStatePropertyAll(kSurfaceCard), + shape: WidgetStatePropertyAll(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(28), + side: BorderSide(color: kDivider), + )), + padding: WidgetStatePropertyAll(EdgeInsets.symmetric(horizontal: 8)), + textStyle: WidgetStatePropertyAll(GoogleFonts.inter(fontSize: 14, color: kTextPrimary)), + hintStyle: WidgetStatePropertyAll(GoogleFonts.inter(fontSize: 14, color: kTextSecondary)), + ), ); final ThemeData ynabDarkTheme = ThemeData(