Files
English/kouyu_english/lib/widgets/usage_card.dart
T

111 lines
3.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import '../core/app_state.dart';
import '../core/app_theme.dart';
import '../core/courses/courses.dart';
import 'app_widgets.dart';
/// What a taught pattern is *for*, next to what it means. Knowing that
/// `How are you?` means 你怎么样 does not tell a learner that it is not the
/// way to ask someone's name, so the course states the situation, the reply
/// it invites, and the items it is easiest to mix up with.
///
/// The card renders nothing when the pack explains nothing about the item,
/// which is the normal case for a plain word.
class UsageCard extends StatelessWidget {
const UsageCard({
super.key,
required this.itemId,
required this.state,
this.compact = false,
});
final String itemId;
final AppState state;
/// Inside a task step, only the situation and the difference are shown —
/// the learner is mid-exercise, not reading a reference card.
final bool compact;
/// A pattern counts as taught once it has a mastery row, which
/// `AppState` creates when the lesson first presents it.
bool _isTaught(String id) => state.mastery.containsKey(id);
@override
Widget build(BuildContext context) {
final usage = coreUsage(itemId);
if (usage == null) return const SizedBox.shrink();
final lines = <({String label, String text})>[
if (usage.when.isNotEmpty) (label: '什么时候用', text: usage.when),
if (!compact && usage.reply.isNotEmpty)
(label: '对方会怎么答', text: usage.reply),
if (!compact && usage.swap.isNotEmpty) (label: '换个说法', text: usage.swap),
// Mid-exercise only the nearest confusion is worth a line; the full
// list belongs on the preview and lookup cards.
for (final other in visibleConfusables(
itemId,
isTaught: _isTaught,
).take(compact ? 1 : 3))
if (other.note.isNotEmpty)
(label: '别和 ${coreItemSpoken(other.id)} 弄混', text: other.note),
];
if (lines.isEmpty) return const SizedBox.shrink();
return SectionCard(
tint: AppColors.warm,
child: SpacedColumn(
spacing: 8,
children: [
for (final line in lines)
RichText(
text: TextSpan(
style: DefaultTextStyle.of(
context,
).style.copyWith(color: AppColors.warmInk, height: 1.45),
children: [
TextSpan(
text: '${line.label}:',
style: const TextStyle(fontWeight: FontWeight.w600),
),
TextSpan(text: line.text),
],
),
),
],
),
);
}
}
/// Every usage note a teaching segment carries, in course order. Shown in the
/// speaking and writing steps, where a learner is about to produce the pattern
/// and the question is which one this situation calls for.
class SegmentUsage extends StatelessWidget {
const SegmentUsage({
super.key,
required this.segmentId,
required this.state,
this.compact = true,
});
final String segmentId;
final AppState state;
final bool compact;
@override
Widget build(BuildContext context) {
final ids = findLessonSegment(segmentId)?.targetItemIds ?? const <String>[];
final explained = [
for (final id in ids)
if (coreUsage(id) != null) id,
];
if (explained.isEmpty) return const SizedBox.shrink();
return SpacedColumn(
spacing: 8,
children: [
for (final id in explained)
UsageCard(itemId: id, state: state, compact: compact),
],
);
}
}