diff --git a/lib/editors/checklist_editor.dart b/lib/editors/checklist_editor.dart index 2da70018..64fa1df4 100644 --- a/lib/editors/checklist_editor.dart +++ b/lib/editors/checklist_editor.dart @@ -10,6 +10,7 @@ import 'package:gitjournal/editors/note_title_editor.dart'; class ChecklistEditor extends StatefulWidget implements Editor { final Note note; + final bool noteModified; @override final NoteCallback noteDeletionSelected; @@ -29,6 +30,7 @@ class ChecklistEditor extends StatefulWidget implements Editor { ChecklistEditor({ Key key, @required this.note, + @required this.noteModified, @required this.noteDeletionSelected, @required this.noteEditorChooserSelected, @required this.exitEditorSelected, @@ -49,6 +51,7 @@ class ChecklistEditorState extends State Checklist checklist; var focusNodes = {}; TextEditingController _titleTextController = TextEditingController(); + bool _noteModified; ChecklistEditorState(Note note) { _titleTextController = TextEditingController(text: note.title); @@ -58,6 +61,8 @@ class ChecklistEditorState extends State @override void initState() { super.initState(); + _noteModified = widget.noteModified; + if (checklist.items.isEmpty) { var item = checklist.buildItem(false, ""); checklist.addItem(item); @@ -85,6 +90,7 @@ class ChecklistEditorState extends State itemTiles.add(AddItemButton( key: UniqueKey(), onPressed: () { + _noteTextChanged(); setState(() { var fn = FocusScopeNode(); var item = checklist.buildItem(false, ""); @@ -102,6 +108,7 @@ class ChecklistEditorState extends State Widget checklistWidget = ReorderableListView( children: itemTiles, onReorder: (int oldIndex, int newIndex) { + _noteTextChanged(); setState(() { var item = checklist.removeAt(oldIndex); @@ -116,11 +123,11 @@ class ChecklistEditorState extends State var titleEditor = Padding( padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0.0), - child: NoteTitleEditor(_titleTextController), + child: NoteTitleEditor(_titleTextController, _noteTextChanged), ); return Scaffold( - appBar: buildEditorAppBar(widget, this), + appBar: buildEditorAppBar(widget, this, noteModified: _noteModified), body: Column( children: [ if (widget.note.canHaveMetadata) titleEditor, @@ -138,6 +145,13 @@ class ChecklistEditorState extends State return note; } + void _noteTextChanged() { + if (_noteModified) return; + setState(() { + _noteModified = true; + }); + } + ChecklistItemTile _buildTile(ChecklistItem item, int index, bool autofocus) { return ChecklistItemTile( key: UniqueKey(), @@ -148,11 +162,14 @@ class ChecklistEditorState extends State setState(() { item.checked = newVal; }); + _noteTextChanged(); }, textChanged: (String newVal) { item.text = newVal; + _noteTextChanged(); }, itemRemoved: () { + _noteTextChanged(); setState(() { // Give next item the focus var nextIndex = index + 1; @@ -180,6 +197,7 @@ class ChecklistEditorState extends State }); }, itemFinished: () { + _noteTextChanged(); setState(() { var fn = FocusScopeNode(); var item = checklist.buildItem(false, ""); diff --git a/lib/editors/common.dart b/lib/editors/common.dart index 45829588..69f9ea9e 100644 --- a/lib/editors/common.dart +++ b/lib/editors/common.dart @@ -21,12 +21,13 @@ enum DropDownChoices { Rename, MoveToFolder, DiscardChanges } AppBar buildEditorAppBar( Editor editor, EditorState editorState, { + @required bool noteModified, List extraButtons, }) { return AppBar( leading: IconButton( key: const ValueKey("NewEntry"), - icon: const Icon(Icons.check), + icon: Icon(noteModified ? Icons.check : Icons.close), onPressed: () { editor.exitEditorSelected(editorState.getNote()); }, diff --git a/lib/editors/journal_editor.dart b/lib/editors/journal_editor.dart index 27575307..1abfcb7e 100644 --- a/lib/editors/journal_editor.dart +++ b/lib/editors/journal_editor.dart @@ -6,6 +6,7 @@ import 'package:gitjournal/widgets/journal_editor_header.dart'; class JournalEditor extends StatefulWidget implements Editor { final Note note; + final bool noteModified; @override final NoteCallback noteDeletionSelected; @@ -25,6 +26,7 @@ class JournalEditor extends StatefulWidget implements Editor { JournalEditor({ Key key, @required this.note, + @required this.noteModified, @required this.noteDeletionSelected, @required this.noteEditorChooserSelected, @required this.exitEditorSelected, @@ -43,11 +45,18 @@ class JournalEditor extends StatefulWidget implements Editor { class JournalEditorState extends State implements EditorState { Note note; TextEditingController _textController = TextEditingController(); + bool _noteModified; JournalEditorState(this.note) { _textController = TextEditingController(text: note.body); } + @override + void initState() { + super.initState(); + _noteModified = widget.noteModified; + } + @override void dispose() { _textController.dispose(); @@ -63,8 +72,9 @@ class JournalEditorState extends State implements EditorState { children: [ JournalEditorHeader(note), _NoteBodyEditor( - _textController, + textController: _textController, autofocus: widget.isNewNote, + onChanged: _noteTextChanged, ), ], ), @@ -72,7 +82,7 @@ class JournalEditorState extends State implements EditorState { ); return Scaffold( - appBar: buildEditorAppBar(widget, this), + appBar: buildEditorAppBar(widget, this, noteModified: _noteModified), body: editor, ); } @@ -83,13 +93,21 @@ class JournalEditorState extends State implements EditorState { note.type = NoteType.Journal; return note; } + + void _noteTextChanged() { + if (_noteModified) return; + setState(() { + _noteModified = true; + }); + } } class _NoteBodyEditor extends StatelessWidget { final TextEditingController textController; final bool autofocus; + final Function onChanged; - _NoteBodyEditor(this.textController, {this.autofocus = false}); + _NoteBodyEditor({this.textController, this.autofocus, this.onChanged}); @override Widget build(BuildContext context) { @@ -108,6 +126,7 @@ class _NoteBodyEditor extends StatelessWidget { controller: textController, textCapitalization: TextCapitalization.sentences, scrollPadding: const EdgeInsets.all(0.0), + onChanged: (_) => onChanged(), ); } } diff --git a/lib/editors/markdown_editor.dart b/lib/editors/markdown_editor.dart index b9d03d8e..94fd7718 100644 --- a/lib/editors/markdown_editor.dart +++ b/lib/editors/markdown_editor.dart @@ -8,6 +8,7 @@ import 'package:gitjournal/widgets/note_viewer.dart'; class MarkdownEditor extends StatefulWidget implements Editor { final Note note; + final bool noteModified; @override final NoteCallback noteDeletionSelected; @@ -27,6 +28,7 @@ class MarkdownEditor extends StatefulWidget implements Editor { MarkdownEditor({ Key key, @required this.note, + @required this.noteModified, @required this.noteDeletionSelected, @required this.noteEditorChooserSelected, @required this.exitEditorSelected, @@ -48,6 +50,7 @@ class MarkdownEditorState extends State implements EditorState { TextEditingController _titleTextController = TextEditingController(); bool editingMode = true; + bool _noteModified; MarkdownEditorState(this.note) { _textController = TextEditingController(text: note.body); @@ -60,6 +63,7 @@ class MarkdownEditorState extends State implements EditorState { @override void initState() { super.initState(); + _noteModified = widget.noteModified; if (widget.isNewNote) { editingMode = true; } @@ -79,10 +83,15 @@ class MarkdownEditorState extends State implements EditorState { child: SingleChildScrollView( child: Column( children: [ - if (note.canHaveMetadata) NoteTitleEditor(_titleTextController), + if (note.canHaveMetadata) + NoteTitleEditor( + _titleTextController, + _noteTextChanged, + ), _NoteBodyEditor( - _textController, + textController: _textController, autofocus: widget.isNewNote, + onChanged: _noteTextChanged, ), ], ), @@ -99,7 +108,12 @@ class MarkdownEditorState extends State implements EditorState { ); return Scaffold( - appBar: buildEditorAppBar(widget, this, extraButtons: [extraButton]), + appBar: buildEditorAppBar( + widget, + this, + noteModified: _noteModified, + extraButtons: [extraButton], + ), body: body, ); } @@ -122,13 +136,25 @@ class MarkdownEditorState extends State implements EditorState { _updateNote(); return note; } + + void _noteTextChanged() { + if (_noteModified) return; + setState(() { + _noteModified = true; + }); + } } class _NoteBodyEditor extends StatelessWidget { final TextEditingController textController; final bool autofocus; + final Function onChanged; - _NoteBodyEditor(this.textController, {this.autofocus = false}); + _NoteBodyEditor({ + @required this.textController, + @required this.autofocus, + @required this.onChanged, + }); @override Widget build(BuildContext context) { @@ -147,6 +173,7 @@ class _NoteBodyEditor extends StatelessWidget { controller: textController, textCapitalization: TextCapitalization.sentences, scrollPadding: const EdgeInsets.all(0.0), + onChanged: (_) => onChanged(), ); } } diff --git a/lib/editors/note_title_editor.dart b/lib/editors/note_title_editor.dart index c95ac933..06aeaa6b 100644 --- a/lib/editors/note_title_editor.dart +++ b/lib/editors/note_title_editor.dart @@ -2,8 +2,9 @@ import 'package:flutter/material.dart'; class NoteTitleEditor extends StatelessWidget { final TextEditingController textController; + final Function onChanged; - NoteTitleEditor(this.textController); + NoteTitleEditor(this.textController, this.onChanged); @override Widget build(BuildContext context) { @@ -20,6 +21,7 @@ class NoteTitleEditor extends StatelessWidget { controller: textController, textCapitalization: TextCapitalization.sentences, maxLines: null, + onChanged: (_) => onChanged(), ); } } diff --git a/lib/editors/raw_editor.dart b/lib/editors/raw_editor.dart index 2a6444d5..723c022e 100644 --- a/lib/editors/raw_editor.dart +++ b/lib/editors/raw_editor.dart @@ -6,6 +6,7 @@ import 'package:gitjournal/editors/common.dart'; class RawEditor extends StatefulWidget implements Editor { final Note note; + final bool noteModified; @override final NoteCallback noteDeletionSelected; @@ -25,6 +26,7 @@ class RawEditor extends StatefulWidget implements Editor { RawEditor({ Key key, @required this.note, + @required this.noteModified, @required this.noteDeletionSelected, @required this.noteEditorChooserSelected, @required this.exitEditorSelected, @@ -42,6 +44,7 @@ class RawEditor extends StatefulWidget implements Editor { class RawEditorState extends State implements EditorState { Note note; + bool _noteModified; TextEditingController _textController = TextEditingController(); final serializer = MarkdownYAMLCodec(); @@ -50,6 +53,12 @@ class RawEditorState extends State implements EditorState { _textController = TextEditingController(text: serializer.encode(note.data)); } + @override + void initState() { + super.initState(); + _noteModified = widget.noteModified; + } + @override void dispose() { _textController.dispose(); @@ -62,14 +71,15 @@ class RawEditorState extends State implements EditorState { padding: const EdgeInsets.all(16.0), child: SingleChildScrollView( child: _NoteEditor( - _textController, + textController: _textController, autofocus: widget.isNewNote, + onChanged: _noteTextChanged, ), ), ); return Scaffold( - appBar: buildEditorAppBar(widget, this), + appBar: buildEditorAppBar(widget, this, noteModified: _noteModified), body: editor, ); } @@ -79,13 +89,21 @@ class RawEditorState extends State implements EditorState { note.data = serializer.decode(_textController.text); return note; } + + void _noteTextChanged() { + if (_noteModified) return; + setState(() { + _noteModified = true; + }); + } } class _NoteEditor extends StatelessWidget { final TextEditingController textController; final bool autofocus; + final Function onChanged; - _NoteEditor(this.textController, {this.autofocus = false}); + _NoteEditor({this.textController, this.autofocus, this.onChanged}); @override Widget build(BuildContext context) { @@ -105,6 +123,7 @@ class _NoteEditor extends StatelessWidget { controller: textController, textCapitalization: TextCapitalization.sentences, scrollPadding: const EdgeInsets.all(0.0), + onChanged: (_) => onChanged(), ); } } diff --git a/lib/screens/note_editor.dart b/lib/screens/note_editor.dart index c3956cfa..835a6979 100644 --- a/lib/screens/note_editor.dart +++ b/lib/screens/note_editor.dart @@ -96,6 +96,7 @@ class NoteEditorState extends State { return MarkdownEditor( key: _markdownEditorKey, note: note, + noteModified: _noteModified(note), noteDeletionSelected: _noteDeletionSelected, noteEditorChooserSelected: _noteEditorChooserSelected, exitEditorSelected: _exitEditorSelected, @@ -108,6 +109,7 @@ class NoteEditorState extends State { return RawEditor( key: _rawEditorKey, note: note, + noteModified: _noteModified(note), noteDeletionSelected: _noteDeletionSelected, noteEditorChooserSelected: _noteEditorChooserSelected, exitEditorSelected: _exitEditorSelected, @@ -120,6 +122,7 @@ class NoteEditorState extends State { return ChecklistEditor( key: _checklistEditorKey, note: note, + noteModified: _noteModified(note), noteDeletionSelected: _noteDeletionSelected, noteEditorChooserSelected: _noteEditorChooserSelected, exitEditorSelected: _exitEditorSelected, @@ -132,6 +135,7 @@ class NoteEditorState extends State { return JournalEditor( key: _journalEditorKey, note: note, + noteModified: _noteModified(note), noteDeletionSelected: _noteDeletionSelected, noteEditorChooserSelected: _noteEditorChooserSelected, exitEditorSelected: _exitEditorSelected,