full app done
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
/// Stage 1 — assign each value card into one of five reflective piles.
|
||||
library;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../models/value_pile.dart';
|
||||
import '../providers/values_sort_provider.dart';
|
||||
import '../widgets/practice_chrome.dart';
|
||||
import '../widgets/value_card_tile.dart';
|
||||
|
||||
class CardSortingScreen extends StatelessWidget {
|
||||
const CardSortingScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final sort = context.watch<ValuesSortController>();
|
||||
final text = Theme.of(context).textTheme;
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
final card = sort.currentCard;
|
||||
|
||||
return PracticeScaffold(
|
||||
padding: const EdgeInsets.fromLTRB(20, 4, 20, 8),
|
||||
header: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 4, 12, 0),
|
||||
child: Row(
|
||||
children: [
|
||||
TextButton.icon(
|
||||
onPressed: sort.sortedCount == 0 ? null : sort.undoLastSort,
|
||||
icon: const Icon(Icons.undo, size: 18),
|
||||
label: const Text('Undo'),
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
'Stage 1 · Initial Sort',
|
||||
style: text.labelMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.bookmark_border, size: 18, color: colors.onSurfaceVariant),
|
||||
const SizedBox(width: 6),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'${sort.sortedCount} of ${sort.totalCount} values sorted',
|
||||
style: text.labelMedium,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${(sort.sortProgress * 100).round()}%',
|
||||
style: text.labelMedium?.copyWith(
|
||||
color: colors.primary,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
child: LinearProgressIndicator(
|
||||
value: sort.sortProgress,
|
||||
minHeight: 6,
|
||||
backgroundColor: colors.surfaceContainerHigh,
|
||||
color: colors.secondary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Expanded(
|
||||
child: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 280),
|
||||
child: card == null
|
||||
? Center(
|
||||
key: const ValueKey('empty'),
|
||||
child: Text('All sorted', style: text.headlineMedium),
|
||||
)
|
||||
: SingleChildScrollView(
|
||||
key: ValueKey(card.id),
|
||||
child: Column(
|
||||
children: [
|
||||
ValueCardTile(card: card, elevation: 2),
|
||||
const SizedBox(height: 14),
|
||||
Text(
|
||||
'Tap a pile to sort',
|
||||
style: text.labelMedium?.copyWith(
|
||||
color: colors.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
...ValuePile.values.map((pile) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: _PileButton(
|
||||
pile: pile,
|
||||
count: sort.countInPile(pile),
|
||||
onTap: () => sort.assignCurrent(pile),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PileButton extends StatelessWidget {
|
||||
const _PileButton({
|
||||
required this.pile,
|
||||
required this.count,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
final ValuePile pile;
|
||||
final int count;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final text = Theme.of(context).textTheme;
|
||||
final colors = Theme.of(context).colorScheme;
|
||||
|
||||
return Material(
|
||||
color: colors.surfaceContainerLow,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
side: BorderSide(color: pile.accent.withValues(alpha: 0.35)),
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 28,
|
||||
height: 28,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: pile.accent.withValues(alpha: 0.15),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Text(
|
||||
'${pile.rank}',
|
||||
style: text.labelMedium?.copyWith(
|
||||
color: pile.accent,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
pile.label,
|
||||
style: text.titleMedium?.copyWith(
|
||||
color: colors.onSurface,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
pile.subtitle,
|
||||
style: text.bodySmall?.copyWith(
|
||||
color: colors.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'$count',
|
||||
style: text.labelMedium?.copyWith(color: pile.accent),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user