Standardize UI to design system tokens
Refactor multiple UI components to use design system tokens and primitives. Added new UiIcons (coffee, wifi, xCircle, ban) and typography color getters (primary, accent). Replaced hardcoded paddings, spacings, radii, borderRadius, and icon imports (lucide_icons -> UiIcons) with UiConstants, UiColors, UiTypography and UiIcons, and switched to UiColors.withValues for opacity. Changes apply across authentication, availability, clock_in (and its widgets), commute tracker, lunch break modal, location map placeholder, attendance card, date selector, and related presentation files to improve visual consistency.
This commit is contained in:
@@ -4,7 +4,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
|
||||
import '../bloc/clock_in_bloc.dart';
|
||||
import '../bloc/clock_in_event.dart';
|
||||
@@ -84,7 +83,9 @@ class _ClockInPageState extends State<ClockInPage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space5,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
@@ -103,12 +104,13 @@ class _ClockInPageState extends State<ClockInPage> {
|
||||
// Date Selector
|
||||
DateSelector(
|
||||
selectedDate: state.selectedDate,
|
||||
onSelect: (DateTime date) => _bloc.add(DateSelected(date)),
|
||||
onSelect: (DateTime date) =>
|
||||
_bloc.add(DateSelected(date)),
|
||||
shiftDates: <String>[
|
||||
DateFormat('yyyy-MM-dd').format(DateTime.now()),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const SizedBox(height: UiConstants.space5),
|
||||
|
||||
// Your Activity Header
|
||||
Text(
|
||||
@@ -117,7 +119,7 @@ class _ClockInPageState extends State<ClockInPage> {
|
||||
style: UiTypography.headline4m,
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
|
||||
// Selected Shift Info Card
|
||||
if (todayShifts.isNotEmpty)
|
||||
@@ -128,14 +130,16 @@ class _ClockInPageState extends State<ClockInPage> {
|
||||
onTap: () =>
|
||||
_bloc.add(ShiftSelected(shift)),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
margin:
|
||||
const EdgeInsets.only(bottom: 12),
|
||||
padding: const EdgeInsets.all(
|
||||
UiConstants.space3,
|
||||
),
|
||||
margin: const EdgeInsets.only(
|
||||
bottom: UiConstants.space3,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.white,
|
||||
borderRadius: BorderRadius.circular(
|
||||
12,
|
||||
),
|
||||
borderRadius:
|
||||
UiConstants.radiusLg,
|
||||
border: Border.all(
|
||||
color: shift.id ==
|
||||
selectedShift?.id
|
||||
@@ -211,39 +215,41 @@ class _ClockInPageState extends State<ClockInPage> {
|
||||
),
|
||||
|
||||
// Swipe To Check In / Checked Out State / No Shift State
|
||||
if (selectedShift != null && checkOutTime == null) ...<Widget>[
|
||||
if (!isCheckedIn &&
|
||||
!_isCheckInAllowed(selectedShift))
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(24),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.bgSecondary,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
const Icon(
|
||||
LucideIcons.clock,
|
||||
size: 48,
|
||||
color: UiColors.iconThird,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
"You're early!",
|
||||
style: UiTypography.body1m.textSecondary,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
"Check-in available at ${_getCheckInAvailabilityTime(selectedShift)}",
|
||||
style: UiTypography.body2r.textSecondary,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
else
|
||||
SwipeToCheckIn(
|
||||
if (selectedShift != null &&
|
||||
checkOutTime == null) ...<Widget>[
|
||||
if (!isCheckedIn &&
|
||||
!_isCheckInAllowed(selectedShift))
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding:
|
||||
const EdgeInsets.all(UiConstants.space6),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.bgSecondary,
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
const Icon(
|
||||
UiIcons.clock,
|
||||
size: 48,
|
||||
color: UiColors.iconThird,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
Text(
|
||||
"You're early!",
|
||||
style: UiTypography.body1m.textSecondary,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space1),
|
||||
Text(
|
||||
"Check-in available at ${_getCheckInAvailabilityTime(selectedShift)}",
|
||||
style: UiTypography.body2r.textSecondary,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
else
|
||||
SwipeToCheckIn(
|
||||
isCheckedIn: isCheckedIn,
|
||||
mode: state.checkInMode,
|
||||
isLoading:
|
||||
@@ -264,14 +270,17 @@ class _ClockInPageState extends State<ClockInPage> {
|
||||
onCheckOut: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) => LunchBreakDialog(
|
||||
onComplete: () {
|
||||
Navigator.of(
|
||||
context,
|
||||
).pop(); // Close dialog first
|
||||
_bloc.add(const CheckOutRequested());
|
||||
},
|
||||
),
|
||||
builder: (BuildContext context) =>
|
||||
LunchBreakDialog(
|
||||
onComplete: () {
|
||||
Navigator.of(
|
||||
context,
|
||||
).pop(); // Close dialog first
|
||||
_bloc.add(
|
||||
const CheckOutRequested(),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -279,12 +288,14 @@ class _ClockInPageState extends State<ClockInPage> {
|
||||
checkOutTime != null) ...<Widget>[
|
||||
// Shift Completed State
|
||||
Container(
|
||||
padding: const EdgeInsets.all(24),
|
||||
padding: const EdgeInsets.all(UiConstants.space6),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.tagSuccess,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
border: Border.all(
|
||||
color: UiColors.success.withValues(alpha: 0.3),
|
||||
color: UiColors.success.withValues(
|
||||
alpha: 0.3,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
@@ -297,17 +308,17 @@ class _ClockInPageState extends State<ClockInPage> {
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
LucideIcons.check,
|
||||
UiIcons.check,
|
||||
color: UiColors.textSuccess,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
const SizedBox(height: UiConstants.space3),
|
||||
Text(
|
||||
"Shift Completed!",
|
||||
style: UiTypography.body1b.textSuccess,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const SizedBox(height: UiConstants.space1),
|
||||
Text(
|
||||
"Great work today",
|
||||
style: UiTypography.body2r.textSuccess,
|
||||
@@ -319,10 +330,10 @@ class _ClockInPageState extends State<ClockInPage> {
|
||||
// No Shift State
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(24),
|
||||
padding: const EdgeInsets.all(UiConstants.space6),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.bgSecondary,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
@@ -331,7 +342,7 @@ class _ClockInPageState extends State<ClockInPage> {
|
||||
style: UiTypography.body1m.textSecondary,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const SizedBox(height: UiConstants.space1),
|
||||
Text(
|
||||
"Accept a shift to clock in",
|
||||
style: UiTypography.body2r.textSecondary,
|
||||
@@ -344,14 +355,16 @@ class _ClockInPageState extends State<ClockInPage> {
|
||||
|
||||
// Checked In Banner
|
||||
if (isCheckedIn && checkInTime != null) ...<Widget>[
|
||||
const SizedBox(height: 12),
|
||||
const SizedBox(height: UiConstants.space3),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
padding: const EdgeInsets.all(UiConstants.space3),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.tagSuccess,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
border: Border.all(
|
||||
color: UiColors.success.withValues(alpha: 0.3),
|
||||
color: UiColors.success.withValues(
|
||||
alpha: 0.3,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
@@ -382,7 +395,7 @@ class _ClockInPageState extends State<ClockInPage> {
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
LucideIcons.check,
|
||||
UiIcons.check,
|
||||
color: UiColors.textSuccess,
|
||||
),
|
||||
),
|
||||
@@ -419,10 +432,10 @@ class _ClockInPageState extends State<ClockInPage> {
|
||||
child: GestureDetector(
|
||||
onTap: () => _bloc.add(CheckInModeChanged(value)),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
padding: const EdgeInsets.symmetric(vertical: UiConstants.space2),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? UiColors.white : UiColors.transparent,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderRadius: UiConstants.radiusMd,
|
||||
boxShadow: isSelected
|
||||
? <BoxShadow>[
|
||||
BoxShadow(
|
||||
@@ -476,21 +489,23 @@ class _ClockInPageState extends State<ClockInPage> {
|
||||
width: 96,
|
||||
height: 96,
|
||||
decoration: BoxDecoration(
|
||||
color: scanned ? UiColors.tagSuccess : UiColors.tagInProgress,
|
||||
color: scanned
|
||||
? UiColors.tagSuccess
|
||||
: UiColors.tagInProgress,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
scanned ? LucideIcons.check : LucideIcons.nfc,
|
||||
scanned ? UiIcons.check : UiIcons.nfc,
|
||||
size: 48,
|
||||
color: scanned ? UiColors.textSuccess : UiColors.primary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
Text(
|
||||
scanned ? 'Processing check-in...' : 'Ready to scan',
|
||||
style: UiTypography.headline4m,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
Text(
|
||||
scanned
|
||||
? 'Please wait...'
|
||||
@@ -499,7 +514,7 @@ class _ClockInPageState extends State<ClockInPage> {
|
||||
style: UiTypography.body2r.textSecondary,
|
||||
),
|
||||
if (!scanned) ...<Widget>[
|
||||
const SizedBox(height: 24),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 56,
|
||||
@@ -520,7 +535,7 @@ class _ClockInPageState extends State<ClockInPage> {
|
||||
// But this dialog is just a function call.
|
||||
// It's safer to just return a result
|
||||
},
|
||||
icon: const Icon(LucideIcons.nfc, size: 24),
|
||||
icon: const Icon(UiIcons.nfc, size: 24),
|
||||
label: Text(
|
||||
'Tap to Scan',
|
||||
style: UiTypography.headline4m.white,
|
||||
@@ -529,7 +544,7 @@ class _ClockInPageState extends State<ClockInPage> {
|
||||
backgroundColor: UiColors.primary,
|
||||
foregroundColor: UiColors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
|
||||
enum AttendanceType { checkin, checkout, breaks, days }
|
||||
|
||||
@@ -24,10 +23,10 @@ class AttendanceCard extends StatelessWidget {
|
||||
final _AttendanceStyle styles = _getStyles(type);
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
padding: const EdgeInsets.all(UiConstants.space3),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
border: Border.all(color: UiColors.bgSecondary),
|
||||
boxShadow: <BoxShadow>[
|
||||
BoxShadow(
|
||||
@@ -46,11 +45,11 @@ class AttendanceCard extends StatelessWidget {
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: styles.bgColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderRadius: UiConstants.radiusMd,
|
||||
),
|
||||
child: Icon(styles.icon, size: 16, color: styles.iconColor),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
Text(
|
||||
title,
|
||||
style: UiTypography.titleUppercase4m.textSecondary,
|
||||
@@ -86,25 +85,25 @@ class AttendanceCard extends StatelessWidget {
|
||||
switch (type) {
|
||||
case AttendanceType.checkin:
|
||||
return _AttendanceStyle(
|
||||
icon: LucideIcons.logIn,
|
||||
icon: UiIcons.logIn,
|
||||
bgColor: UiColors.primary.withValues(alpha: 0.1),
|
||||
iconColor: UiColors.primary,
|
||||
);
|
||||
case AttendanceType.checkout:
|
||||
return _AttendanceStyle(
|
||||
icon: LucideIcons.logOut,
|
||||
icon: UiIcons.logOut,
|
||||
bgColor: UiColors.foreground.withValues(alpha: 0.1),
|
||||
iconColor: UiColors.foreground,
|
||||
);
|
||||
case AttendanceType.breaks:
|
||||
return _AttendanceStyle(
|
||||
icon: LucideIcons.coffee,
|
||||
icon: UiIcons.coffee,
|
||||
bgColor: UiColors.accent.withValues(alpha: 0.2),
|
||||
iconColor: UiColors.accentForeground,
|
||||
);
|
||||
case AttendanceType.days:
|
||||
return _AttendanceStyle(
|
||||
icon: LucideIcons.calendar,
|
||||
icon: UiIcons.calendar,
|
||||
bgColor: UiColors.success.withValues(alpha: 0.1),
|
||||
iconColor: UiColors.textSuccess,
|
||||
);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
|
||||
enum CommuteMode {
|
||||
lockedNoShift,
|
||||
@@ -158,8 +157,8 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
|
||||
Widget _buildConsentCard() {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 20),
|
||||
padding: const EdgeInsets.all(12),
|
||||
margin: const EdgeInsets.only(bottom: UiConstants.space5),
|
||||
padding: const EdgeInsets.all(UiConstants.space3),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
@@ -169,7 +168,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
UiColors.primary.withValues(alpha: 0.1),
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
boxShadow: <BoxShadow>[
|
||||
BoxShadow(
|
||||
color: UiColors.black.withValues(alpha: 0.05),
|
||||
@@ -192,12 +191,12 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
LucideIcons.mapPin,
|
||||
UiIcons.mapPin,
|
||||
size: 16,
|
||||
color: UiColors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const SizedBox(width: UiConstants.space3),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -206,7 +205,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
'Enable Commute Tracking?',
|
||||
style: UiTypography.body2m.textPrimary,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const SizedBox(height: UiConstants.space1),
|
||||
Text(
|
||||
'Share location 1hr before shift so your manager can see you\'re on the way.',
|
||||
style: UiTypography.body4r.textSecondary,
|
||||
@@ -216,7 +215,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
const SizedBox(height: UiConstants.space3),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
@@ -225,13 +224,15 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
setState(() => _localHasConsent = false);
|
||||
},
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: UiConstants.space2,
|
||||
),
|
||||
side: const BorderSide(color: UiColors.border),
|
||||
),
|
||||
child: Text('Not Now', style: UiTypography.footnote1m),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const SizedBox(width: UiConstants.space2),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
@@ -239,7 +240,9 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: UiColors.primary,
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: UiConstants.space2,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
'Enable',
|
||||
@@ -256,11 +259,11 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
|
||||
Widget _buildPreShiftCard() {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 20),
|
||||
padding: const EdgeInsets.all(12),
|
||||
margin: const EdgeInsets.only(bottom: UiConstants.space5),
|
||||
padding: const EdgeInsets.all(UiConstants.space3),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
boxShadow: <BoxShadow>[
|
||||
BoxShadow(
|
||||
color: UiColors.black.withValues(alpha: 0.05),
|
||||
@@ -279,12 +282,12 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
LucideIcons.navigation,
|
||||
UiIcons.navigation,
|
||||
size: 16,
|
||||
color: UiColors.textSecondary,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const SizedBox(width: UiConstants.space2),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -295,11 +298,11 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
'On My Way',
|
||||
style: UiTypography.body2m.textPrimary,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const SizedBox(width: UiConstants.space2),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
const Icon(
|
||||
LucideIcons.clock,
|
||||
UiIcons.clock,
|
||||
size: 12,
|
||||
color: UiColors.textInactive,
|
||||
),
|
||||
@@ -351,7 +354,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
padding: const EdgeInsets.all(UiConstants.space5),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
@@ -370,7 +373,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
LucideIcons.navigation,
|
||||
UiIcons.navigation,
|
||||
size: 48,
|
||||
color: UiColors.white,
|
||||
),
|
||||
@@ -382,12 +385,12 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
Text(
|
||||
'On My Way',
|
||||
style: UiTypography.displayMb.white,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
Text(
|
||||
'Your manager can see you\'re heading to the site',
|
||||
style: UiTypography.body2r.copyWith(
|
||||
@@ -395,15 +398,15 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
const SizedBox(height: UiConstants.space8),
|
||||
if (widget.distanceMeters != null) ...<Widget>[
|
||||
Container(
|
||||
width: double.infinity,
|
||||
constraints: const BoxConstraints(maxWidth: 300),
|
||||
padding: const EdgeInsets.all(20),
|
||||
padding: const EdgeInsets.all(UiConstants.space5),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.white.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
border: Border.all(
|
||||
color: UiColors.white.withValues(alpha: 0.2),
|
||||
),
|
||||
@@ -416,7 +419,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
color: UiColors.primaryForeground.withValues(alpha: 0.8),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const SizedBox(height: UiConstants.space1),
|
||||
Text(
|
||||
_formatDistance(widget.distanceMeters!),
|
||||
style: UiTypography.displayM.white,
|
||||
@@ -425,14 +428,14 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
),
|
||||
),
|
||||
if (widget.etaMinutes != null) ...<Widget>[
|
||||
const SizedBox(height: 12),
|
||||
const SizedBox(height: UiConstants.space3),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
constraints: const BoxConstraints(maxWidth: 300),
|
||||
padding: const EdgeInsets.all(20),
|
||||
padding: const EdgeInsets.all(UiConstants.space5),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.white.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
border: Border.all(
|
||||
color: UiColors.white.withValues(alpha: 0.2),
|
||||
),
|
||||
@@ -445,7 +448,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
color: UiColors.primaryForeground.withValues(alpha: 0.8),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const SizedBox(height: UiConstants.space1),
|
||||
Text(
|
||||
'${widget.etaMinutes} min',
|
||||
style: UiTypography.headline1m.white,
|
||||
@@ -455,7 +458,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
),
|
||||
],
|
||||
],
|
||||
const SizedBox(height: 32),
|
||||
const SizedBox(height: UiConstants.space8),
|
||||
Text(
|
||||
'Most app features are locked while commute mode is on. You\'ll be able to clock in once you arrive.',
|
||||
style: UiTypography.footnote1r.copyWith(
|
||||
@@ -469,7 +472,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
padding: const EdgeInsets.all(UiConstants.space5),
|
||||
child: OutlinedButton(
|
||||
onPressed: () {
|
||||
setState(() => _localIsCommuteOn = false);
|
||||
@@ -477,7 +480,9 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: UiColors.white,
|
||||
side: BorderSide(color: UiColors.white.withValues(alpha: 0.3)),
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: UiConstants.space4,
|
||||
),
|
||||
minimumSize: const Size(double.infinity, 48),
|
||||
),
|
||||
child: Text('Turn Off Commute Mode', style: UiTypography.buttonL),
|
||||
@@ -491,8 +496,8 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
|
||||
Widget _buildArrivedCard() {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 20),
|
||||
padding: const EdgeInsets.all(20),
|
||||
margin: const EdgeInsets.only(bottom: UiConstants.space5),
|
||||
padding: const EdgeInsets.all(UiConstants.space5),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
@@ -502,7 +507,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
UiColors.tagActive,
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
boxShadow: <BoxShadow>[
|
||||
BoxShadow(
|
||||
color: UiColors.black.withValues(alpha: 0.1),
|
||||
@@ -521,17 +526,17 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
LucideIcons.checkCircle,
|
||||
UiIcons.check,
|
||||
size: 32,
|
||||
color: UiColors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
Text(
|
||||
'You\'ve Arrived! 🎉',
|
||||
style: UiTypography.headline3m.textPrimary,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
Text(
|
||||
'You\'re at the shift location. Ready to clock in?',
|
||||
style: UiTypography.body2r.textSecondary,
|
||||
|
||||
@@ -34,10 +34,12 @@ class DateSelector extends StatelessWidget {
|
||||
onTap: () => onSelect(date),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4),
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space1,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? UiColors.primary : UiColors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
boxShadow: isSelected
|
||||
? <BoxShadow>[
|
||||
BoxShadow(
|
||||
@@ -55,7 +57,8 @@ class DateSelector extends StatelessWidget {
|
||||
DateFormat('d').format(date),
|
||||
style: UiTypography.title1m.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isSelected ? UiColors.white : UiColors.foreground,
|
||||
color:
|
||||
isSelected ? UiColors.white : UiColors.foreground,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
@@ -67,7 +70,7 @@ class DateSelector extends StatelessWidget {
|
||||
: UiColors.textInactive,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const SizedBox(height: UiConstants.space1),
|
||||
if (hasShift)
|
||||
Container(
|
||||
width: 6,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
|
||||
class LocationMapPlaceholder extends StatelessWidget {
|
||||
|
||||
@@ -19,7 +18,7 @@ class LocationMapPlaceholder extends StatelessWidget {
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.border,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
image: DecorationImage(
|
||||
image: const NetworkImage(
|
||||
'https://maps.googleapis.com/maps/api/staticmap?center=40.7128,-74.0060&zoom=15&size=600x300&maptype=roadmap&markers=color:red%7C40.7128,-74.0060&key=YOUR_API_KEY',
|
||||
@@ -37,9 +36,12 @@ class LocationMapPlaceholder extends StatelessWidget {
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
const Icon(LucideIcons.mapPin,
|
||||
size: 48, color: UiColors.iconSecondary),
|
||||
const SizedBox(height: 8),
|
||||
const Icon(
|
||||
UiIcons.mapPin,
|
||||
size: 48,
|
||||
color: UiColors.iconSecondary,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
Text('Map View (GPS)', style: UiTypography.body2r.textSecondary),
|
||||
],
|
||||
),
|
||||
@@ -47,14 +49,17 @@ class LocationMapPlaceholder extends StatelessWidget {
|
||||
|
||||
// Status Overlay
|
||||
Positioned(
|
||||
bottom: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
bottom: UiConstants.space4,
|
||||
left: UiConstants.space4,
|
||||
right: UiConstants.space4,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space4,
|
||||
vertical: UiConstants.space3,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
boxShadow: <BoxShadow>[
|
||||
BoxShadow(
|
||||
color: UiColors.black.withValues(alpha: 0.1),
|
||||
@@ -66,15 +71,13 @@ class LocationMapPlaceholder extends StatelessWidget {
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Icon(
|
||||
isVerified
|
||||
? LucideIcons.checkCircle
|
||||
: LucideIcons.alertCircle,
|
||||
isVerified ? UiIcons.checkCircle : UiIcons.warning,
|
||||
color: isVerified
|
||||
? UiColors.textSuccess
|
||||
: UiColors.destructive,
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const SizedBox(width: UiConstants.space3),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
|
||||
class LunchBreakDialog extends StatefulWidget {
|
||||
const LunchBreakDialog({super.key, required this.onComplete});
|
||||
@@ -48,7 +47,9 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
backgroundColor: UiColors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(UiConstants.space6),
|
||||
),
|
||||
child: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: _buildCurrentStep(),
|
||||
@@ -75,7 +76,7 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
|
||||
|
||||
Widget _buildStep1() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
padding: const EdgeInsets.all(UiConstants.space6),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
@@ -87,18 +88,18 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
LucideIcons.coffee,
|
||||
UiIcons.coffee,
|
||||
size: 40,
|
||||
color: UiColors.iconSecondary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
Text(
|
||||
"Did You Take\na Lunch?",
|
||||
textAlign: TextAlign.center,
|
||||
style: UiTypography.headline1m.textPrimary,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
@@ -110,10 +111,12 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: UiConstants.space4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: UiColors.border),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
color: UiColors.transparent,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
@@ -124,7 +127,7 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
const SizedBox(width: UiConstants.space4),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
@@ -135,9 +138,11 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: UiColors.primary,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: UiConstants.space4,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
@@ -156,169 +161,183 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
|
||||
Widget _buildStep2() {
|
||||
// Time input
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"When did you take lunch?",
|
||||
style: UiTypography.headline4m,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
// Mock Inputs
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: DropdownButtonFormField<String>(
|
||||
isExpanded: true,
|
||||
value: _breakStart,
|
||||
items: _timeOptions
|
||||
.map((String t) => DropdownMenuItem(
|
||||
value: t,
|
||||
child: Text(t, style: UiTypography.body3r)))
|
||||
.toList(),
|
||||
onChanged: (String? v) => setState(() => _breakStart = v),
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Start',
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 10, vertical: 8),
|
||||
padding: const EdgeInsets.all(UiConstants.space6),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"When did you take lunch?",
|
||||
style: UiTypography.headline4m,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
// Mock Inputs
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: DropdownButtonFormField<String>(
|
||||
isExpanded: true,
|
||||
value: _breakStart,
|
||||
items: _timeOptions
|
||||
.map(
|
||||
(String t) => DropdownMenuItem(
|
||||
value: t,
|
||||
child: Text(t, style: UiTypography.body3r),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (String? v) => setState(() => _breakStart = v),
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Start',
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 8,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: DropdownButtonFormField<String>(
|
||||
isExpanded: true,
|
||||
value: _breakEnd,
|
||||
items: _timeOptions
|
||||
.map((String t) => DropdownMenuItem(
|
||||
value: t,
|
||||
child: Text(t, style: UiTypography.body3r)))
|
||||
.toList(),
|
||||
onChanged: (String? v) => setState(() => _breakEnd = v),
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'End',
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 10, vertical: 8),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() => _step = 3);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: UiColors.primary,
|
||||
minimumSize: const Size(double.infinity, 48),
|
||||
),
|
||||
child: Text("Next", style: UiTypography.body1m.white),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: DropdownButtonFormField<String>(
|
||||
isExpanded: true,
|
||||
value: _breakEnd,
|
||||
items: _timeOptions
|
||||
.map(
|
||||
(String t) => DropdownMenuItem(
|
||||
value: t,
|
||||
child: Text(t, style: UiTypography.body3r),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (String? v) => setState(() => _breakEnd = v),
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'End',
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 8,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() => _step = 3);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: UiColors.primary,
|
||||
minimumSize: const Size(double.infinity, 48),
|
||||
),
|
||||
],
|
||||
));
|
||||
child: Text("Next", style: UiTypography.body1m.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStep2b() {
|
||||
// No lunch reason
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"Why didn't you take lunch?",
|
||||
style: UiTypography.headline4m,
|
||||
padding: const EdgeInsets.all(UiConstants.space6),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"Why didn't you take lunch?",
|
||||
style: UiTypography.headline4m,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
..._noLunchReasons.map(
|
||||
(String reason) => RadioListTile<String>(
|
||||
title: Text(reason, style: UiTypography.body2r),
|
||||
value: reason,
|
||||
groupValue: _noLunchReason,
|
||||
onChanged: (String? val) => setState(() => _noLunchReason = val),
|
||||
activeColor: UiColors.primary,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
..._noLunchReasons.map((String reason) => RadioListTile<String>(
|
||||
title: Text(reason, style: UiTypography.body2r),
|
||||
value: reason,
|
||||
groupValue: _noLunchReason,
|
||||
onChanged: (String? val) =>
|
||||
setState(() => _noLunchReason = val),
|
||||
activeColor: UiColors.primary,
|
||||
)),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() => _step = 3);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: UiColors.primary,
|
||||
minimumSize: const Size(double.infinity, 48),
|
||||
),
|
||||
child: Text("Next", style: UiTypography.body1m.white),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() => _step = 3);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: UiColors.primary,
|
||||
minimumSize: const Size(double.infinity, 48),
|
||||
),
|
||||
],
|
||||
));
|
||||
child: Text("Next", style: UiTypography.body1m.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStep3() {
|
||||
// Additional Notes
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"Additional Notes",
|
||||
style: UiTypography.headline4m,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
onChanged: (String v) => _additionalNotes = v,
|
||||
style: UiTypography.body2r,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Add any details...',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
maxLines: 3,
|
||||
padding: const EdgeInsets.all(UiConstants.space6),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"Additional Notes",
|
||||
style: UiTypography.headline4m,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
TextField(
|
||||
onChanged: (String v) => _additionalNotes = v,
|
||||
style: UiTypography.body2r,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Add any details...',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
maxLines: 3,
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() => _step = 4);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: UiColors.primary,
|
||||
minimumSize: const Size(double.infinity, 48),
|
||||
),
|
||||
child: Text("Submit", style: UiTypography.body1m.white),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() => _step = 4);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: UiColors.primary,
|
||||
minimumSize: const Size(double.infinity, 48),
|
||||
),
|
||||
],
|
||||
));
|
||||
child: Text("Submit", style: UiTypography.body1m.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Widget _buildStep4() {
|
||||
// Success
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
const Icon(LucideIcons.checkCircle,
|
||||
size: 64, color: UiColors.success),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
"Break Logged!",
|
||||
style: UiTypography.headline1m,
|
||||
padding: const EdgeInsets.all(UiConstants.space6),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
const Icon(UiIcons.checkCircle, size: 64, color: UiColors.success),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
Text(
|
||||
"Break Logged!",
|
||||
style: UiTypography.headline1m,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
ElevatedButton(
|
||||
onPressed: widget.onComplete,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: UiColors.primary,
|
||||
minimumSize: const Size(double.infinity, 48),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
ElevatedButton(
|
||||
onPressed: widget.onComplete,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: UiColors.primary,
|
||||
minimumSize: const Size(double.infinity, 48),
|
||||
),
|
||||
child: Text("Close", style: UiTypography.body1m.white),
|
||||
),
|
||||
],
|
||||
));
|
||||
child: Text("Close", style: UiTypography.body1m.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
|
||||
class SwipeToCheckIn extends StatefulWidget {
|
||||
|
||||
const SwipeToCheckIn({
|
||||
super.key,
|
||||
this.onCheckIn,
|
||||
@@ -73,8 +71,9 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Color baseColor =
|
||||
widget.isCheckedIn ? UiColors.success : UiColors.primary;
|
||||
final Color baseColor = widget.isCheckedIn
|
||||
? UiColors.success
|
||||
: UiColors.primary;
|
||||
|
||||
if (widget.mode == 'nfc') {
|
||||
return GestureDetector(
|
||||
@@ -93,7 +92,7 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
|
||||
height: 56,
|
||||
decoration: BoxDecoration(
|
||||
color: baseColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
boxShadow: <BoxShadow>[
|
||||
BoxShadow(
|
||||
color: baseColor.withValues(alpha: 0.4),
|
||||
@@ -106,8 +105,8 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
const Icon(LucideIcons.wifi, color: UiColors.white),
|
||||
const SizedBox(width: 12),
|
||||
const Icon(UiIcons.wifi, color: UiColors.white),
|
||||
const SizedBox(width: UiConstants.space3),
|
||||
Text(
|
||||
widget.isLoading
|
||||
? (widget.isCheckedIn
|
||||
@@ -129,10 +128,12 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
|
||||
|
||||
// Calculate background color based on drag
|
||||
final double progress = _dragValue / maxDrag;
|
||||
final Color startColor =
|
||||
widget.isCheckedIn ? UiColors.success : UiColors.primary;
|
||||
final Color endColor =
|
||||
widget.isCheckedIn ? UiColors.primary : UiColors.success;
|
||||
final Color startColor = widget.isCheckedIn
|
||||
? UiColors.success
|
||||
: UiColors.primary;
|
||||
final Color endColor = widget.isCheckedIn
|
||||
? UiColors.primary
|
||||
: UiColors.success;
|
||||
final Color currentColor =
|
||||
Color.lerp(startColor, endColor, progress) ?? startColor;
|
||||
|
||||
@@ -140,7 +141,7 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
|
||||
height: 56,
|
||||
decoration: BoxDecoration(
|
||||
color: currentColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
boxShadow: <BoxShadow>[
|
||||
BoxShadow(
|
||||
color: UiColors.black.withValues(alpha: 0.1),
|
||||
@@ -173,14 +174,16 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
|
||||
left: 4 + _dragValue,
|
||||
top: 4,
|
||||
child: GestureDetector(
|
||||
onHorizontalDragUpdate: (DragUpdateDetails d) => _onDragUpdate(d, maxWidth),
|
||||
onHorizontalDragEnd: (DragEndDetails d) => _onDragEnd(d, maxWidth),
|
||||
onHorizontalDragUpdate: (DragUpdateDetails d) =>
|
||||
_onDragUpdate(d, maxWidth),
|
||||
onHorizontalDragEnd: (DragEndDetails d) =>
|
||||
_onDragEnd(d, maxWidth),
|
||||
child: Container(
|
||||
width: _handleSize,
|
||||
height: _handleSize,
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
boxShadow: <BoxShadow>[
|
||||
BoxShadow(
|
||||
color: UiColors.black.withValues(alpha: 0.1),
|
||||
@@ -191,9 +194,7 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
_isComplete
|
||||
? LucideIcons.check
|
||||
: LucideIcons.arrowRight,
|
||||
_isComplete ? UiIcons.check : UiIcons.arrowRight,
|
||||
color: startColor,
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user