diff --git a/lib/screens/home/components/MyListTile.dart b/lib/screens/home/components/MyListTile.dart deleted file mode 100644 index 2cc4712..0000000 --- a/lib/screens/home/components/MyListTile.dart +++ /dev/null @@ -1,196 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; -import 'package:notes_app/components/storage.dart'; - -import 'package:path/path.dart'; -import 'dart:io'; - -class MyListTile extends StatefulWidget { - MyListTile( - {Key? key, - required this.file, - required this.storage, - required this.updateFunction, - required this.deleteFunction, - required this.toggleFunction, - required this.selectFunction, - required this.selectMode, - required this.isChecked}) - : super(key: key); - final File file; - final CounterStorage storage; - final Function updateFunction; - final Function toggleFunction; - final Function deleteFunction; - final Function selectFunction; - final bool selectMode; - bool isChecked; - - @override - MyListTileState createState() => MyListTileState(); -} - -class MyListTileState extends State { - final _biggerFont = const TextStyle(fontSize: 18); - late TextEditingController _controller; - - @override - void initState() { - super.initState(); - _controller = TextEditingController(); - } - - @override - Widget build(BuildContext context) { - // print("${widget.isChecked} / ${widget.file}"); - - return widget.selectMode - ? CheckboxListTile( - title: Text(basename(widget.file.path)), - checkColor: Colors.white, - value: widget.isChecked, - onChanged: (bool? value) { - setState(() { - widget.toggleFunction(widget.file, widget.isChecked); - }); - }) - : ListTile( - title: Text( - basename(widget.file.path), - style: _biggerFont, - ), - onTap: () => - widget.storage.openNote(context, basename(widget.file.path)), - onLongPress: () { - widget.toggleFunction(widget.file, true); - widget.selectFunction(); - }, - // onTap: _handleNote, - subtitle: _subtitle(widget.file), - trailing: btnListTileMore(context, widget.file), - ); - } - - DropdownButton btnListTileMore(BuildContext context, File f) { - return DropdownButton( - icon: const Icon(Icons.more_vert_outlined), - onChanged: (String? newValue) { - switch (newValue) { - case "Delete": - _deleteDialog(context, f); - break; - case "Rename": - _changeTitleDialog(context, f.path); - break; - } - setState(() {}); - }, - underline: Container(color: Colors.transparent), - items: ['Rename', 'Delete'] - .map>((String value) { - return DropdownMenuItem( - value: value, - child: Text(value), - ); - }).toList(), - ); - } - - /* DIALOGS */ - void _changeTitleDialog(BuildContext context, String path) { - // debugdebugPrint("Clicked title"); - showDialog( - context: context, - builder: (BuildContext ctx) { - return AlertDialog( - title: Text('Rename note:'), - content: TextField( - controller: _controller, - maxLines: 1, - decoration: InputDecoration( - hintText: basename(path), - ), - ), - actions: [ - TextButton( - onPressed: () { - // Return to previous screen - Navigator.pop(context); - _controller.text = ""; - widget.updateFunction(); - - // Remove the box - }, - child: Text('No')), - TextButton( - onPressed: () { - if (_controller.text != "") { - Navigator.pop(context); - widget.toggleFunction(widget.file, false); - widget.storage.renameNote( - basename(widget.file.path), _controller.text); - _controller.text = ""; - } - }, - child: Text('Yes')), - ], - ); - }); - } - - void _deleteDialog(BuildContext context, File f) { - // debugdebugPrint("Clicked title"); - showDialog( - context: context, - builder: (BuildContext ctx) { - return AlertDialog( - title: Text('Delete note'), - content: Text( - "Are you sure you want to delete this note? This action cannot be undone!"), - actions: [ - TextButton( - onPressed: () { - // Remove the box - // Return to previous screen - Navigator.pop(context); - setState(() {}); - widget.updateFunction(); - }, - child: Text('Cancel')), - TextButton( - onPressed: () { - Navigator.pop(context); - //Return to previous screen - setState(() {}); - widget.deleteFunction(widget.file); - }, - child: Text('Ok')), - ], - ); - }); - } - - Widget _subtitle(File f) { - try { - return Text(_lastEdited(f.lastModifiedSync())); - } catch (e) { - return Text(""); - } - } - - String _lastEdited(DateTime now) { - return "Last edited: ${now.year.toString()}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')} at ${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}"; - } - - Color getColor(Set states) { - const Set interactiveStates = { - MaterialState.pressed, - MaterialState.hovered, - MaterialState.focused, - }; - if (states.any(interactiveStates.contains)) { - return Colors.blue; - } - return Colors.red; - } -} diff --git a/lib/screens/home/home.dart b/lib/screens/home/home.dart index bb24acb..69aa6fa 100644 --- a/lib/screens/home/home.dart +++ b/lib/screens/home/home.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -import 'package:notes_app/components/storage.dart'; -import 'package:notes_app/screens/home/components/MyListTile.dart'; -import 'package:notes_app/screens/settings/settings.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:notes_app/components/storage.dart'; +import 'package:notes_app/screens/settings/settings.dart'; +import 'package:path/path.dart'; import 'dart:io'; class MyHomePage extends StatefulWidget { @@ -18,12 +18,15 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { List _allFilesNames = []; List _selected = []; - + bool selectMode = false; String _sortBy = ""; + late TextEditingController _controller; + @override void initState() { super.initState(); + _controller = TextEditingController(); } void getSortBy() async { @@ -44,7 +47,7 @@ class _MyHomePageState extends State { centerTitle: true, toolbarHeight: 50, actions: selectMode - ? [btnAppBarMoreSelect()] + ? [btnAppBarMoreSelect(context)] : [btnAppBarMoreNoSelect(context)], ), body: FutureBuilder>( @@ -89,7 +92,46 @@ class _MyHomePageState extends State { }); } - bool selectMode = false; + Widget _buildList(BuildContext context) { + sortFiles(); + final tiles = _allFilesNames.map( + ( + File file, + ) { + return selectMode + ? CheckboxListTile( + title: Text(basename(file.path)), + checkColor: Colors.white, + value: _selected.contains(file.path), + onChanged: (bool? value) { + toggleFile(file); + }) + : ListTile( + title: Text( + basename(file.path), + style: TextStyle(fontSize: 18), + ), + onTap: () => + widget.storage.openNote(context, basename(file.path)), + onLongPress: () { + _selected.add(file.path); + enterSelectMode(); + }, + subtitle: _subtitle(file), + trailing: btnListTileMore(context, file), + ); + }, + ); + final divided = tiles.isNotEmpty + ? ListTile.divideTiles(context: context, tiles: tiles).toList() + : []; + return ListView( + padding: const EdgeInsets.all(8), + children: divided, + ); + } + + /* --------------------------------------- UTILITY FUNCTIONS ------------------------------------------- */ void sortFiles() { if (_sortBy == "Name") { @@ -103,30 +145,16 @@ class _MyHomePageState extends State { } } - Widget _buildList(BuildContext context) { - sortFiles(); - final tiles = _allFilesNames.map( - ( - File f, - ) { - return new MyListTile( - file: f, - selectMode: selectMode, - storage: widget.storage, - selectFunction: enterSelectMode, - updateFunction: updateChildren, - toggleFunction: toggleFile, - deleteFunction: _deleteFile, - isChecked: _selected.contains(f.path)); - }, - ); - final divided = tiles.isNotEmpty - ? ListTile.divideTiles(context: context, tiles: tiles).toList() - : []; - return ListView( - padding: const EdgeInsets.all(8), - children: divided, - ); + Widget _subtitle(File f) { + try { + return Text(_lastEdited(f.lastModifiedSync())); + } catch (e) { + return Text(""); + } + } + + String _lastEdited(DateTime now) { + return "Last edited: ${now.year.toString()}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')} at ${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}"; } void enterSelectMode() { @@ -142,16 +170,11 @@ class _MyHomePageState extends State { }); } - void updateChildren() { - setState(() {}); - } - - void toggleFile(File f, bool b) { - if (_selected.contains(f.path)) - _selected.remove(f.path); + void toggleFile(File file) { + if (_selected.contains(file.path)) + _selected.remove(file.path); else - _selected.add(f.path); - setState(() {}); + _selected.add(file.path); } void _deleteFile(File f) { @@ -164,16 +187,17 @@ class _MyHomePageState extends State { exitSelectMode(); } - /* DROP DOWN MENUS*/ - DropdownButton btnAppBarMoreSelect() { + /* --------------------------------------- DROP DOWN MENUS ------------------------------------------- */ + /* APPBAR MORE (SELECT)*/ + DropdownButton btnAppBarMoreSelect(BuildContext context) { return DropdownButton( icon: const Icon(Icons.more_vert), onChanged: (String? newValue) { switch (newValue) { - case "Delete": - _deleteDialog(context); + case "Delete Selected": + _deleteAllDialog(context); break; - case "Cancel": + case "Exit Select Mode": exitSelectMode(); break; case "Select All": @@ -202,11 +226,11 @@ class _MyHomePageState extends State { }, underline: Container(color: Colors.transparent), items: [ - "Delete", + "Delete Selected", "Select All", "Deselect All", "Toggle Selected", - "Cancel", + "Exit Select Mode", ].map>((String value) { return DropdownMenuItem( value: value, @@ -215,7 +239,9 @@ class _MyHomePageState extends State { }).toList(), ); } + /* --------------------- */ + /* APPBAR MORE (NO SELECT)*/ DropdownButton btnAppBarMoreNoSelect(BuildContext context) { return DropdownButton( icon: const Icon(Icons.more_vert), @@ -245,7 +271,76 @@ class _MyHomePageState extends State { ); } - void _deleteDialog(BuildContext context) { + /* TILE MORE */ + DropdownButton btnListTileMore(BuildContext context, File f) { + return DropdownButton( + icon: const Icon(Icons.more_vert_outlined), + onChanged: (String? newValue) { + switch (newValue) { + case "Delete": + _deleteSingleDialog(context, f); + break; + case "Rename": + _changeTitleDialog(context, f.path); + break; + } + setState(() {}); + }, + underline: Container(color: Colors.transparent), + items: ['Rename', 'Delete'] + .map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + ); + } + /* --------------------- */ + + /* --------------------------------------- DIALOGS ------------------------------------------- */ + /* RENAME DIALOG */ + void _changeTitleDialog(BuildContext context, String path) { + // debugdebugPrint("Clicked title"); + showDialog( + context: context, + builder: (BuildContext ctx) { + return AlertDialog( + title: Text('Rename note:'), + content: TextField( + controller: _controller, + maxLines: 1, + decoration: InputDecoration( + hintText: basename(path), + ), + ), + actions: [ + TextButton( + onPressed: () { + // Return to previous screen + Navigator.pop(context); + _controller.text = ""; + }, + child: Text('No')), + TextButton( + onPressed: () { + if (_controller.text != "") { + Navigator.pop(context); + _selected.remove(path); + widget.storage + .renameNote(basename(path), _controller.text); + _controller.text = ""; + } + }, + child: Text('Yes')), + ], + ); + }); + } + /* --------------------- */ + + /* DELETE MULTIPLE DIALOG */ + void _deleteAllDialog(BuildContext context) { // debugdebugPrint("Clicked title"); showDialog( context: context, @@ -261,7 +356,6 @@ class _MyHomePageState extends State { // Return to previous screen Navigator.pop(context); setState(() {}); - updateChildren(); }, child: Text('Cancel')), TextButton( @@ -280,4 +374,37 @@ class _MyHomePageState extends State { ); }); } + /* --------------------- */ + + /* DELETE SINGLE DIALOG */ + void _deleteSingleDialog(BuildContext context, File f) { + showDialog( + context: context, + builder: (BuildContext ctx) { + return AlertDialog( + title: Text('Delete note'), + content: Text( + "Are you sure you want to delete this note? This action cannot be undone!"), + actions: [ + TextButton( + onPressed: () { + // Remove the box + // Return to previous screen + Navigator.pop(context); + setState(() {}); + }, + child: Text('Cancel')), + TextButton( + onPressed: () { + Navigator.pop(context); + //Return to previous screen + setState(() {}); + _deleteFile(f); + }, + child: Text('Ok')), + ], + ); + }); + } + /* --------------------- */ } diff --git a/lib/screens/note/note.dart b/lib/screens/note/note.dart index 1a6de72..16e8901 100644 --- a/lib/screens/note/note.dart +++ b/lib/screens/note/note.dart @@ -42,6 +42,12 @@ class _NewNoteState extends State { } } + @override + void dispose() { + _controllerNote.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return new WillPopScope( @@ -82,9 +88,11 @@ class _NewNoteState extends State { }); } + /* --------------------------------------- DIALOGS ------------------------------------------- */ + /* RENAME DIALOG */ void _changeTitleDialog(BuildContext context) { // debugdebugPrint("Clicked title"); - showDialog ( + showDialog( context: context, builder: (BuildContext ctx) { return AlertDialog( @@ -111,10 +119,8 @@ class _NewNoteState extends State { setState(() { Navigator.pop(context); //Note: code repetition - if(_title != _controller.text){ - _changed = false; - widget.storage.saveRename(_title, _controllerNote.text, _controller.text); - _title = _controller.text; + if (_title != _controller.text) { + _save(rename: true, newname: _controller.text); _controller.text = ""; } @@ -126,7 +132,9 @@ class _NewNoteState extends State { ); }); } + /* --------------------- */ + /* DELETE DIALOG */ void _deleteDialog(BuildContext context) { if (_changed) showDialog( @@ -167,7 +175,9 @@ class _NewNoteState extends State { else _toPrevious(); } + /* --------------------- */ + /* --------------------------------------- UTILITY FUNCTIONS ------------------------------------------- */ void _toPrevious() { Navigator.pushReplacement( context, @@ -175,22 +185,20 @@ class _NewNoteState extends State { builder: (context) => MyHomePage(storage: widget.storage))); } - void _save() { - _changed = false; - - widget.storage.saveNote(_title, _controllerNote.text); - - setState(() {}); + void _save({bool rename = false, String newname = ""}) { + setState(() { + _changed = false; + if (rename) { + widget.storage.saveRename(_title, _controllerNote.text, newname); + _title = newname; + } else + widget.storage.saveNote(_title, _controllerNote.text); + }); } void _onChange(String s) { - _changed = true; - setState(() {}); - } - - @override - void dispose() { - _controllerNote.dispose(); - super.dispose(); + setState(() { + _changed = true; + }); } } diff --git a/lib/screens/settings/settings.dart b/lib/screens/settings/settings.dart index 870c021..293533c 100644 --- a/lib/screens/settings/settings.dart +++ b/lib/screens/settings/settings.dart @@ -13,6 +13,9 @@ class Settings extends StatefulWidget { } class _SettingsState extends State { + String _theme = ""; + String _sortBy = ""; + @override void initState() { getTheme(); @@ -21,31 +24,9 @@ class _SettingsState extends State { super.initState(); } - String _theme = ""; - String _sortBy = ""; - - void getTheme() async { - final savedThemeMode = await AdaptiveTheme.getThemeMode(); - setState(() { - switch (savedThemeMode) { - case AdaptiveThemeMode.dark: - _theme = "Dark"; - break; - case AdaptiveThemeMode.light: - _theme = "Light"; - break; - default: - _theme = "Follow System"; - break; - } - }); - } - - void getSortBy() async { - final prefs = await SharedPreferences.getInstance(); - setState(() { - _sortBy = prefs.getString("sortBy") ?? "Name"; - }); + @override + void dispose() { + super.dispose(); } @override @@ -97,6 +78,7 @@ class _SettingsState extends State { ); } + /* --------------------------------------- UTLITY FUNCTIONS ------------------------------------------- */ void _toPrevious() { Navigator.pushReplacement( context, @@ -104,37 +86,6 @@ class _SettingsState extends State { builder: (context) => MyHomePage(storage: widget.storage))); } - void _sortByDialog(BuildContext context) { - showDialog( - context: context, - builder: (BuildContext ctx) { - return SimpleDialog( - title: const Text('Choose theme'), - children: [ - _radioListTile("Name", "Name", _sortBy, _setSortBy), - _radioListTile( - "Last Modified", "Last Modified", _sortBy, _setSortBy), - ], - ); - }); - } - - void _themeDialog(BuildContext context) { - showDialog( - context: context, - builder: (BuildContext ctx) { - return SimpleDialog( - title: const Text('Choose theme'), - children: [ - _radioListTile( - "Follow System", "Follow System", _theme, _setTheme), - _radioListTile("Dark", "Dark", _theme, _setTheme), - _radioListTile("Light", "Light", _theme, _setTheme), - ], - ); - }); - } - RadioListTile _radioListTile( String title, String value, String groupValue, Function(dynamic) f) { return new RadioListTile( @@ -146,6 +97,32 @@ class _SettingsState extends State { }); } + /* --------------------------------------- GETTERS ------------------------------------------- */ + void getTheme() async { + final savedThemeMode = await AdaptiveTheme.getThemeMode(); + setState(() { + switch (savedThemeMode) { + case AdaptiveThemeMode.dark: + _theme = "Dark"; + break; + case AdaptiveThemeMode.light: + _theme = "Light"; + break; + default: + _theme = "Follow System"; + break; + } + }); + } + + void getSortBy() async { + final prefs = await SharedPreferences.getInstance(); + setState(() { + _sortBy = prefs.getString("sortBy") ?? "Name"; + }); + } + + /* --------------------------------------- SETTERS ------------------------------------------- */ void _setTheme(dynamic newTheme) { // final prefs = await SharedPreferences.getInstance(); @@ -179,6 +156,43 @@ class _SettingsState extends State { }); } + /* --------------------------------------- DIALOGS ------------------------------------------- */ + /* SORT BY DIALOG */ + void _sortByDialog(BuildContext context) { + showDialog( + context: context, + builder: (BuildContext ctx) { + return SimpleDialog( + title: const Text('Choose theme'), + children: [ + _radioListTile("Name", "Name", _sortBy, _setSortBy), + _radioListTile( + "Last Modified", "Last Modified", _sortBy, _setSortBy), + ], + ); + }); + } + /* --------------------- */ + + /* THEME DIALOG */ + void _themeDialog(BuildContext context) { + showDialog( + context: context, + builder: (BuildContext ctx) { + return SimpleDialog( + title: const Text('Choose theme'), + children: [ + _radioListTile( + "Follow System", "Follow System", _theme, _setTheme), + _radioListTile("Dark", "Dark", _theme, _setTheme), + _radioListTile("Light", "Light", _theme, _setTheme), + ], + ); + }); + } + /* --------------------- */ + + /* ABOUT DIALOG */ void _aboutDialog(BuildContext context) { showDialog( context: context, @@ -191,9 +205,4 @@ class _SettingsState extends State { ); }); } - - @override - void dispose() { - super.dispose(); - } } diff --git a/pubspec.lock b/pubspec.lock index dc7de1f..5c026cb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -14,7 +14,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.6.1" + version: "2.8.1" boolean_selector: dependency: transitive description: @@ -35,7 +35,7 @@ packages: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" clock: dependency: transitive description: @@ -113,7 +113,7 @@ packages: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.7.0" path: dependency: transitive description: @@ -265,7 +265,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" + version: "0.4.2" typed_data: dependency: transitive description: