Refactor UI components to use design system tokens for consistency

- Removed the UiSuccessSnackbar widget as it is no longer needed.
- Updated WorkerHomePage to replace hardcoded spacing with UiConstants.
- Refactored HomeHeader to use UiConstants for dimensions and colors.
- Modified PlaceholderBanner to utilize UiConstants for sizes and colors.
- Adjusted QuickActionItem to apply UiConstants for dimensions and icon sizes.
- Updated RecommendedShiftCard to use design system typography and constants.
- Refined SectionHeader to implement design system styles and spacing.
- Enhanced ShiftCard to adopt design system tokens for colors and spacing.
- Updated AutoMatchToggle to use design system colors and dimensions.
- Refactored BenefitsWidget to apply design system styles and constants.
- Improved ImproveYourselfWidget to utilize design system tokens for styling.
- Updated MoreWaysToUseKrowWidget to implement design system styles and constants.
This commit is contained in:
Achintha Isuru
2026-02-10 16:25:54 -05:00
parent 77370a7688
commit 2730277075
16 changed files with 284 additions and 604 deletions

View File

@@ -10,7 +10,5 @@ export 'src/widgets/ui_step_indicator.dart';
export 'src/widgets/ui_icon_button.dart'; export 'src/widgets/ui_icon_button.dart';
export 'src/widgets/ui_button.dart'; export 'src/widgets/ui_button.dart';
export 'src/widgets/ui_chip.dart'; export 'src/widgets/ui_chip.dart';
export 'src/widgets/ui_error_snackbar.dart';
export 'src/widgets/ui_success_snackbar.dart';
export 'src/widgets/ui_loading_page.dart'; export 'src/widgets/ui_loading_page.dart';
export 'src/widgets/ui_snackbar.dart'; export 'src/widgets/ui_snackbar.dart';

View File

@@ -255,7 +255,7 @@ class UiTheme {
} }
return UiColors.switchInactive; return UiColors.switchInactive;
}), }),
thumbColor: const WidgetStatePropertyAll(UiColors.white), thumbColor: const WidgetStatePropertyAll<Color>(UiColors.white),
), ),
// Checkbox Theme // Checkbox Theme

View File

@@ -1,202 +0,0 @@
import 'package:flutter/material.dart';
import 'package:core_localization/core_localization.dart';
import '../ui_colors.dart';
import '../ui_typography.dart';
/// Centralized error snackbar for consistent error presentation across the app.
///
/// This widget automatically resolves localization keys and displays
/// user-friendly error messages with optional error codes for support.
///
/// Usage:
/// ```dart
/// UiErrorSnackbar.show(
/// context,
/// messageKey: 'errors.auth.invalid_credentials',
/// errorCode: 'AUTH_001',
/// );
/// ```
class UiErrorSnackbar {
/// Shows an error snackbar with a localized message.
///
/// [messageKey] should be a dot-separated path like 'errors.auth.invalid_credentials'
/// [errorCode] is optional and will be shown in smaller text for support reference
/// [duration] controls how long the snackbar is visible
static void show(
BuildContext context, {
required String messageKey,
String? errorCode,
Duration duration = const Duration(seconds: 4),
}) {
// 1. Added explicit type 'Translations' to satisfy the lint
final Translations texts = Translations.of(context);
final String message = _getMessageFromKey(texts, messageKey);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
const Icon(Icons.error_outline, color: UiColors.white),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
message,
style: UiTypography.body2m.copyWith(color: UiColors.white),
),
if (errorCode != null) ...[
const SizedBox(height: 4),
Text(
'Error Code: $errorCode',
style: UiTypography.footnote2r.copyWith(
// 3. Fixed deprecated member use
color: UiColors.white.withValues(alpha: 0.7),
),
),
],
],
),
),
],
),
backgroundColor: UiColors.error,
behavior: SnackBarBehavior.floating,
duration: duration,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
margin: const EdgeInsets.all(16),
),
);
}
/// Resolves a localization key path to the actual translated message.
///
/// Supports keys like:
/// - errors.auth.invalid_credentials
/// - errors.hub.has_orders
/// - errors.generic.unknown
static String _getMessageFromKey(Translations texts, String key) {
// Parse key like "errors.auth.invalid_credentials"
final parts = key.split('.');
if (parts.length < 2) return texts.errors.generic.unknown;
try {
switch (parts[1]) {
case 'auth':
return _getAuthError(texts, parts.length > 2 ? parts[2] : '');
case 'hub':
return _getHubError(texts, parts.length > 2 ? parts[2] : '');
case 'order':
return _getOrderError(texts, parts.length > 2 ? parts[2] : '');
case 'profile':
return _getProfileError(texts, parts.length > 2 ? parts[2] : '');
case 'shift':
return _getShiftError(texts, parts.length > 2 ? parts[2] : '');
case 'generic':
return _getGenericError(texts, parts.length > 2 ? parts[2] : '');
default:
return texts.errors.generic.unknown;
}
} catch (_) {
return texts.errors.generic.unknown;
}
}
static String _getAuthError(Translations texts, String key) {
switch (key) {
case 'invalid_credentials':
return texts.errors.auth.invalid_credentials;
case 'account_exists':
return texts.errors.auth.account_exists;
case 'session_expired':
return texts.errors.auth.session_expired;
case 'user_not_found':
return texts.errors.auth.user_not_found;
case 'unauthorized_app':
return texts.errors.auth.unauthorized_app;
case 'weak_password':
return texts.errors.auth.weak_password;
case 'sign_up_failed':
return texts.errors.auth.sign_up_failed;
case 'sign_in_failed':
return texts.errors.auth.sign_in_failed;
case 'not_authenticated':
return texts.errors.auth.not_authenticated;
case 'password_mismatch':
return texts.errors.auth.password_mismatch;
case 'google_only_account':
return texts.errors.auth.google_only_account;
default:
return texts.errors.generic.unknown;
}
}
static String _getHubError(Translations texts, String key) {
switch (key) {
case 'has_orders':
return texts.errors.hub.has_orders;
case 'not_found':
return texts.errors.hub.not_found;
case 'creation_failed':
return texts.errors.hub.creation_failed;
default:
return texts.errors.generic.unknown;
}
}
static String _getOrderError(Translations texts, String key) {
switch (key) {
case 'missing_hub':
return texts.errors.order.missing_hub;
case 'missing_vendor':
return texts.errors.order.missing_vendor;
case 'creation_failed':
return texts.errors.order.creation_failed;
case 'shift_creation_failed':
return texts.errors.order.shift_creation_failed;
case 'missing_business':
return texts.errors.order.missing_business;
default:
return texts.errors.generic.unknown;
}
}
static String _getProfileError(Translations texts, String key) {
switch (key) {
case 'staff_not_found':
return texts.errors.profile.staff_not_found;
case 'business_not_found':
return texts.errors.profile.business_not_found;
case 'update_failed':
return texts.errors.profile.update_failed;
default:
return texts.errors.generic.unknown;
}
}
static String _getShiftError(Translations texts, String key) {
switch (key) {
case 'no_open_roles':
return texts.errors.shift.no_open_roles;
case 'application_not_found':
return texts.errors.shift.application_not_found;
case 'no_active_shift':
return texts.errors.shift.no_active_shift;
default:
return texts.errors.generic.unknown;
}
}
static String _getGenericError(Translations texts, String key) {
switch (key) {
case 'unknown':
return texts.errors.generic.unknown;
case 'no_connection':
return texts.errors.generic.no_connection;
default:
return texts.errors.generic.unknown;
}
}
}

View File

@@ -8,12 +8,6 @@ import '../ui_icons.dart';
/// This widget shows a series of circular step indicators connected by lines, /// This widget shows a series of circular step indicators connected by lines,
/// with different visual states for completed, active, and inactive steps. /// with different visual states for completed, active, and inactive steps.
class UiStepIndicator extends StatelessWidget { class UiStepIndicator extends StatelessWidget {
/// The list of icons to display for each step.
final List<IconData> stepIcons;
/// The index of the currently active step (0-based).
final int currentStep;
/// Creates a [UiStepIndicator]. /// Creates a [UiStepIndicator].
const UiStepIndicator({ const UiStepIndicator({
super.key, super.key,
@@ -21,6 +15,12 @@ class UiStepIndicator extends StatelessWidget {
required this.currentStep, required this.currentStep,
}); });
/// The list of icons to display for each step.
final List<IconData> stepIcons;
/// The index of the currently active step (0-based).
final int currentStep;
@override @override
/// Builds the step indicator UI. /// Builds the step indicator UI.
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -35,7 +35,7 @@ class UiStepIndicator extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: UiConstants.space2), padding: const EdgeInsets.symmetric(vertical: UiConstants.space2),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(stepIcons.length, (int index) { children: List<Widget>.generate(stepIcons.length, (int index) {
final bool isActive = index == currentStep; final bool isActive = index == currentStep;
final bool isCompleted = index < currentStep; final bool isCompleted = index < currentStep;

View File

@@ -1,49 +0,0 @@
import 'package:flutter/material.dart';
import '../ui_colors.dart';
import '../ui_typography.dart';
/// Centralized success snackbar for consistent success message presentation.
///
/// This widget provides a unified way to show success feedback across the app
/// with consistent styling and behavior.
///
/// Usage:
/// ```dart
/// UiSuccessSnackbar.show(
/// context,
/// message: 'Profile updated successfully!',
/// );
/// ```
class UiSuccessSnackbar {
/// Shows a success snackbar with a custom message.
///
/// [message] is the success message to display
/// [duration] controls how long the snackbar is visible
static void show(
BuildContext context, {
required String message,
Duration duration = const Duration(seconds: 3),
}) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
Icon(Icons.check_circle_outline, color: UiColors.white),
const SizedBox(width: 12),
Expanded(
child: Text(
message,
style: UiTypography.body2m.copyWith(color: UiColors.white),
),
),
],
),
backgroundColor: UiColors.success,
behavior: SnackBarBehavior.floating,
duration: duration,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
margin: const EdgeInsets.all(16),
),
);
}
}

View File

@@ -104,7 +104,7 @@ class WorkerHomePage extends StatelessWidget {
), ),
], ],
), ),
const SizedBox(height: 24), const SizedBox(height: UiConstants.space6),
// Today's Shifts // Today's Shifts
BlocBuilder<HomeCubit, HomeState>( BlocBuilder<HomeCubit, HomeState>(
@@ -123,9 +123,11 @@ class WorkerHomePage extends StatelessWidget {
if (state.status == HomeStatus.loading) if (state.status == HomeStatus.loading)
const Center( const Center(
child: SizedBox( child: SizedBox(
height: 40, height: UiConstants.space10,
width: 40, width: UiConstants.space10,
child: CircularProgressIndicator(), child: CircularProgressIndicator(
color: UiColors.primary,
),
), ),
) )
else if (shifts.isEmpty) else if (shifts.isEmpty)
@@ -149,7 +151,7 @@ class WorkerHomePage extends StatelessWidget {
); );
}, },
), ),
const SizedBox(height: 24), const SizedBox(height: UiConstants.space6),
// Tomorrow's Shifts // Tomorrow's Shifts
BlocBuilder<HomeCubit, HomeState>( BlocBuilder<HomeCubit, HomeState>(
@@ -177,7 +179,7 @@ class WorkerHomePage extends StatelessWidget {
); );
}, },
), ),
const SizedBox(height: 24), const SizedBox(height: UiConstants.space6),
// Recommended Shifts // Recommended Shifts
SectionHeader( SectionHeader(
@@ -197,7 +199,8 @@ class WorkerHomePage extends StatelessWidget {
itemCount: state.recommendedShifts.length, itemCount: state.recommendedShifts.length,
clipBehavior: Clip.none, clipBehavior: Clip.none,
itemBuilder: (context, index) => Padding( itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.only(right: 12), padding: const EdgeInsets.only(
right: UiConstants.space3),
child: RecommendedShiftCard( child: RecommendedShiftCard(
shift: state.recommendedShifts[index], shift: state.recommendedShifts[index],
), ),
@@ -206,7 +209,7 @@ class WorkerHomePage extends StatelessWidget {
); );
}, },
), ),
const SizedBox(height: 24), const SizedBox(height: UiConstants.space6),
], ],
), ),
), ),

View File

@@ -28,23 +28,20 @@ class HomeHeader extends StatelessWidget {
spacing: UiConstants.space3, spacing: UiConstants.space3,
children: [ children: [
Container( Container(
width: 48, width: UiConstants.space12,
height: 48, height: UiConstants.space12,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
border: Border.all( border: Border.all(
color: UiColors.primary.withOpacity(0.2), color: UiColors.primary.withValues(alpha: 0.2),
width: 2, width: 2,
), ),
), ),
child: CircleAvatar( child: CircleAvatar(
backgroundColor: UiColors.primary.withOpacity(0.1), backgroundColor: UiColors.primary.withValues(alpha: 0.1),
child: Text( child: Text(
initial, initial,
style: const TextStyle( style: UiTypography.body1b.textPrimary,
color: UiColors.primary,
fontWeight: FontWeight.bold,
),
), ),
), ),
), ),

View File

@@ -29,19 +29,19 @@ class PlaceholderBanner extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
color: bg, color: bg,
borderRadius: BorderRadius.circular(UiConstants.radiusBase), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(color: accent.withOpacity(0.3)), border: Border.all(color: accent.withValues(alpha: 0.3)),
), ),
child: Row( child: Row(
children: [ children: [
Container( Container(
width: 40, width: UiConstants.space10,
height: 40, height: UiConstants.space10,
padding: const EdgeInsets.all(UiConstants.space2), padding: const EdgeInsets.all(UiConstants.space2),
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: UiColors.bgBanner, color: UiColors.bgBanner,
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: Icon(UiIcons.sparkles, color: accent, size: 20), child: Icon(UiIcons.sparkles, color: accent, size: UiConstants.space5),
), ),
const SizedBox(width: UiConstants.space3), const SizedBox(width: UiConstants.space3),
Expanded( Expanded(

View File

@@ -22,8 +22,8 @@ class QuickActionItem extends StatelessWidget {
child: Column( child: Column(
children: [ children: [
Container( Container(
width: 64, width: UiConstants.space16,
height: 64, height: UiConstants.space16,
padding: const EdgeInsets.all(UiConstants.space4), padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: UiColors.bgBanner, color: UiColors.bgBanner,
@@ -31,13 +31,13 @@ class QuickActionItem extends StatelessWidget {
border: Border.all(color: UiColors.bgSecondary), border: Border.all(color: UiColors.bgSecondary),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: UiColors.foreground.withOpacity(0.05), color: UiColors.foreground.withValues(alpha: 0.05),
blurRadius: 4, blurRadius: 4,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
], ],
), ),
child: Icon(icon, color: UiColors.primary, size: 24), child: Icon(icon, color: UiColors.primary, size: UiConstants.space6),
), ),
const SizedBox(height: UiConstants.space2), const SizedBox(height: UiConstants.space2),
Text( Text(

View File

@@ -14,8 +14,6 @@ class RecommendedShiftCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final recI18n = t.staff.home.recommended_card; final recI18n = t.staff.home.recommended_card;
final duration = 8;
final totalPay = duration * shift.hourlyRate;
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
@@ -23,14 +21,14 @@ class RecommendedShiftCard extends StatelessWidget {
}, },
child: Container( child: Container(
width: 300, width: 300,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: UiColors.white,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(color: UiColors.border), border: Border.all(color: UiColors.border),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withValues(alpha: 0.02), color: UiColors.black.withValues(alpha: 0.02),
blurRadius: 4, blurRadius: 4,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@@ -45,51 +43,43 @@ class RecommendedShiftCard extends StatelessWidget {
children: [ children: [
Text( Text(
recI18n.act_now, recI18n.act_now,
style: const TextStyle( style: UiTypography.body3m.copyWith(color: UiColors.textError),
fontSize: 10,
fontWeight: FontWeight.bold,
color: Color(0xFFDC2626),
),
), ),
const SizedBox(width: 8), const SizedBox(width: UiConstants.space2),
Container( Container(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 8, horizontal: UiConstants.space2,
vertical: 2, vertical: 2,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFFE8F0FF), color: UiColors.tagInProgress,
borderRadius: BorderRadius.circular(999), borderRadius: UiConstants.radiusFull,
), ),
child: Text( child: Text(
recI18n.one_day, recI18n.one_day,
style: const TextStyle( style: UiTypography.body3m.textPrimary,
fontSize: 10,
fontWeight: FontWeight.w500,
color: Color(0xFF0047FF),
),
), ),
), ),
], ],
), ),
const SizedBox(height: 12), const SizedBox(height: UiConstants.space3),
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Container( Container(
width: 44, width: UiConstants.space10,
height: 44, height: UiConstants.space10,
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFFE8F0FF), color: UiColors.tagInProgress,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
), ),
child: const Icon( child: const Icon(
UiIcons.calendar, UiIcons.calendar,
color: Color(0xFF0047FF), color: UiColors.primary,
size: 20, size: UiConstants.space5,
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: UiConstants.space3),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -97,25 +87,16 @@ class RecommendedShiftCard extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Expanded( Flexible(
child: Text( child: Text(
shift.title, shift.title,
style: const TextStyle( style: UiTypography.body1m.textPrimary,
fontWeight: FontWeight.w600,
fontSize: 16,
color: UiColors.foreground,
),
maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
), ),
Text( Text(
'\$${totalPay.round()}', '\$${shift.hourlyRate}/h',
style: const TextStyle( style: UiTypography.headline4m.textPrimary,
fontSize: 18,
fontWeight: FontWeight.bold,
color: UiColors.foreground,
),
), ),
], ],
), ),
@@ -125,17 +106,11 @@ class RecommendedShiftCard extends StatelessWidget {
children: [ children: [
Text( Text(
shift.clientName, shift.clientName,
style: const TextStyle( style: UiTypography.body3r.textSecondary,
fontSize: 12,
color: UiColors.mutedForeground,
),
), ),
Text( Text(
'\$${shift.hourlyRate.toStringAsFixed(0)}/hr${duration}h', '\$${shift.hourlyRate.toStringAsFixed(0)}/hr',
style: const TextStyle( style: UiTypography.body3r.textSecondary,
fontSize: 10,
color: UiColors.mutedForeground,
),
), ),
], ],
), ),
@@ -144,57 +119,48 @@ class RecommendedShiftCard extends StatelessWidget {
), ),
], ],
), ),
const SizedBox(height: 12), const SizedBox(height: UiConstants.space3),
Row( Row(
children: [ children: [
const Icon( const Icon(
UiIcons.calendar, UiIcons.calendar,
size: 14, size: UiConstants.space3,
color: UiColors.mutedForeground, color: UiColors.mutedForeground,
), ),
const SizedBox(width: 4), const SizedBox(width: UiConstants.space1),
Text( Text(
recI18n.today, recI18n.today,
style: const TextStyle( style: UiTypography.body3r.textSecondary,
fontSize: 12,
color: UiColors.mutedForeground,
),
), ),
const SizedBox(width: 12), const SizedBox(width: UiConstants.space3),
const Icon( const Icon(
UiIcons.clock, UiIcons.clock,
size: 14, size: UiConstants.space3,
color: UiColors.mutedForeground, color: UiColors.mutedForeground,
), ),
const SizedBox(width: 4), const SizedBox(width: UiConstants.space1),
Text( Text(
recI18n.time_range( recI18n.time_range(
start: shift.startTime, start: shift.startTime,
end: shift.endTime, end: shift.endTime,
), ),
style: const TextStyle( style: UiTypography.body3r.textSecondary,
fontSize: 12,
color: UiColors.mutedForeground,
),
), ),
], ],
), ),
const SizedBox(height: 4), const SizedBox(height: UiConstants.space1),
Row( Row(
children: [ children: [
const Icon( const Icon(
UiIcons.mapPin, UiIcons.mapPin,
size: 14, size: UiConstants.space3,
color: UiColors.mutedForeground, color: UiColors.mutedForeground,
), ),
const SizedBox(width: 4), const SizedBox(width: UiConstants.space1),
Expanded( Expanded(
child: Text( child: Text(
shift.locationAddress, shift.locationAddress,
style: const TextStyle( style: UiTypography.body3r.textSecondary,
fontSize: 12,
color: UiColors.mutedForeground,
),
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),

View File

@@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
/// Section header widget for home page sections, using design system tokens. /// Section header widget for home page sections, using design system tokens.
class SectionHeader extends StatelessWidget { class SectionHeader extends StatelessWidget {
/// Section title /// Section title
@@ -23,43 +22,58 @@ class SectionHeader extends StatelessWidget {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Expanded(
title, child: action != null
style: UiTypography.headline4m, ? Row(
), mainAxisAlignment: MainAxisAlignment.spaceBetween,
if (action != null) children: [
if (onAction != null) Text(
GestureDetector( title,
onTap: onAction, style: UiTypography.body2m.textPrimary,
child: Row( ),
children: [ if (onAction != null)
Text( GestureDetector(
action!, onTap: onAction,
style: UiTypography.body2m.copyWith(color: UiColors.primary), child: Row(
), children: [
const Icon( Text(
UiIcons.chevronRight, action ?? '',
size: 16, style: UiTypography.body3r.textPrimary,
color: UiColors.primary, ),
), const Icon(
], UiIcons.chevronRight,
), size: UiConstants.space4,
) color: UiColors.primary,
else ),
Container( ],
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), ),
decoration: BoxDecoration( )
color: UiColors.primary.withOpacity(0.08), else
borderRadius: BorderRadius.circular(12), Container(
border: Border.all( padding: const EdgeInsets.symmetric(
color: UiColors.primary.withOpacity(0.2), horizontal: UiConstants.space2,
vertical: 2,
),
decoration: BoxDecoration(
color: UiColors.primary.withValues(alpha: 0.08),
borderRadius:
BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(
color: UiColors.primary.withValues(alpha: 0.2),
),
),
child: Text(
action!,
style: UiTypography.body3r.textPrimary,
),
),
],
)
: Text(
title,
style: UiTypography.body2m.textPrimary,
), ),
), ),
child: Text(
action!,
style: UiTypography.body3r.copyWith(color: UiColors.primary),
),
),
], ],
), ),
); );

View File

@@ -77,15 +77,15 @@ class _ShiftCardState extends State<ShiftCard> {
Modular.to.pushShiftDetails(widget.shift); Modular.to.pushShiftDetails(widget.shift);
}, },
child: Container( child: Container(
margin: const EdgeInsets.only(bottom: 12), margin: const EdgeInsets.only(bottom: UiConstants.space3),
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: UiColors.white,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(color: UiColors.border), border: Border.all(color: UiColors.border),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withValues(alpha: 0.05), color: UiColors.black.withValues(alpha: 0.05),
blurRadius: 2, blurRadius: 2,
offset: const Offset(0, 1), offset: const Offset(0, 1),
), ),
@@ -94,16 +94,17 @@ class _ShiftCardState extends State<ShiftCard> {
child: Row( child: Row(
children: [ children: [
Container( Container(
width: 48, width: UiConstants.space12,
height: 48, height: UiConstants.space12,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: UiColors.white,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(color: UiColors.border), border: Border.all(color: UiColors.border),
), ),
child: widget.shift.logoUrl != null child: widget.shift.logoUrl != null
? ClipRRect( ? ClipRRect(
borderRadius: BorderRadius.circular(12), borderRadius:
BorderRadius.circular(UiConstants.radiusBase),
child: Image.network( child: Image.network(
widget.shift.logoUrl!, widget.shift.logoUrl!,
fit: BoxFit.contain, fit: BoxFit.contain,
@@ -114,7 +115,7 @@ class _ShiftCardState extends State<ShiftCard> {
color: UiColors.mutedForeground, color: UiColors.mutedForeground,
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: UiConstants.space3),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -125,28 +126,18 @@ class _ShiftCardState extends State<ShiftCard> {
Flexible( Flexible(
child: Text( child: Text(
widget.shift.title, widget.shift.title,
style: const TextStyle( style: UiTypography.body1m.textPrimary,
fontWeight: FontWeight.w600,
color: UiColors.foreground,
),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
), ),
Text.rich( Text.rich(
TextSpan( TextSpan(
text: '\$${widget.shift.hourlyRate}', text: '\$${widget.shift.hourlyRate}',
style: const TextStyle( style: UiTypography.body1b.textPrimary,
fontWeight: FontWeight.bold, children: [
fontSize: 16,
color: UiColors.foreground,
),
children: const [
TextSpan( TextSpan(
text: '/h', text: '/h',
style: TextStyle( style: UiTypography.body3r,
fontWeight: FontWeight.normal,
fontSize: 12,
),
), ),
], ],
), ),
@@ -155,19 +146,13 @@ class _ShiftCardState extends State<ShiftCard> {
), ),
Text( Text(
widget.shift.clientName, widget.shift.clientName,
style: const TextStyle( style: UiTypography.body2r.textSecondary,
color: UiColors.mutedForeground,
fontSize: 13,
),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
const SizedBox(height: 4), const SizedBox(height: UiConstants.space1),
Text( Text(
'${_formatTime(widget.shift.startTime)}${widget.shift.location}', '${_formatTime(widget.shift.startTime)}${widget.shift.location}',
style: const TextStyle( style: UiTypography.body3r.textSecondary,
color: UiColors.mutedForeground,
fontSize: 12,
),
), ),
], ],
), ),
@@ -179,14 +164,14 @@ class _ShiftCardState extends State<ShiftCard> {
} }
return Container( return Container(
margin: const EdgeInsets.only(bottom: 16), margin: const EdgeInsets.only(bottom: UiConstants.space4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: UiColors.white,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(color: UiColors.border), border: Border.all(color: UiColors.border),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withValues(alpha: 0.05), color: UiColors.black.withValues(alpha: 0.05),
blurRadius: 4, blurRadius: 4,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@@ -195,7 +180,7 @@ class _ShiftCardState extends State<ShiftCard> {
child: Column( child: Column(
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(UiConstants.space5),
child: Column( child: Column(
children: [ children: [
// Header // Header
@@ -203,16 +188,17 @@ class _ShiftCardState extends State<ShiftCard> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Container( Container(
width: 56, width: UiConstants.space14,
height: 56, height: UiConstants.space14,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: UiColors.white,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(color: UiColors.border), border: Border.all(color: UiColors.border),
), ),
child: widget.shift.logoUrl != null child: widget.shift.logoUrl != null
? ClipRRect( ? ClipRRect(
borderRadius: BorderRadius.circular(12), borderRadius:
BorderRadius.circular(UiConstants.radiusBase),
child: Image.network( child: Image.network(
widget.shift.logoUrl!, widget.shift.logoUrl!,
fit: BoxFit.contain, fit: BoxFit.contain,
@@ -226,25 +212,21 @@ class _ShiftCardState extends State<ShiftCard> {
), ),
Container( Container(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 16, horizontal: UiConstants.space4,
vertical: 6, vertical: 6,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: UiColors.primary, color: UiColors.primary,
borderRadius: BorderRadius.circular(20), borderRadius: UiConstants.radiusFull,
), ),
child: Text( child: Text(
'Assigned ${_getTimeAgo(widget.shift.createdDate).replaceAll('Pending ', '')}', 'Assigned ${_getTimeAgo(widget.shift.createdDate).replaceAll('Pending ', '')}',
style: const TextStyle( style: UiTypography.body3m.white,
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w600,
),
), ),
), ),
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: UiConstants.space4),
// Title and Rate // Title and Rate
Row( Row(
@@ -257,18 +239,11 @@ class _ShiftCardState extends State<ShiftCard> {
children: [ children: [
Text( Text(
widget.shift.title, widget.shift.title,
style: const TextStyle( style: UiTypography.headline3m.textPrimary,
fontSize: 20,
fontWeight: FontWeight.bold,
color: UiColors.foreground,
),
), ),
Text( Text(
widget.shift.clientName, widget.shift.clientName,
style: const TextStyle( style: UiTypography.body2r.textSecondary,
color: UiColors.mutedForeground,
fontSize: 14,
),
), ),
], ],
), ),
@@ -276,30 +251,23 @@ class _ShiftCardState extends State<ShiftCard> {
Text.rich( Text.rich(
TextSpan( TextSpan(
text: '\$${widget.shift.hourlyRate}', text: '\$${widget.shift.hourlyRate}',
style: const TextStyle( style: UiTypography.headline3m.textPrimary,
fontWeight: FontWeight.bold, children: [
fontSize: 20,
color: UiColors.foreground,
),
children: const [
TextSpan( TextSpan(
text: '/h', text: '/h',
style: TextStyle( style: UiTypography.body1r,
fontWeight: FontWeight.normal,
fontSize: 16,
),
), ),
], ],
), ),
), ),
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: UiConstants.space4),
// Location and Date // Location and Date
Row( Row(
children: [ children: [
const Icon( Icon(
UiIcons.mapPin, UiIcons.mapPin,
size: 16, size: 16,
color: UiColors.mutedForeground, color: UiColors.mutedForeground,
@@ -308,15 +276,12 @@ class _ShiftCardState extends State<ShiftCard> {
Expanded( Expanded(
child: Text( child: Text(
widget.shift.location, widget.shift.location,
style: const TextStyle( style: UiTypography.body2r.textSecondary,
color: UiColors.mutedForeground,
fontSize: 14,
),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
), ),
const SizedBox(width: 16), const SizedBox(width: UiConstants.space4),
const Icon( Icon(
UiIcons.calendar, UiIcons.calendar,
size: 16, size: 16,
color: UiColors.mutedForeground, color: UiColors.mutedForeground,
@@ -324,14 +289,11 @@ class _ShiftCardState extends State<ShiftCard> {
const SizedBox(width: 6), const SizedBox(width: 6),
Text( Text(
'${_formatDate(widget.shift.date)}, ${_formatTime(widget.shift.startTime)}', '${_formatDate(widget.shift.date)}, ${_formatTime(widget.shift.startTime)}',
style: const TextStyle( style: UiTypography.body2r.textSecondary,
color: UiColors.mutedForeground,
fontSize: 14,
),
), ),
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: UiConstants.space4),
// Tags // Tags
Wrap( Wrap(
@@ -347,13 +309,13 @@ class _ShiftCardState extends State<ShiftCard> {
_buildTag( _buildTag(
UiIcons.timer, UiIcons.timer,
'No experience', 'No experience',
const Color(0xFFFEE2E2), UiColors.tagError,
const Color(0xFFDC2626), UiColors.textError,
), ),
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: UiConstants.space4),
], ],
), ),
), ),
@@ -361,46 +323,48 @@ class _ShiftCardState extends State<ShiftCard> {
// Actions // Actions
if (!widget.compact) if (!widget.compact)
Padding( Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space5,
),
child: Column( child: Column(
children: [ children: [
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
height: 48, height: UiConstants.space12,
child: ElevatedButton( child: ElevatedButton(
onPressed: widget.onApply, onPressed: widget.onApply,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: UiColors.foreground, backgroundColor: UiColors.primary,
foregroundColor: Colors.white, foregroundColor: UiColors.white,
elevation: 0,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), borderRadius:
BorderRadius.circular(UiConstants.radiusBase),
), ),
), ),
child: const Text( child: const Text('Accept shift'),
'Accept shift',
style: TextStyle(fontWeight: FontWeight.w600),
),
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: UiConstants.space2),
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
height: 48, height: UiConstants.space12,
child: OutlinedButton( child: OutlinedButton(
onPressed: widget.onDecline, onPressed: widget.onDecline,
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
foregroundColor: const Color(0xFFEF4444), foregroundColor: UiColors.destructive,
side: const BorderSide(color: Color(0xFFFCA5A5)), side: BorderSide(
color: UiColors.destructive.withValues(alpha: 0.3),
),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), borderRadius:
BorderRadius.circular(UiConstants.radiusBase),
), ),
), ),
child: const Text( child: const Text('Decline shift'),
'Decline shift',
style: TextStyle(fontWeight: FontWeight.w600),
),
), ),
), ),
const SizedBox(height: UiConstants.space5),
], ],
), ),
), ),
@@ -414,7 +378,7 @@ class _ShiftCardState extends State<ShiftCard> {
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration( decoration: BoxDecoration(
color: bg, color: bg,
borderRadius: BorderRadius.circular(20), borderRadius: UiConstants.radiusFull,
), ),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@@ -424,11 +388,7 @@ class _ShiftCardState extends State<ShiftCard> {
Flexible( Flexible(
child: Text( child: Text(
label, label,
style: TextStyle( style: UiTypography.body3m.copyWith(color: text),
color: text,
fontSize: 12,
fontWeight: FontWeight.w600,
),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
), ),

View File

@@ -18,29 +18,33 @@ class AutoMatchToggle extends StatefulWidget {
State<AutoMatchToggle> createState() => _AutoMatchToggleState(); State<AutoMatchToggle> createState() => _AutoMatchToggleState();
} }
class _AutoMatchToggleState extends State<AutoMatchToggle> with SingleTickerProviderStateMixin { class _AutoMatchToggleState extends State<AutoMatchToggle>
with SingleTickerProviderStateMixin {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final i18n = t.staff.home.auto_match; final i18n = t.staff.home.auto_match;
final Color primary = Theme.of(context).colorScheme.primary;
return AnimatedContainer( return AnimatedContainer(
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
gradient: widget.enabled gradient: widget.enabled
? LinearGradient( ? LinearGradient(
colors: [primary, primary.withOpacity(0.8)], colors: [
UiColors.primary,
UiColors.primary.withValues(alpha: 0.8),
],
begin: Alignment.centerLeft, begin: Alignment.centerLeft,
end: Alignment.centerRight, end: Alignment.centerRight,
) )
: null, : null,
color: widget.enabled ? null : Colors.white, color: widget.enabled ? null : UiColors.white,
border: widget.enabled ? null : Border.all(color: Colors.grey.shade200), border:
widget.enabled ? null : Border.all(color: UiColors.border),
boxShadow: widget.enabled boxShadow: widget.enabled
? [ ? [
BoxShadow( BoxShadow(
color: primary.withOpacity(0.3), color: UiColors.primary.withValues(alpha: 0.3),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
@@ -55,36 +59,39 @@ class _AutoMatchToggleState extends State<AutoMatchToggle> with SingleTickerProv
Row( Row(
children: [ children: [
Container( Container(
width: 40, width: UiConstants.space10,
height: 40, height: UiConstants.space10,
decoration: BoxDecoration( decoration: BoxDecoration(
color: widget.enabled color: widget.enabled
? Colors.white.withOpacity(0.2) ? UiColors.white.withValues(alpha: 0.2)
: primary.withOpacity(0.1), : UiColors.primary.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12), borderRadius:
BorderRadius.circular(UiConstants.radiusBase),
), ),
child: Icon( child: Icon(
UiIcons.zap, UiIcons.zap,
color: widget.enabled ? Colors.white : primary, color: widget.enabled ? UiColors.white : UiColors.primary,
size: 20, size: 20,
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: UiConstants.space3),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
i18n.title, i18n.title,
style: TextStyle( style: UiTypography.body1b.copyWith(
fontWeight: FontWeight.bold, color: widget.enabled
color: widget.enabled ? Colors.white : const Color(0xFF0F172A), ? UiColors.white
: UiColors.textPrimary,
), ),
), ),
Text( Text(
widget.enabled ? i18n.finding_shifts : i18n.get_matched, widget.enabled ? i18n.finding_shifts : i18n.get_matched,
style: TextStyle( style: UiTypography.body3r.copyWith(
fontSize: 12, color: widget.enabled
color: widget.enabled ? const Color(0xFFF8E08E) : Colors.grey.shade500, ? UiColors.accent
: UiColors.textInactive,
), ),
), ),
], ],
@@ -94,10 +101,10 @@ class _AutoMatchToggleState extends State<AutoMatchToggle> with SingleTickerProv
Switch( Switch(
value: widget.enabled, value: widget.enabled,
onChanged: widget.onToggle, onChanged: widget.onToggle,
activeThumbColor: Colors.white, activeThumbColor: UiColors.white,
activeTrackColor: Colors.white.withValues(alpha: 0.3), activeTrackColor: UiColors.white.withValues(alpha: 0.3),
inactiveThumbColor: Colors.white, inactiveThumbColor: UiColors.white,
inactiveTrackColor: Colors.grey.shade300, inactiveTrackColor: UiColors.border,
), ),
], ],
), ),
@@ -107,20 +114,19 @@ class _AutoMatchToggleState extends State<AutoMatchToggle> with SingleTickerProv
? Column( ? Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const SizedBox(height: 16), const SizedBox(height: UiConstants.space4),
Container( Container(
height: 1, height: 1,
color: Colors.white.withValues(alpha: 0.2), color: UiColors.white.withValues(alpha: 0.2),
), ),
const SizedBox(height: 16), const SizedBox(height: UiConstants.space4),
Text( Text(
i18n.matching_based_on, i18n.matching_based_on,
style: const TextStyle( style: UiTypography.body3r.copyWith(
color: Color(0xFFF8E08E), color: UiColors.accent,
fontSize: 12,
), ),
), ),
const SizedBox(height: 12), const SizedBox(height: UiConstants.space3),
Wrap( Wrap(
spacing: 8, spacing: 8,
children: [ children: [
@@ -145,17 +151,17 @@ class _AutoMatchToggleState extends State<AutoMatchToggle> with SingleTickerProv
return Container( return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.2), color: UiColors.white.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
), ),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Icon(icon, size: 12, color: Colors.white), Icon(icon, size: 12, color: UiColors.white),
const SizedBox(width: 4), const SizedBox(width: 4),
Text( Text(
label, label,
style: const TextStyle(color: Colors.white, fontSize: 12), style: UiTypography.body3r.white,
), ),
], ],
), ),

View File

@@ -16,14 +16,14 @@ class BenefitsWidget extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final i18n = t.staff.home.benefits; final i18n = t.staff.home.benefits;
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background, color: UiColors.white,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(color: Theme.of(context).dividerColor), border: Border.all(color: UiColors.border),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Theme.of(context).colorScheme.onBackground.withOpacity(0.05), color: UiColors.black.withValues(alpha: 0.05),
blurRadius: 2, blurRadius: 2,
offset: const Offset(0, 1), offset: const Offset(0, 1),
), ),
@@ -36,7 +36,7 @@ class BenefitsWidget extends StatelessWidget {
children: [ children: [
Text( Text(
i18n.title, i18n.title,
style: Theme.of(context).textTheme.titleMedium, style: UiTypography.title1m.textPrimary,
), ),
GestureDetector( GestureDetector(
onTap: () => Modular.to.pushNamed('/benefits'), onTap: () => Modular.to.pushNamed('/benefits'),
@@ -44,19 +44,19 @@ class BenefitsWidget extends StatelessWidget {
children: [ children: [
Text( Text(
i18n.view_all, i18n.view_all,
style: Theme.of(context).textTheme.labelLarge?.copyWith(color: Theme.of(context).colorScheme.primary), style: UiTypography.buttonL.textPrimary,
), ),
Icon( Icon(
UiIcons.chevronRight, UiIcons.chevronRight,
size: 16, size: UiConstants.space4,
color: Theme.of(context).colorScheme.primary, color: UiColors.primary,
), ),
], ],
), ),
), ),
], ],
), ),
const SizedBox(height: 16), const SizedBox(height: UiConstants.space4),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@@ -64,19 +64,19 @@ class BenefitsWidget extends StatelessWidget {
label: i18n.items.sick_days, label: i18n.items.sick_days,
current: 10, current: 10,
total: 40, total: 40,
color: Theme.of(context).colorScheme.primary, color: UiColors.primary,
), ),
_BenefitItem( _BenefitItem(
label: i18n.items.vacation, label: i18n.items.vacation,
current: 40, current: 40,
total: 40, total: 40,
color: Theme.of(context).colorScheme.primary, color: UiColors.primary,
), ),
_BenefitItem( _BenefitItem(
label: i18n.items.holidays, label: i18n.items.holidays,
current: 24, current: 24,
total: 24, total: 24,
color: Theme.of(context).colorScheme.primary, color: UiColors.primary,
), ),
], ],
), ),
@@ -105,13 +105,13 @@ class _BenefitItem extends StatelessWidget {
return Column( return Column(
children: [ children: [
SizedBox( SizedBox(
width: 56, width: UiConstants.space14,
height: 56, height: UiConstants.space14,
child: CustomPaint( child: CustomPaint(
painter: _CircularProgressPainter( painter: _CircularProgressPainter(
progress: current / total, progress: current / total,
color: color, color: color,
backgroundColor: const Color(0xFFE5E7EB), backgroundColor: UiColors.border,
strokeWidth: 4, strokeWidth: 4,
), ),
child: Center( child: Center(
@@ -120,32 +120,21 @@ class _BenefitItem extends StatelessWidget {
children: [ children: [
Text( Text(
'${current.toInt()}/${total.toInt()}', '${current.toInt()}/${total.toInt()}',
style: const TextStyle( style: UiTypography.body3m.textPrimary,
fontSize: 12,
fontWeight: FontWeight.bold,
color: Color(0xFF1E293B),
),
), ),
Text( Text(
i18n.hours_label, i18n.hours_label,
style: const TextStyle( style: UiTypography.footnote1r.textTertiary,
fontSize: 8,
color: Color(0xFF94A3B8),
),
), ),
], ],
), ),
), ),
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: UiConstants.space2),
Text( Text(
label, label,
style: const TextStyle( style: UiTypography.body3m.textSecondary,
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF475569),
),
), ),
], ],
); );

View File

@@ -1,3 +1,4 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'package:core_localization/core_localization.dart'; import 'package:core_localization/core_localization.dart';
@@ -32,9 +33,9 @@ class ImproveYourselfWidget extends StatelessWidget {
children: [ children: [
Text( Text(
i18n.title, i18n.title,
style: Theme.of(context).textTheme.titleMedium, style: UiTypography.title1m.textPrimary,
), ),
const SizedBox(height: 12), const SizedBox(height: UiConstants.space3),
SingleChildScrollView( SingleChildScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
clipBehavior: Clip.none, clipBehavior: Clip.none,
@@ -51,14 +52,14 @@ class ImproveYourselfWidget extends StatelessWidget {
onTap: () => Modular.to.pushNamed(item['page']!), onTap: () => Modular.to.pushNamed(item['page']!),
child: Container( child: Container(
width: 160, width: 160,
margin: const EdgeInsets.only(right: 12), margin: const EdgeInsets.only(right: UiConstants.space3),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background, color: UiColors.white,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(color: Theme.of(context).dividerColor), border: Border.all(color: UiColors.border),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Theme.of(context).colorScheme.onBackground.withOpacity(0.05), color: UiColors.black.withValues(alpha: 0.05),
blurRadius: 2, blurRadius: 2,
offset: const Offset(0, 1), offset: const Offset(0, 1),
), ),
@@ -69,36 +70,33 @@ class ImproveYourselfWidget extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox( SizedBox(
height: 96, height: UiConstants.space24,
width: double.infinity, width: double.infinity,
child: Image.network( child: Image.network(
item['image']!, item['image']!,
fit: BoxFit.cover, fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) => Container( errorBuilder: (context, error, stackTrace) => Container(
color: Theme.of(context).colorScheme.surfaceVariant, color: UiColors.background,
child: const Icon( child: Icon(
Icons.image_not_supported, UiIcons.zap,
color: Colors.grey, color: UiColors.mutedForeground,
), ),
), ),
), ),
), ),
Padding( Padding(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(UiConstants.space3),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
item['title']!, item['title']!,
style: Theme.of(context).textTheme.titleSmall, style: UiTypography.body1m.textPrimary,
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
Text( Text(
item['description']!, item['description']!,
style: const TextStyle( style: UiTypography.body3r.textSecondary,
fontSize: 12,
color: Color(0xFF64748B),
),
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),

View File

@@ -1,8 +1,8 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'package:core_localization/core_localization.dart'; import 'package:core_localization/core_localization.dart';
/// Widget for displaying more ways to use Krow, using design system tokens. /// Widget for displaying more ways to use Krow, using design system tokens.
class MoreWaysToUseKrowWidget extends StatelessWidget { class MoreWaysToUseKrowWidget extends StatelessWidget {
/// Creates a [MoreWaysToUseKrowWidget]. /// Creates a [MoreWaysToUseKrowWidget].
@@ -31,9 +31,9 @@ class MoreWaysToUseKrowWidget extends StatelessWidget {
children: [ children: [
Text( Text(
i18n.title, i18n.title,
style: Theme.of(context).textTheme.titleMedium, style: UiTypography.title1m.textPrimary,
), ),
const SizedBox(height: 12), const SizedBox(height: UiConstants.space3),
SingleChildScrollView( SingleChildScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
clipBehavior: Clip.none, clipBehavior: Clip.none,
@@ -50,14 +50,14 @@ class MoreWaysToUseKrowWidget extends StatelessWidget {
onTap: () => Modular.to.pushNamed(item['page']!), onTap: () => Modular.to.pushNamed(item['page']!),
child: Container( child: Container(
width: 160, width: 160,
margin: const EdgeInsets.only(right: 12), margin: const EdgeInsets.only(right: UiConstants.space3),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background, color: UiColors.white,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(color: Theme.of(context).dividerColor), border: Border.all(color: UiColors.border),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Theme.of(context).colorScheme.onBackground.withOpacity(0.05), color: UiColors.black.withValues(alpha: 0.05),
blurRadius: 2, blurRadius: 2,
offset: const Offset(0, 1), offset: const Offset(0, 1),
), ),
@@ -68,25 +68,25 @@ class MoreWaysToUseKrowWidget extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox( SizedBox(
height: 96, height: UiConstants.space24,
width: double.infinity, width: double.infinity,
child: Image.network( child: Image.network(
item['image']!, item['image']!,
fit: BoxFit.cover, fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) => Container( errorBuilder: (context, error, stackTrace) => Container(
color: Theme.of(context).colorScheme.surfaceVariant, color: UiColors.background,
child: const Icon( child: Icon(
Icons.image_not_supported, UiIcons.zap,
color: Colors.grey, color: UiColors.mutedForeground,
), ),
), ),
), ),
), ),
Padding( Padding(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(UiConstants.space3),
child: Text( child: Text(
item['title']!, item['title']!,
style: Theme.of(context).textTheme.titleSmall, style: UiTypography.body1m.textPrimary,
), ),
), ),
], ],