fix(learn): a passage's gaps flow inside its text

A passage was a Wrap of a Text per part between gaps. A Wrap places whole
children, so a part longer than a line took the width to itself, and every
gap and the text after it started a line of its own. One Text.rich with each
gap a WidgetSpan wraps the text as one paragraph, the gaps standing in it.

Whats-New: Fill-in-the-blank passages read as running text, their gaps inside the lines.
Whats-New-De: Lückentexte lesen sich als fortlaufender Text, die Lücken stehen in den Zeilen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-11-11 15:44:51 +01:00
commit b45cb0f3cb
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -339,26 +339,38 @@ class const FillInTheBlankView({
),
),
const SizedBox(height: 12),
Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
runSpacing: 8,
children: [
for (var gap = 0; gap < exercise.gaps; gap++) ...[
Text(parts[gap], style: theme.textTheme.bodyLarge),
DragTarget<String>(
onAcceptWithDetails: (details) => fill(gap, details.data),
builder: (context, candidates, _) => _Gap(
word: words[gap],
highlighted: candidates.isNotEmpty,
mark: markOf(grade, gap, picked: words[gap] != null),
onTap: words[gap] == null
? null
: () => onChanged([...words]..[gap] = null),
// One paragraph with the gaps standing in its lines: a Wrap of a
// Text per part gave a long part the width to itself, and put each
// gap, and the text after it, on a line of its own.
Text.rich(
TextSpan(
style: theme.textTheme.bodyLarge,
children: [
for (var gap = 0; gap < exercise.gaps; gap++) ...[
TextSpan(text: parts[gap]),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Padding(
// Room above and below, so a gap on one line doesn't
// touch a gap on the next.
padding: const EdgeInsets.symmetric(vertical: 4),
child: DragTarget<String>(
onAcceptWithDetails: (details) => fill(gap, details.data),
builder: (context, candidates, _) => _Gap(
word: words[gap],
highlighted: candidates.isNotEmpty,
mark: markOf(grade, gap, picked: words[gap] != null),
onTap: words[gap] == null
? null
: () => onChanged([...words]..[gap] = null),
),
),
),
),
),
],
TextSpan(text: parts.last),
],
Text(parts.last, style: theme.textTheme.bodyLarge),
],
),
),
const SizedBox(height: 16),
Wrap(