From db13702e54ba39f8f4e7c723e24562a66aef1fd5 Mon Sep 17 00:00:00 2001 From: randogoth Date: Fri, 19 Jun 2026 17:50:56 +0300 Subject: [PATCH] add long-press gesture to open note scrolled to end Long-pressing a note in the list opens it in view mode with the scroll position at the bottom of the file. Switching to edit mode the first time positions the cursor at the end of the text and scrolls there too, so the user can immediately append content. Implementation: - NoteEditorScreen gains a scrollToEnd parameter (default false) - A ScrollController is attached to the view/edit SingleChildScrollView - After content loads with scrollToEnd=true, two post-frame callbacks fire (first renders the content, second has a stable maxScrollExtent) then jumpTo(maxScrollExtent) - _cursorAtEnd flag is consumed on the first Mode Edit switch: sets selection to text.length, requests focus, and scrolls to bottom - NoteListScreen ListTile gets onLongPress that navigates with scrollToEnd: true and reloads on return Co-Authored-By: Claude Sonnet 4.6 --- lib/note_editor.dart | 30 ++++++++++++++++++++++++++++-- lib/note_list.dart | 14 ++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/lib/note_editor.dart b/lib/note_editor.dart index 5a50118..77c32f5 100755 --- a/lib/note_editor.dart +++ b/lib/note_editor.dart @@ -14,10 +14,10 @@ import 'codefield.dart'; class NoteEditorScreen extends StatefulWidget { final File note; - final String? notesDirectoryPath; // ✅ Add this + final String? notesDirectoryPath; final bool isDarkMode; - final NoteMode initialMode; + final bool scrollToEnd; const NoteEditorScreen({ super.key, @@ -25,6 +25,7 @@ class NoteEditorScreen extends StatefulWidget { required this.isDarkMode, required this.notesDirectoryPath, this.initialMode = NoteMode.view, + this.scrollToEnd = false, }); @override @@ -38,14 +39,17 @@ class _NoteEditorScreenState extends State { late String _initialContent; late NoteMode _mode; final FocusNode _editorFocusNode = FocusNode(); + final ScrollController _scrollController = ScrollController(); bool _isLoading = true; bool _autoSaveEnabled = false; double _fontSize = 16.0; + bool _cursorAtEnd = false; @override void initState() { super.initState(); _mode = widget.initialMode; + _cursorAtEnd = widget.scrollToEnd; _loadPreferences(); _loadNoteContent(); } @@ -88,6 +92,17 @@ class _NoteEditorScreenState extends State { TextSelection.collapsed(offset: _controller.text.length); _editorFocusNode.requestFocus(); }); + } else if (widget.scrollToEnd) { + // Two frames: first renders the content, second has a stable maxScrollExtent. + WidgetsBinding.instance.addPostFrameCallback((_) { + WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToBottom()); + }); + } + } + + void _scrollToBottom() { + if (_scrollController.hasClients) { + _scrollController.jumpTo(_scrollController.position.maxScrollExtent); } } @@ -160,6 +175,7 @@ class _NoteEditorScreenState extends State { _controller.removeListener(_onTextChanged); _controller.dispose(); _editorFocusNode.dispose(); + _scrollController.dispose(); super.dispose(); } @@ -208,6 +224,15 @@ class _NoteEditorScreenState extends State { break; case 'Mode Edit': setState(() { _mode = NoteMode.edit; }); + if (_cursorAtEnd) { + _cursorAtEnd = false; // consume once + WidgetsBinding.instance.addPostFrameCallback((_) { + _controller.selection = TextSelection.collapsed( + offset: _controller.text.length); + _editorFocusNode.requestFocus(); + _scrollToBottom(); + }); + } break; case 'Mode Preview': setState(() { _mode = NoteMode.preview; }); @@ -362,6 +387,7 @@ class _NoteEditorScreenState extends State { data: CodeThemeData( styles: widget.isDarkMode ? nightTheme : dayTheme), child: SingleChildScrollView( + controller: _scrollController, child: WrappedCodeField( wrap: true, controller: _controller, diff --git a/lib/note_list.dart b/lib/note_list.dart index 3efcd59..5b15451 100755 --- a/lib/note_list.dart +++ b/lib/note_list.dart @@ -943,6 +943,20 @@ Happy note taking! ✨ setState(() {}); // ✅ Force UI refresh } }, + onLongPress: () async { + await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => NoteEditorScreen( + note: noteFile, + isDarkMode: widget.isDarkMode, + notesDirectoryPath: notesDirectoryPath, + scrollToEnd: true, + ), + ), + ); + if (context.mounted) _loadNotes(); + }, ), ); },