Files
shaynee-flutter/lib/widgets/s_card.dart
mav 6311088256 redesign: duolingo-pattern onboarding, no gradient, pressable button
- Nunito font replacing Plus Jakarta Sans
- SButton: Stack-based 3D pressable (dark shadow layer + top layer translates on press)
- SCard: flat border-based selection, no shadows
- SProgressBar: 4px thin bar, animated fill
- All screens: white bg, no gradients
- Duolingo layout: illustration top zone, content below, sticky button
2026-05-28 16:34:04 +02:00

112 lines
3.5 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../theme.dart';
// Duolingo-style option card. Flat — no shadow, border-based selection.
class SCard extends StatelessWidget {
final IconData icon;
final String label;
final String sub;
final bool isSelected;
final VoidCallback onTap;
const SCard({
super.key,
required this.icon,
required this.label,
required this.isSelected,
required this.onTap,
this.sub = '',
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
HapticFeedback.selectionClick();
onTap();
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 150),
curve: Curves.easeOut,
height: sub.isEmpty ? 62 : null,
padding: sub.isEmpty
? const EdgeInsets.symmetric(horizontal: 14)
: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
decoration: BoxDecoration(
color: isSelected ? SColors.primaryLight : SColors.bgSurface,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isSelected ? SColors.primaryBorder : SColors.borderLight,
width: isSelected ? 2.0 : 1.5,
),
),
child: Row(
children: [
// Icon box
AnimatedContainer(
duration: const Duration(milliseconds: 150),
width: 40,
height: 40,
decoration: BoxDecoration(
color: isSelected
? SColors.primary.withAlpha(20)
: const Color(0xFFF7F7F7),
borderRadius: BorderRadius.circular(10),
),
child: Icon(
icon,
size: 22,
color: isSelected ? SColors.primary : SColors.textBody,
),
),
const SizedBox(width: 12),
// Labels
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: SText.cardLabel(
isSelected ? SColors.primary : SColors.textHead,
),
),
if (sub.isNotEmpty) ...[
const SizedBox(height: 2),
Text(
sub,
style: SText.cardSub(SColors.textSub),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
],
),
),
const SizedBox(width: 10),
// Radio
AnimatedContainer(
duration: const Duration(milliseconds: 150),
width: 22,
height: 22,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isSelected ? SColors.primary : Colors.transparent,
border: Border.all(
color: isSelected ? SColors.primary : SColors.borderMedium,
width: 1.5,
),
),
child: isSelected
? const Icon(Icons.check, size: 13, color: Colors.white)
: null,
),
],
),
),
);
}
}