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 []; 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), ], ); } }