Remove AppBar gap from notes and tasks list screens
Notes: no AppBar at all; a floating search icon (top-right corner) triggers an inline search field above the list rather than an AppBar. Tasks: no AppBar; TabBar sits directly at the top of the body, flush with the quick-capture bar. The search icon lives to the right of the tabs and expands a search field above them when tapped. Both changes eliminate the ~56dp toolbar gap that was visible between the quick-capture bar and the list content. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -36,90 +36,113 @@ class _TasksListScreenState extends ConsumerState<TasksListScreen>
|
||||
final tasksAsync = ref.watch(tasksProvider);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
title: _showSearch
|
||||
? TextField(
|
||||
autofocus: true,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Search tasks…',
|
||||
border: InputBorder.none,
|
||||
),
|
||||
onChanged: (v) =>
|
||||
setState(() => _search = v.trim().toLowerCase()),
|
||||
)
|
||||
: null,
|
||||
actions: [
|
||||
body: Column(
|
||||
children: [
|
||||
// Search field — appears above the tabs when active
|
||||
if (_showSearch)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
tooltip: 'Close search',
|
||||
onPressed: () => setState(() {
|
||||
_showSearch = false;
|
||||
_search = '';
|
||||
}),
|
||||
)
|
||||
else
|
||||
IconButton(
|
||||
icon: const Icon(Icons.search),
|
||||
tooltip: 'Search',
|
||||
onPressed: () => setState(() => _showSearch = true),
|
||||
),
|
||||
],
|
||||
bottom: TabBar(
|
||||
controller: _tabs,
|
||||
tabs: const [
|
||||
Tab(text: 'To Do'),
|
||||
Tab(text: 'In Progress'),
|
||||
Tab(text: 'Done'),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: 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'),
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
data: (tasks) {
|
||||
final filtered = _search.isEmpty
|
||||
? tasks
|
||||
: tasks
|
||||
.where((t) =>
|
||||
t.title.toLowerCase().contains(_search) ||
|
||||
(t.description ?? '').toLowerCase().contains(_search))
|
||||
.toList();
|
||||
// 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();
|
||||
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),
|
||||
],
|
||||
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',
|
||||
|
||||
Reference in New Issue
Block a user