Enhance navigation and structure: integrate SettingsScreen into main app flow, update HomeScreen to open settings, and improve UI elements for better user experience.
This commit is contained in:
+176
-194
@@ -6,27 +6,19 @@ import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||
import 'package:notas/data/note_repository.dart';
|
||||
import 'package:notas/models/note.dart';
|
||||
import 'package:notas/screens/note_editor_screen.dart';
|
||||
import 'package:notas/widgets/app_title_bar.dart';
|
||||
import 'package:notas/widgets/menu_drawer.dart';
|
||||
import 'package:notas/screens/settings_screen.dart';
|
||||
import 'package:notas/widgets/note_card.dart';
|
||||
import 'package:notas/widgets/search_app_bar.dart';
|
||||
|
||||
// HomeScreen: main entry showing notes in a responsive masonry grid.
|
||||
// Key behaviors implemented here:
|
||||
// - Load/save notes via `NoteRepository` (SQLite through Drift)
|
||||
// - Open `NoteEditorScreen` for create/edit
|
||||
// - Drag & drop reordering (updates `index` in the database)
|
||||
|
||||
class HomeScreen extends StatefulWidget {
|
||||
const HomeScreen({
|
||||
super.key,
|
||||
required this.repository,
|
||||
required this.onDeleteAllData,
|
||||
required this.onOpenSettings,
|
||||
});
|
||||
|
||||
final NoteRepository repository;
|
||||
final Future<void> Function() onDeleteAllData;
|
||||
final VoidCallback onOpenSettings;
|
||||
|
||||
@override
|
||||
State<HomeScreen> createState() => _HomeScreenState();
|
||||
@@ -174,130 +166,130 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
: _notes.isEmpty
|
||||
? const _EmptyState()
|
||||
: MouseRegion(
|
||||
cursor: _isDragging ? SystemMouseCursors.grabbing : SystemMouseCursors.basic,
|
||||
child: MasonryGridView.count(
|
||||
crossAxisCount: crossAxisCount,
|
||||
mainAxisSpacing: 10,
|
||||
crossAxisSpacing: 10,
|
||||
itemCount: _getFilteredNotes().length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final List<Note> filteredNotes = _getFilteredNotes();
|
||||
return DragTarget<int>(
|
||||
onAcceptWithDetails: (DragTargetDetails<int> details) {
|
||||
final Note targetNote = filteredNotes[index];
|
||||
final int originalTargetIndex = _notes.indexOf(targetNote);
|
||||
_reorderNote(details.data, originalTargetIndex);
|
||||
},
|
||||
builder: (context, candidateData, rejectedData) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final double cellWidth = constraints.maxWidth;
|
||||
cursor: _isDragging ? SystemMouseCursors.grabbing : SystemMouseCursors.basic,
|
||||
child: MasonryGridView.count(
|
||||
crossAxisCount: crossAxisCount,
|
||||
mainAxisSpacing: 10,
|
||||
crossAxisSpacing: 10,
|
||||
itemCount: _getFilteredNotes().length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final List<Note> filteredNotes = _getFilteredNotes();
|
||||
return DragTarget<int>(
|
||||
onAcceptWithDetails: (DragTargetDetails<int> details) {
|
||||
final Note targetNote = filteredNotes[index];
|
||||
final int originalTargetIndex = _notes.indexOf(targetNote);
|
||||
_reorderNote(details.data, originalTargetIndex);
|
||||
},
|
||||
builder: (context, candidateData, rejectedData) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final double cellWidth = constraints.maxWidth;
|
||||
|
||||
return Draggable<int>(
|
||||
data: _notes.indexOf(filteredNotes[index]),
|
||||
onDragStarted: () {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isDragging = true;
|
||||
});
|
||||
},
|
||||
onDragEnd: (_) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isDragging = false;
|
||||
});
|
||||
},
|
||||
onDraggableCanceled: (_, _) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isDragging = false;
|
||||
});
|
||||
},
|
||||
feedback: MouseRegion(
|
||||
cursor: SystemMouseCursors.grabbing,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
elevation: 8,
|
||||
child: SizedBox(
|
||||
width: cellWidth,
|
||||
child: TweenAnimationBuilder<double>(
|
||||
tween: Tween(begin: 0.97, end: 1.0),
|
||||
duration: const Duration(milliseconds: 180),
|
||||
curve: Curves.easeOutCubic,
|
||||
builder: (context, scale, child) {
|
||||
return Transform.scale(
|
||||
scale: scale,
|
||||
alignment: Alignment.topLeft,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: Opacity(
|
||||
opacity: 0.95,
|
||||
child: NoteCard(note: filteredNotes[index], onTap: () {}, isDragging: true),
|
||||
return Draggable<int>(
|
||||
data: _notes.indexOf(filteredNotes[index]),
|
||||
onDragStarted: () {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isDragging = true;
|
||||
});
|
||||
},
|
||||
onDragEnd: (_) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isDragging = false;
|
||||
});
|
||||
},
|
||||
onDraggableCanceled: (_, _) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_isDragging = false;
|
||||
});
|
||||
},
|
||||
feedback: MouseRegion(
|
||||
cursor: SystemMouseCursors.grabbing,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
elevation: 8,
|
||||
child: SizedBox(
|
||||
width: cellWidth,
|
||||
child: TweenAnimationBuilder<double>(
|
||||
tween: Tween(begin: 0.97, end: 1.0),
|
||||
duration: const Duration(milliseconds: 180),
|
||||
curve: Curves.easeOutCubic,
|
||||
builder: (context, scale, child) {
|
||||
return Transform.scale(
|
||||
scale: scale,
|
||||
alignment: Alignment.topLeft,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: Opacity(
|
||||
opacity: 0.95,
|
||||
child: NoteCard(note: filteredNotes[index], onTap: () {}, isDragging: true),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
childWhenDragging: MouseRegion(
|
||||
cursor: SystemMouseCursors.grabbing,
|
||||
child: Opacity(
|
||||
opacity: 0.3,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color.fromRGBO(24, 25, 26, 1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: Colors.white24, width: 1),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
filteredNotes[index].title,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
childWhenDragging: MouseRegion(
|
||||
cursor: SystemMouseCursors.grabbing,
|
||||
child: Opacity(
|
||||
opacity: 0.3,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color.fromRGBO(24, 25, 26, 1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: Colors.white24, width: 1),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
filteredNotes[index].title,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
filteredNotes[index].body,
|
||||
style: const TextStyle(color: Colors.white70, fontSize: 14),
|
||||
maxLines: 20,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
filteredNotes[index].body,
|
||||
style: const TextStyle(color: Colors.white70, fontSize: 14),
|
||||
maxLines: 20,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: candidateData.isNotEmpty
|
||||
? Border.all(color: Colors.blue.shade400, width: 2)
|
||||
: null,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: candidateData.isNotEmpty
|
||||
? Border.all(color: Colors.blue.shade400, width: 2)
|
||||
: null,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: NoteCard(
|
||||
key: ValueKey<int>(filteredNotes[index].id ?? filteredNotes[index].index),
|
||||
note: filteredNotes[index],
|
||||
onTap: () => _openNoteEditor(filteredNotes[index]),
|
||||
isDragging: _isDragging,
|
||||
),
|
||||
),
|
||||
child: NoteCard(
|
||||
key: ValueKey<int>(filteredNotes[index].id ?? filteredNotes[index].index),
|
||||
note: filteredNotes[index],
|
||||
onTap: () => _openNoteEditor(filteredNotes[index]),
|
||||
isDragging: _isDragging,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
body: Container(
|
||||
@@ -314,84 +306,74 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
const AppTitleBar(),
|
||||
SearchAppBar(
|
||||
onMenuPressed: () {
|
||||
setState(() {
|
||||
_isMenuOpen = !_isMenuOpen;
|
||||
});
|
||||
},
|
||||
onSearchChanged: (String query) {
|
||||
setState(() {
|
||||
_searchQuery = query;
|
||||
});
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
|
||||
child: body,
|
||||
),
|
||||
// Dark overlay when menu is open; clicking it closes the menu.
|
||||
Positioned.fill(
|
||||
child: IgnorePointer(
|
||||
ignoring: !_isMenuOpen,
|
||||
child: AnimatedOpacity(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
opacity: _isMenuOpen ? 0.5 : 0.0,
|
||||
curve: Curves.easeOutCubic,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_isMenuOpen = false;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.black,
|
||||
children: [
|
||||
SearchAppBar(
|
||||
onMenuPressed: () {
|
||||
setState(() {
|
||||
_isMenuOpen = !_isMenuOpen;
|
||||
});
|
||||
},
|
||||
onSearchChanged: (String query) {
|
||||
setState(() {
|
||||
_searchQuery = query;
|
||||
});
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
|
||||
child: body,
|
||||
),
|
||||
Positioned.fill(
|
||||
child: IgnorePointer(
|
||||
ignoring: !_isMenuOpen,
|
||||
child: AnimatedOpacity(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
opacity: _isMenuOpen ? 0.5 : 0.0,
|
||||
curve: Curves.easeOutCubic,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_isMenuOpen = false;
|
||||
});
|
||||
},
|
||||
child: Container(color: Colors.black),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
AnimatedPositioned(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeOutCubic,
|
||||
left: _isMenuOpen ? 0 : -280,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: 280,
|
||||
child: Material(
|
||||
color: const Color.fromRGBO(24, 25, 26, 1),
|
||||
elevation: 8,
|
||||
child: MenuDrawer(
|
||||
onMenuItemTapped: (String item) {
|
||||
setState(() {
|
||||
_isMenuOpen = false;
|
||||
});
|
||||
AnimatedPositioned(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeOutCubic,
|
||||
left: _isMenuOpen ? 0 : -280,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: 280,
|
||||
child: Material(
|
||||
color: const Color.fromRGBO(24, 25, 26, 1),
|
||||
elevation: 8,
|
||||
child: MenuDrawer(
|
||||
onMenuItemTapped: (String item) {
|
||||
setState(() {
|
||||
_isMenuOpen = false;
|
||||
});
|
||||
|
||||
if (item == 'settings') {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => SettingsScreen(
|
||||
onDeleteAllData: widget.onDeleteAllData,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
if (item == 'settings') {
|
||||
widget.onOpenSettings();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _openNoteComposer,
|
||||
@@ -429,4 +411,4 @@ class _EmptyState extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user