todo-app/lib/ui/capture_app_bar.dart
Krzysztof kuhy Rudnicki d4c8162e0e Split capture_screen.dart into ten files under the cap
739 -> 249 lines. As with note_editor, the ~600-line State class needed
decomposing rather than moving:

  capture_draft.dart         the note being captured and its write-through
                             to storage -- the model behind "every keystroke
                             is already persisted". Lazy row creation stays
                             here; the screen just logs that it happened.
  capture_sync_runner.dart   one sync pass, with no BuildContext. This also
                             de-duplicates the two callers: the manual button
                             and the background tick ran byte-identical
                             runSync blocks differing only in the snackbar
                             and the 'auto' log flag.
  capture_status.dart        SyncStatusStore (the persisted last outcome) and
                             describeSyncResult.
  capture_app_bar.dart       the app bar.
  capture_meta_row.dart      the priority/status pickers.
  capture_status_footer.dart the two status lines.
  capture_screen_{widget,sync}.dart
                             parts, for the same reason as note_editor's.

The mounted checks are preserved exactly, including the one in
SyncStatusStore.restore that must fire AFTER its await rather than before
(writing the notifier post-dispose is the bug it exists to prevent), which
is why it takes a stillLive callback instead of a bool.

Verified: 313 tests pass, analyze clean, every capture file at 100%
coverage. lib/ and bin/ are now down to three files over the cap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-14 16:25:51 +02:00

122 lines
3.5 KiB
Dart

/// The capture screen's app bar: note count, and the four actions.
///
/// Split out of `capture_screen.dart` for file size. Callback-only — the
/// screen still owns every action; this just lays the row out.
library;
import 'package:flutter/material.dart';
import 'package:todo/data/note_repository.dart';
import 'package:todo/frame_stats.dart';
/// App bar for the capture screen.
class CaptureAppBar extends StatelessWidget implements PreferredSizeWidget {
/// Creates the capture screen's app bar.
const CaptureAppBar({
required this.repository,
required this.advanced,
required this.syncing,
required this.onNewNote,
required this.onSync,
required this.onOpenList,
required this.onOpenSettings,
super.key,
});
/// Store whose note count is shown live.
final NoteRepository repository;
/// Whether advanced mode is on; hides the manual Sync action when off.
final bool advanced;
/// Whether a sync is in flight, which disables and spins the Sync action.
final bool syncing;
/// Saves the current draft and clears the box for a new one.
final VoidCallback onNewNote;
/// Runs a manual sync.
final VoidCallback onSync;
/// Opens the notes list.
final VoidCallback onOpenList;
/// Opens the settings screen.
final VoidCallback onOpenSettings;
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
@override
Widget build(BuildContext context) {
return AppBar(
actions: [
// TEMPORARY: forces continuous frame production so raster cost
// can be sampled at each window size. Armed by
// TODO_FRAME_STATS=1, which is never set under the test
// runner, so the branch is unreachable.
// coverage:ignore-start
if (frameStatsEnabled)
const Padding(
padding: EdgeInsets.only(right: 8),
child: Center(
child: SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(strokeWidth: 2),
),
),
),
// coverage:ignore-end
// Live count of stored notes, proving local persistence.
StreamBuilder<int>(
stream: repository.watchCount(),
builder: (context, snapshot) {
final count = snapshot.data ?? 0;
return Padding(
padding: const EdgeInsets.only(right: 4),
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.list_alt_outlined, size: 18),
const SizedBox(width: 4),
Text('$count'),
],
),
),
);
},
),
IconButton(
tooltip: 'New note',
onPressed: onNewNote,
icon: const Icon(Icons.add),
),
if (advanced)
IconButton(
tooltip: 'Sync',
onPressed: syncing ? null : onSync,
icon: syncing
? const SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.sync),
),
IconButton(
tooltip: 'Notes',
onPressed: onOpenList,
icon: const Icon(Icons.list),
),
IconButton(
tooltip: 'Settings',
onPressed: onOpenSettings,
icon: const Icon(Icons.settings),
),
],
);
}
}