Files
base_flutter/lib/features/todos/presentation/screens/home_screen.dart
2025-09-26 18:48:14 +07:00

352 lines
9.7 KiB
Dart

import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
bool _isLoading = false;
List<Map<String, dynamic>> _todos = [];
String _searchQuery = '';
final TextEditingController _searchController = TextEditingController();
@override
void initState() {
super.initState();
_loadTodos();
}
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
// Mock todos data - will be replaced with API call
Future<void> _loadTodos() async {
setState(() {
_isLoading = true;
});
// Simulate API call delay
await Future.delayed(const Duration(seconds: 1));
// Mock data simulating JSONPlaceholder response
final mockTodos = [
{
'id': 1,
'title': 'Complete project documentation',
'completed': false,
'userId': 1,
},
{
'id': 2,
'title': 'Review code changes',
'completed': true,
'userId': 1,
},
{
'id': 3,
'title': 'Update Flutter dependencies',
'completed': false,
'userId': 1,
},
{
'id': 4,
'title': 'Write unit tests',
'completed': false,
'userId': 2,
},
{
'id': 5,
'title': 'Fix navigation bug',
'completed': true,
'userId': 2,
},
];
if (mounted) {
setState(() {
_todos = mockTodos;
_isLoading = false;
});
}
}
List<Map<String, dynamic>> get _filteredTodos {
if (_searchQuery.isEmpty) {
return _todos;
}
return _todos.where((todo) {
return todo['title']
.toString()
.toLowerCase()
.contains(_searchQuery.toLowerCase());
}).toList();
}
void _toggleTodoStatus(int id) {
setState(() {
final todoIndex = _todos.indexWhere((todo) => todo['id'] == id);
if (todoIndex != -1) {
_todos[todoIndex]['completed'] = !_todos[todoIndex]['completed'];
}
});
}
Future<void> _refreshTodos() async {
await _loadTodos();
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
return Scaffold(
appBar: AppBar(
title: const Text('My Todos'),
elevation: 0,
backgroundColor: colorScheme.surfaceVariant,
foregroundColor: colorScheme.onSurfaceVariant,
actions: [
IconButton(
icon: const Icon(Icons.logout),
onPressed: () {
// Handle logout - will be connected to auth logic later
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Logout functionality will be implemented'),
),
);
},
),
],
),
body: Column(
children: [
// Search Bar
Container(
padding: const EdgeInsets.all(16.0),
color: colorScheme.surfaceVariant,
child: TextField(
controller: _searchController,
onChanged: (value) {
setState(() {
_searchQuery = value;
});
},
decoration: InputDecoration(
hintText: 'Search todos...',
prefixIcon: const Icon(Icons.search),
suffixIcon: _searchQuery.isNotEmpty
? IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
_searchController.clear();
setState(() {
_searchQuery = '';
});
},
)
: null,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
filled: true,
fillColor: colorScheme.surface,
),
),
),
// Todos List
Expanded(
child: _isLoading
? const Center(
child: CircularProgressIndicator(),
)
: _todos.isEmpty
? _buildEmptyState()
: RefreshIndicator(
onRefresh: _refreshTodos,
child: _filteredTodos.isEmpty
? _buildNoResultsState()
: ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: _filteredTodos.length,
itemBuilder: (context, index) {
final todo = _filteredTodos[index];
return _buildTodoCard(todo);
},
),
),
),
],
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
// Handle add new todo
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Add todo functionality will be implemented'),
),
);
},
icon: const Icon(Icons.add),
label: const Text('Add Todo'),
),
);
}
Widget _buildTodoCard(Map<String, dynamic> todo) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
final isCompleted = todo['completed'] as bool;
return Card(
margin: const EdgeInsets.only(bottom: 8.0),
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: ListTile(
contentPadding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
leading: Checkbox(
value: isCompleted,
onChanged: (_) => _toggleTodoStatus(todo['id']),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
),
title: Text(
todo['title'],
style: theme.textTheme.bodyLarge?.copyWith(
decoration: isCompleted ? TextDecoration.lineThrough : null,
color: isCompleted
? colorScheme.onSurfaceVariant
: colorScheme.onSurface,
),
),
subtitle: Text(
'ID: ${todo['id']} • User: ${todo['userId']}',
style: theme.textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
trailing: PopupMenuButton<String>(
onSelected: (value) {
switch (value) {
case 'edit':
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Edit functionality will be implemented'),
),
);
break;
case 'delete':
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Delete functionality will be implemented'),
),
);
break;
}
},
itemBuilder: (context) => [
const PopupMenuItem(
value: 'edit',
child: Row(
children: [
Icon(Icons.edit),
SizedBox(width: 8),
Text('Edit'),
],
),
),
const PopupMenuItem(
value: 'delete',
child: Row(
children: [
Icon(Icons.delete),
SizedBox(width: 8),
Text('Delete'),
],
),
),
],
),
),
);
}
Widget _buildEmptyState() {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.task_outlined,
size: 64,
color: colorScheme.onSurfaceVariant,
),
const SizedBox(height: 16),
Text(
'No todos yet',
style: theme.textTheme.headlineSmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 8),
Text(
'Add your first todo to get started!',
style: theme.textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
],
),
);
}
Widget _buildNoResultsState() {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.search_off,
size: 64,
color: colorScheme.onSurfaceVariant,
),
const SizedBox(height: 16),
Text(
'No todos found',
style: theme.textTheme.headlineSmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 8),
Text(
'Try adjusting your search terms',
style: theme.textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
],
),
);
}
}