mirror of
https://github.com/kuhyx/todo-app.git
synced 2026-11-10 21:27:01 +01:00
702 -> 220 lines, decomposed rather than just moved, because a single
~600-line State class was the actual problem:
note_editor_document.dart the text model -- template, controllers, and
which of the two sources (raw body vs. section
fields) is authoritative. None of it needed a
BuildContext or a setState, which is what made
it the honest seam. The raw<->guided conversions
and the initial-template detection moved here as
tryAdoptGuided/materialiseToRaw/forInitialText.
note_editor_wizard.dart the two-step priority+template wizard, which now
owns its own step state instead of parking three
fields on the editor.
note_editor_step_page.dart the guided per-step view.
note_editor_chrome.dart the template picker + mode toggle.
note_editor_raw_field.dart the raw-mode text field.
note_editor_mode.dart the mode enum, re-exported so callers are
unaffected.
note_editor_{widget,views} parts, not libraries: createState returns a
private class, so the widget and its State
cannot be split across libraries.
The layout comments about Expanded/expands:true are load-bearing (nesting
broke the constraint chain in release builds before), so they travelled with
the code they describe rather than being summarised away.
Verified: 313 tests pass, flutter analyze clean. Covered lines went UP,
1922 -> 1958, because the wizard's state is now exercised in a widget of its
own; every new file reports 100%.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
96 lines
3.1 KiB
Dart
96 lines
3.1 KiB
Dart
/// The editor's chrome: the template picker and the View/Guided/Raw toggle.
|
|
///
|
|
/// Split out of `note_editor.dart` for file size. Stateless and callback-only
|
|
/// — every choice it offers is applied by `_NoteEditorState`, which owns the
|
|
/// template and mode. Guided hides this chrome entirely, so it is only ever
|
|
/// built in Preview and Raw.
|
|
library;
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:todo/data/note_template.dart';
|
|
import 'package:todo/ui/note_editor_mode.dart';
|
|
|
|
/// Template dropdown above a segmented View / Guided / Raw selector.
|
|
class NoteEditorChrome extends StatelessWidget {
|
|
/// Creates the chrome for [template] in [mode].
|
|
const NoteEditorChrome({
|
|
required this.template,
|
|
required this.mode,
|
|
required this.onTemplateSelected,
|
|
required this.onModeSelected,
|
|
super.key,
|
|
});
|
|
|
|
/// The template currently selected in the dropdown.
|
|
final NoteTemplate template;
|
|
|
|
/// The mode the segmented button shows as selected.
|
|
final NoteEditorMode mode;
|
|
|
|
/// Called with the template the user picked.
|
|
final ValueChanged<NoteTemplate> onTemplateSelected;
|
|
|
|
/// Called with the mode the user picked.
|
|
final ValueChanged<NoteEditorMode> onModeSelected;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
DropdownButtonFormField<String>(
|
|
initialValue: template.id,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Template',
|
|
isDense: true,
|
|
border: OutlineInputBorder(),
|
|
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
),
|
|
items: [
|
|
for (final t in NoteTemplate.all)
|
|
DropdownMenuItem(value: t.id, child: Text(t.label)),
|
|
],
|
|
onChanged: (id) {
|
|
if (id == null) return;
|
|
onTemplateSelected(
|
|
NoteTemplate.all.firstWhere((t) => t.id == id),
|
|
);
|
|
},
|
|
),
|
|
const SizedBox(height: 8),
|
|
Align(
|
|
alignment: Alignment.centerLeft,
|
|
child: SegmentedButton<NoteEditorMode>(
|
|
showSelectedIcon: false,
|
|
segments: [
|
|
const ButtonSegment(
|
|
value: NoteEditorMode.preview,
|
|
icon: Icon(Icons.visibility_outlined),
|
|
label: Text('View'),
|
|
),
|
|
// Guided is offered for any structured template; tapping it
|
|
// on an empty draft opens the wizard, and is blocked at
|
|
// switch time if the raw text no longer conforms.
|
|
if (!template.isFreeform)
|
|
const ButtonSegment(
|
|
value: NoteEditorMode.guided,
|
|
icon: Icon(Icons.checklist),
|
|
label: Text('Guided'),
|
|
),
|
|
const ButtonSegment(
|
|
value: NoteEditorMode.raw,
|
|
icon: Icon(Icons.notes),
|
|
label: Text('Raw'),
|
|
),
|
|
],
|
|
selected: {mode},
|
|
onSelectionChanged: (s) => onModeSelected(s.first),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|