import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../core/exceptions.dart'; import '../../providers/api_client_provider.dart'; import '../../providers/notes_provider.dart'; class NoteEditScreen extends ConsumerStatefulWidget { final int? noteId; const NoteEditScreen({super.key, this.noteId}); @override ConsumerState createState() => _NoteEditScreenState(); } class _NoteEditScreenState extends ConsumerState { final _titleController = TextEditingController(); final _contentController = TextEditingController(); bool _preview = false; bool _saving = false; bool _loaded = false; @override void dispose() { _titleController.dispose(); _contentController.dispose(); super.dispose(); } Future _loadExisting() async { if (_loaded || widget.noteId == null) { _loaded = true; return; } final note = await ref.read(notesRepositoryProvider).getOne(widget.noteId!); _titleController.text = note.title; _contentController.text = note.body; _loaded = true; } Future _delete() async { final confirm = await showDialog( context: context, builder: (dialogContext) => AlertDialog( title: const Text('Delete note?'), content: Text(_titleController.text), actions: [ TextButton( onPressed: () => Navigator.pop(dialogContext, false), child: const Text('Cancel'), ), TextButton( onPressed: () => Navigator.pop(dialogContext, true), child: const Text('Delete'), ), ], ), ); if (confirm == true) { await ref.read(notesProvider.notifier).delete(widget.noteId!); if (mounted) context.pop(); } } Future _save() async { final title = _titleController.text.trim(); final body = _contentController.text; if (title.isEmpty) { ScaffoldMessenger.of(context) .showSnackBar(const SnackBar(content: Text('Title is required.'))); return; } setState(() => _saving = true); try { if (widget.noteId == null) { await ref.read(notesProvider.notifier).create(title, body); if (mounted) context.pop(); } else { await ref .read(notesProvider.notifier) .updateNote(widget.noteId!, title, body); if (mounted) context.pop(); } } on AppException catch (e) { if (mounted) { ScaffoldMessenger.of(context) .showSnackBar(SnackBar(content: Text(e.message))); } } finally { if (mounted) setState(() => _saving = false); } } @override Widget build(BuildContext context) { return FutureBuilder( future: _loadExisting(), builder: (context, snapshot) { return Scaffold( appBar: AppBar( title: Text(widget.noteId == null ? 'New Note' : 'Edit Note'), actions: [ if (widget.noteId != null) IconButton( icon: const Icon(Icons.delete_outline), tooltip: 'Delete', onPressed: _delete, ), IconButton( icon: Icon(_preview ? Icons.edit : Icons.preview), tooltip: _preview ? 'Edit' : 'Preview', onPressed: () => setState(() => _preview = !_preview), ), IconButton( icon: _saving ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.check), onPressed: _saving ? null : _save, ), ], ), body: snapshot.connectionState == ConnectionState.waiting ? const Center(child: CircularProgressIndicator()) : Column( children: [ Padding( padding: const EdgeInsets.fromLTRB(16, 8, 16, 0), child: TextField( controller: _titleController, decoration: const InputDecoration( hintText: 'Title', border: InputBorder.none, ), style: Theme.of(context).textTheme.titleLarge, textInputAction: TextInputAction.next, ), ), const Divider(), Expanded( child: _preview ? Markdown(data: _contentController.text) : Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: TextField( controller: _contentController, decoration: const InputDecoration( hintText: 'Write in markdown...', border: InputBorder.none, ), maxLines: null, expands: true, keyboardType: TextInputType.multiline, textAlignVertical: TextAlignVertical.top, onChanged: (_) => setState(() {}), ), ), ), ], ), ); }, ); } }