search on budget + transactions; extended FAB for add tx; M3 expressive SearchBar

This commit is contained in:
alexis 2026-07-12 00:20:41 +02:00
parent 993e4f62a1
commit 83523b1811
5 changed files with 46 additions and 15 deletions

View file

@ -93,7 +93,7 @@ class _YnabAppState extends ConsumerState<YnabApp> {
),
),
actions: [
TextButton(
FilledButton.tonal(
onPressed: () => Navigator.pop(ctx),
child: const Text('Annuler'),
),
@ -139,14 +139,12 @@ class _YnabAppState extends ConsumerState<YnabApp> {
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<YnabApp> {
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<YnabApp> {
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) {

View file

@ -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'),
),

View file

@ -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(',', '.'));

View file

@ -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'),

View file

@ -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(