import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../core/constants.dart'; import '../../data/models/note.dart'; import '../../providers/notes_provider.dart'; import 'note_detail_screen.dart'; class NotesListScreen extends ConsumerStatefulWidget { const NotesListScreen({super.key}); @override ConsumerState createState() => _NotesListScreenState(); } class _NotesListScreenState extends ConsumerState { bool _showSearch = false; String _search = ''; int? _selectedNoteId; @override Widget build(BuildContext context) { final notesAsync = ref.watch(notesProvider); final isWide = MediaQuery.of(context).size.width >= 600; // Clear stale selection when switching to narrow mode. if (!isWide && _selectedNoteId != null) { _selectedNoteId = null; } return Scaffold( body: isWide ? Row( children: [ SizedBox( width: 300, child: Column( children: [ Expanded(child: _buildListPane(notesAsync, isWide)), const Divider(height: 1), ListTile( leading: const Icon(Icons.add), title: const Text('New note'), onTap: () => context.push(Routes.noteNew), ), ], ), ), const VerticalDivider(width: 1), Expanded(child: _buildDetailPane()), ], ) : _buildListPane(notesAsync, isWide), floatingActionButton: isWide ? null : FloatingActionButton( heroTag: 'notes_fab', onPressed: () => context.push(Routes.noteNew), child: const Icon(Icons.add), ), ); } Widget _buildDetailPane() { if (_selectedNoteId == null) { return const Center(child: Text('Select a note to read it.')); } return NoteDetailScreen( key: ValueKey(_selectedNoteId), noteId: _selectedNoteId!, onDeleted: () => setState(() => _selectedNoteId = null), ); } Widget _buildListPane(AsyncValue> notesAsync, bool isWide) { return Stack( children: [ Column( children: [ if (_showSearch) Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: Row( children: [ Expanded( child: TextField( autofocus: true, decoration: const InputDecoration( hintText: 'Search notes…', border: InputBorder.none, prefixIcon: Icon(Icons.search), ), onChanged: (v) => setState(() => _search = v.trim().toLowerCase()), ), ), IconButton( icon: const Icon(Icons.close), tooltip: 'Close search', onPressed: () => setState(() { _showSearch = false; _search = ''; }), ), ], ), ), Expanded( child: notesAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (_, _) => Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.cloud_off, size: 48), const SizedBox(height: 12), const Text('Could not load notes.'), const SizedBox(height: 4), TextButton( onPressed: () => ref.invalidate(notesProvider), child: const Text('Retry'), ), ], ), ), data: (notes) { final filtered = _search.isEmpty ? notes : notes .where((n) => n.title.toLowerCase().contains(_search) || n.body.toLowerCase().contains(_search)) .toList(); if (filtered.isEmpty) { return Center( child: Text( _search.isEmpty ? 'No notes yet. Tap + to create one.' : 'No notes match "$_search".', ), ); } return RefreshIndicator( onRefresh: () => ref.refresh(notesProvider.future), child: ListView.separated( itemCount: filtered.length, separatorBuilder: (_, _) => const Divider(height: 1), itemBuilder: (context, i) { final note = filtered[i]; final preview = note.body .split('\n') .firstWhere((l) => l.trim().isNotEmpty, orElse: () => '') .trim(); return ListTile( title: Text(note.title), subtitle: Text( preview.isNotEmpty ? preview : note.updatedAt .toLocal() .toString() .substring(0, 16), style: Theme.of(context) .textTheme .bodySmall ?.copyWith( color: Theme.of(context) .colorScheme .onSurfaceVariant, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), selected: isWide && _selectedNoteId == note.id, selectedTileColor: Theme.of(context) .colorScheme .secondaryContainer, onTap: () { if (isWide) { setState(() => _selectedNoteId = note.id); } else { context.push( Routes.noteDetail .replaceFirst(':id', '${note.id}'), ); } }, ); }, ), ); }, ), ), ], ), // Floating search button — only visible when search is closed. if (!_showSearch) Positioned( top: 4, right: 4, child: IconButton( icon: const Icon(Icons.search), tooltip: 'Search', onPressed: () => setState(() => _showSearch = true), ), ), ], ); } }