import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../core/constants.dart'; import '../../data/models/task.dart'; import '../../providers/tasks_provider.dart'; class TasksListScreen extends ConsumerStatefulWidget { const TasksListScreen({super.key}); @override ConsumerState createState() => _TasksListScreenState(); } class _TasksListScreenState extends ConsumerState with SingleTickerProviderStateMixin { late final TabController _tabs; bool _showSearch = false; String _search = ''; @override void initState() { super.initState(); _tabs = TabController(length: 3, vsync: this); } @override void dispose() { _tabs.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final tasksAsync = ref.watch(tasksProvider); return Scaffold( body: Column( children: [ // Search field — appears above the tabs when active if (_showSearch) Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: Row( children: [ Expanded( child: TextField( autofocus: true, decoration: const InputDecoration( hintText: 'Search tasks…', border: InputBorder.none, prefixIcon: Icon(Icons.search), ), onChanged: (v) => setState(() => _search = v.trim().toLowerCase()), ), ), IconButton( icon: const Icon(Icons.close), tooltip: 'Close search', onPressed: () => setState(() { _showSearch = false; _search = ''; }), ), ], ), ), // Tab bar row — search icon sits to the right of the tabs Row( children: [ Expanded( child: TabBar( controller: _tabs, tabs: const [ Tab(text: 'To Do'), Tab(text: 'In Progress'), Tab(text: 'Done'), ], ), ), if (!_showSearch) IconButton( icon: const Icon(Icons.search), tooltip: 'Search', onPressed: () => setState(() => _showSearch = true), ), ], ), // Content Expanded( child: tasksAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (_, _) => Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.cloud_off, size: 48), const SizedBox(height: 12), const Text('Could not load tasks.'), const SizedBox(height: 4), TextButton( onPressed: () => ref.invalidate(tasksProvider), child: const Text('Retry'), ), ], ), ), data: (tasks) { final filtered = _search.isEmpty ? tasks : tasks .where((t) => t.title.toLowerCase().contains(_search) || (t.description ?? '') .toLowerCase() .contains(_search)) .toList(); final todo = filtered .where((t) => t.status == TaskStatus.todo) .toList(); final inProgress = filtered .where((t) => t.status == TaskStatus.inProgress) .toList(); final done = filtered .where((t) => t.status == TaskStatus.done) .toList(); return RefreshIndicator( onRefresh: () => ref.refresh(tasksProvider.future), child: TabBarView( controller: _tabs, children: [ _TaskList(tasks: todo, search: _search), _TaskList(tasks: inProgress, search: _search), _TaskList(tasks: done, search: _search), ], ), ); }, ), ), ], ), floatingActionButton: FloatingActionButton( heroTag: 'tasks_fab', onPressed: () => context.push(Routes.taskNew), child: const Icon(Icons.add), ), ); } } class _TaskList extends ConsumerWidget { final List tasks; final String search; const _TaskList({required this.tasks, this.search = ''}); @override Widget build(BuildContext context, WidgetRef ref) { if (tasks.isEmpty) { return Center( child: Text( search.isEmpty ? 'No tasks here.' : 'No tasks match "$search".', ), ); } return ListView.separated( itemCount: tasks.length, separatorBuilder: (_, _) => const Divider(height: 1), itemBuilder: (context, i) { final task = tasks[i]; return ListTile( leading: _priorityIcon(task.priority), title: Text(task.title), subtitle: task.dueDate != null ? Text( 'Due: ${task.dueDate!.toLocal().toString().substring(0, 10)}') : null, onTap: () => context.push( Routes.taskEdit.replaceFirst(':id', '${task.id}'), ), ); }, ); } Widget _priorityIcon(TaskPriority p) { final color = switch (p) { TaskPriority.high => Colors.red, TaskPriority.medium => Colors.orange, TaskPriority.low => Colors.green, TaskPriority.none => Colors.grey, }; return Icon(Icons.flag, color: color); } }