Files
doormile_milderapp/lib/views/Dashboard/pickups/pickup_verify.dart
Thiru-tenext d7348e253f Miler rider app: surface system, visible design language, backend lifecycle
Design system
- MilerSurface ladder (canvas → working → raised → floating) with MilerPanel
  as layer 1; canvas moved to #DEE3EA so white separates at 1.290:1.
- Visible vocabulary applied across Home, Deliveries, Activity, Account and
  the sheets: hero heads (tabular numeral + small caption, clamped at 1.3x),
  canvas wells for anything that opens, small filled tags for shelf labels,
  demoted placeholders. Recorded in DESIGN_SYSTEM.md §6.
- One icon family: 222 Material glyphs migrated to Lucide; none left outside
  lib/xpress.
- Colour semantics corrected: amber only for what is genuinely owed, brand red
  reserved for the live stop, disabled primaries go neutral rather than pale.

Data and lifecycle
- lib/data/lifecycle.dart reads mutations for what they prove; route_order.dart
  makes admin sequence the single ordering authority; service_day.dart, and
  stop_area.dart rewritten against live Coimbatore addresses (digit-token
  stripping, city stoplist, street suffixes, stammer collapse).
- countLabel states the load once, in bags.

Testing
- 1440 tests passing; golden shot harnesses for Home, Deliveries, Activity,
  sheets and verify, with test/failures/ now gitignored (diff debris).
- New pins: home_gutter_test, stop_area_test, plus updated structural bounds.

Note: this commit also carries pre-existing working-tree deletions that were
present before this work (API_SPEC.md, README.md, demo test fixtures).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-22 05:40:35 +05:30

910 lines
30 KiB
Dart

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'package:image_picker/image_picker.dart';
import 'package:miler/views/helpers/constants/Font_constant.dart';
import 'package:miler/views/helpers/constants/Colorconstants.dart';
import 'package:miler/views/helpers/constants/design_constants.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:miler/views/Dashboard/pickups/stop_type.dart';
import 'package:slider_button_lite/feature/presentation/slider_button/slider.dart';
import 'package:slider_button_lite/feature/presentation/slider_button/slider_button_prop.dart';
class PickupVerificationPage extends StatefulWidget {
final Map<String, dynamic> pickup;
const PickupVerificationPage({super.key, required this.pickup});
@override
State<PickupVerificationPage> createState() => _PickupVerificationPageState();
}
class _PickupVerificationPageState extends State<PickupVerificationPage>
with TickerProviderStateMixin {
File? _parcelImage;
final ImagePicker _picker = ImagePicker();
bool _isConfirming = false;
bool _photoVerified = false;
// Form controllers
final TextEditingController _weightController = TextEditingController();
final TextEditingController _descriptionController = TextEditingController();
final TextEditingController _otpController = TextEditingController();
final TextEditingController _dropAddressController = TextEditingController();
late final String _customerName;
late final String _pickupAddress;
late final String _orderId;
late final String _dropAddressDefault;
// Delivery stops (Amazon/Flipkart-style) verify differently: proof is
// OTP + photo (prepaid, no weight/signature). Pickups keep the full form.
late final bool _isDelivery;
late AnimationController _fadeController;
late Animation<double> _fadeAnim;
// Red Theme Colors
static Color get _primaryRed => ColorConstants.primary;
static Color get _darkRed => ColorConstants.primary;
static Color get _lightRed => ColorConstants.errorLight;
static Color get _successGreen => ColorConstants.acceptGreen;
@override
void initState() {
super.initState();
final d = widget.pickup;
_customerName = (d['PickupCustomer'] ?? d['pickupcustomer'] ?? 'Customer')
.toString();
_pickupAddress =
(d['pickupaddress'] ?? d['PickupAddress'] ?? 'Pickup Location')
.toString();
_orderId = (d['orderid'] ?? '').toString();
_dropAddressDefault =
(d['DropAddress'] ?? d['dropaddress'] ?? 'Drop Location').toString();
_dropAddressController.text = _dropAddressDefault;
_isDelivery = stopKindOf(d).isDelivery;
_fadeController = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_fadeAnim = CurvedAnimation(parent: _fadeController, curve: Curves.easeOut);
_fadeController.forward();
}
@override
void dispose() {
_weightController.dispose();
_descriptionController.dispose();
_otpController.dispose();
_dropAddressController.dispose();
_fadeController.dispose();
super.dispose();
}
Future<void> _takeParcelPhoto() async {
try {
final XFile? photo = await _picker.pickImage(
source: ImageSource.camera,
imageQuality: 85,
);
if (photo != null && mounted) {
setState(() {
_parcelImage = File(photo.path);
_photoVerified = true;
});
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Failed to capture photo')),
);
}
}
}
// Required proof per stop is kept light: Photo + Code (pickup) / Photo + OTP
// (delivery). Weight is optional — no keyboard entry needed at every doorstep.
bool get _isFormValid => _isDelivery
? (_photoVerified && _otpController.text.trim().isNotEmpty)
: (_photoVerified && _otpController.text.trim().isNotEmpty);
void _confirmPickup() {
if (!_isFormValid) {
String msg = 'Please complete all required fields';
if (!_photoVerified) {
msg = _isDelivery
? 'Take a photo of the delivered parcel first'
: 'Take a photo of the parcel first';
} else if (_isDelivery && _otpController.text.trim().isEmpty) {
msg = 'Enter the delivery OTP from the customer';
} else if (!_isDelivery && _otpController.text.trim().isEmpty) {
msg = 'Enter the pickup code from the customer';
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(msg),
backgroundColor: _primaryRed,
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(DesignConstants.radiusLg),
),
),
);
return;
}
setState(() => _isConfirming = true);
final verified = {
'verified': true,
'parcelImage': _parcelImage?.path,
'weight': _weightController.text.trim(),
'description': _descriptionController.text.trim(),
'dropAddress': _dropAddressController.text.trim(),
'otp': _otpController.text.trim(),
};
debugPrint(
'[PICKUP_VERIFY] Confirming pickup and popping with verified data',
);
// Pop with verified data
Navigator.pop(context, verified);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: ColorConstants.daylightSurface,
appBar: _buildAppBar(),
body: FadeTransition(
opacity: _fadeAnim,
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
padding: EdgeInsets.fromLTRB(16.w, 12.h, 16.w, 32.h),
child: SizedBox(
width: MediaQuery.of(context).size.width - 32.w,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildProgressSteps(),
SizedBox(height: 20.h),
_buildCustomerCard(),
SizedBox(height: 20.h),
_buildSectionHeader(
icon: LucideIcons.camera,
title: _isDelivery ? 'Delivery Photo' : 'Parcel Photo',
badge: 'Required',
isComplete: _photoVerified,
),
SizedBox(height: 10.h),
_buildPhotoCapture(),
SizedBox(height: 20.h),
// Weight is pickup-only. Deliveries are prepaid and proven by
// OTP + photo. Signature was dropped — photo is enough proof for
// a first-mile pickup, so it was pure friction on every stop.
if (!_isDelivery) ...[
_buildSectionHeader(
icon: LucideIcons.package,
title: 'Parcel Details (Optional)',
isComplete: _weightController.text.isNotEmpty,
),
SizedBox(height: 10.h),
_buildParcelDetailsCard(),
SizedBox(height: 20.h),
],
_buildSectionHeader(
icon: LucideIcons.lock,
title: _isDelivery ? 'Delivery OTP' : 'Pickup Code',
badge: 'Required',
isComplete: _otpController.text.isNotEmpty,
),
SizedBox(height: 10.h),
_buildCard([
TextField(
controller: _otpController,
keyboardType: TextInputType.number,
maxLength: 6,
onChanged: (_) => setState(() {}),
decoration:
_inputDecoration(
_isDelivery
? 'Enter OTP from customer'
: 'Enter code from customer',
).copyWith(
counterText: '',
prefixIcon: Icon(
LucideIcons.lock,
color: ColorConstants.secondaryText,
size: 20.sp,
),
),
style: TextStyle(
fontSize: 15.sp,
fontFamily: FontConstants.fontFamily,
),
),
]),
SizedBox(height: 32.h),
_buildConfirmSlider(),
SizedBox(height: 20.h),
],
),
),
),
),
);
}
AppBar _buildAppBar() {
return AppBar(
backgroundColor: ColorConstants.pureSurface,
elevation: 0,
centerTitle: false,
surfaceTintColor: Colors.transparent,
// 56 → 68, the same air the tab bars gained. Height rather than a title
// inset so the back arrow moves down with the heading instead of the two
// drifting apart.
toolbarHeight: 68,
leading: IconButton(
tooltip: 'Back',
icon: Icon(
LucideIcons.chevronLeft,
color: ColorConstants.onSurface,
size: 20.sp,
),
onPressed: () => Navigator.pop(context),
),
title: Row(
children: [
// No brand mark. It is gone from every bar in the app: on a pushed
// sub-page the back arrow beside it already says where the rider is,
// and the logo was repeating what the screen he came from said.
Text(
_isDelivery ? 'Verify Delivery' : 'Verify Pickup',
style: TextStyle(
fontSize: 21.sp,
color: ColorConstants.onSurface,
fontWeight: FontWeight.w700,
fontFamily: FontConstants.fontFamily,
),
),
],
),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1),
child: Container(height: 1, color: ColorConstants.dividerColor),
),
);
}
Widget _buildProgressSteps() {
final steps = _isDelivery
? [
{'label': 'Photo', 'done': _photoVerified},
{'label': 'OTP', 'done': _otpController.text.isNotEmpty},
]
: [
{'label': 'Photo', 'done': _photoVerified},
{'label': 'Code', 'done': _otpController.text.isNotEmpty},
];
return Container(
padding: EdgeInsets.symmetric(horizontal: 4.w, vertical: 12.h),
decoration: BoxDecoration(
color: ColorConstants.pureSurface,
borderRadius: BorderRadius.circular(DesignConstants.radiusXl),
border: Border.all(color: ColorConstants.dividerColor),
),
child: Row(
children: List.generate(steps.length * 2 - 1, (i) {
if (i.isOdd) {
final leftDone = steps[i ~/ 2]['done'] as bool;
return Expanded(
child: Container(
height: 2.h,
margin: EdgeInsets.symmetric(horizontal: 4.w),
decoration: BoxDecoration(
color: leftDone ? _successGreen : ColorConstants.dividerColor,
borderRadius: BorderRadius.circular(DesignConstants.radiusLg),
),
),
);
}
final idx = i ~/ 2;
final step = steps[idx];
final done = step['done'] as bool;
final label = step['label'] as String;
return Expanded(
child: Column(
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 300),
width: 32.w,
height: 32.w,
decoration: BoxDecoration(
color: done ? _successGreen : ColorConstants.neutralLight,
shape: BoxShape.circle,
border: Border.all(
color: done ? _successGreen : ColorConstants.dividerColor,
width: 2,
),
),
child: Icon(
done ? LucideIcons.check : LucideIcons.circle,
color: done ? Colors.white : ColorConstants.secondaryText,
size: 16.sp,
),
),
SizedBox(height: 4.h),
Text(
label,
style: TextStyle(
fontSize: 10.sp,
fontWeight: done ? FontWeight.w600 : FontWeight.w500,
color: done
? _successGreen
: ColorConstants.onSurfaceVariant,
fontFamily: FontConstants.fontFamily,
),
),
],
),
);
}),
),
);
}
Widget _buildCustomerCard() {
final initials = _customerName.trim().isNotEmpty
? _customerName
.trim()
.split(' ')
.map((w) => w.isNotEmpty ? w[0] : '')
.take(2)
.join()
.toUpperCase()
: 'C';
return Container(
padding: EdgeInsets.all(16.r),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [_primaryRed, _darkRed],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(DesignConstants.radiusXl),
boxShadow: [
BoxShadow(
color: _primaryRed.withValues(alpha: 0.25),
blurRadius: 16,
offset: const Offset(0, 6),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 44.w,
height: 44.w,
decoration: BoxDecoration(
color: ColorConstants.pureSurface.withValues(alpha: 0.2),
shape: BoxShape.circle,
border: Border.all(
color: Colors.white.withValues(alpha: 0.4),
),
),
child: Center(
child: Text(
initials,
style: TextStyle(
color: Colors.white,
fontSize: 16.sp,
fontWeight: FontWeight.w700,
fontFamily: FontConstants.fontFamily,
),
),
),
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_customerName,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w700,
color: Colors.white,
fontFamily: FontConstants.fontFamily,
),
),
SizedBox(height: 2.h),
Text(
'Booking #$_orderId',
style: TextStyle(
fontSize: 12.sp,
color: Colors.white.withValues(alpha: 0.8),
fontFamily: FontConstants.fontFamily,
),
),
],
),
),
],
),
SizedBox(height: 12.h),
Container(
height: 1,
color: ColorConstants.pureSurface.withValues(alpha: 0.2),
),
SizedBox(height: 12.h),
_buildLocationRow(
LucideIcons.mapPin,
_pickupAddress,
_isDelivery ? 'DELIVERY' : 'PICKUP',
ColorConstants.warning.withValues(alpha: 0.40),
),
],
),
);
}
Widget _buildLocationRow(
IconData icon,
String address,
String label,
Color accentColor,
) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.all(5.r),
decoration: BoxDecoration(
color: ColorConstants.pureSurface.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(DesignConstants.radiusLg),
),
child: Icon(icon, size: 14.sp, color: accentColor),
),
SizedBox(width: 10.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(
fontSize: 9.sp,
fontWeight: FontWeight.w600,
color: accentColor,
letterSpacing: 0.8,
fontFamily: FontConstants.fontFamily,
),
),
SizedBox(height: 1.h),
Text(
address,
style: TextStyle(
fontSize: 12.sp,
color: Colors.white.withValues(alpha: 0.9),
fontFamily: FontConstants.fontFamily,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
],
);
}
Widget _buildSectionHeader({
required IconData icon,
required String title,
String? badge,
bool isComplete = false,
}) {
return Row(
children: [
Container(
padding: EdgeInsets.all(7.r),
decoration: BoxDecoration(
color: isComplete
? ColorConstants.successLight
: ColorConstants.neutralLight,
borderRadius: BorderRadius.circular(DesignConstants.radiusLg),
),
child: Icon(
isComplete ? LucideIcons.circleCheck : icon,
size: 16.sp,
color: isComplete ? _successGreen : ColorConstants.onSurfaceVariant,
),
),
SizedBox(width: 10.w),
Expanded(
child: Text(
title,
style: TextStyle(
fontSize: 15.sp,
fontWeight: FontWeight.w600,
color: ColorConstants.onSurface,
fontFamily: FontConstants.fontFamily,
),
),
),
if (badge != null)
Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 3.h),
decoration: BoxDecoration(
color: _lightRed,
borderRadius: BorderRadius.circular(DesignConstants.radiusLg),
),
child: Text(
badge,
style: TextStyle(
fontSize: 10.sp,
fontWeight: FontWeight.w600,
color: _primaryRed,
fontFamily: FontConstants.fontFamily,
),
),
),
],
);
}
Widget _buildPhotoCapture() {
return GestureDetector(
onTap: _takeParcelPhoto,
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
width: double.infinity,
height: 180.h,
decoration: BoxDecoration(
color: _photoVerified
? Colors.transparent
: ColorConstants.pureSurface,
borderRadius: BorderRadius.circular(DesignConstants.radiusXl),
border: Border.all(
color: _photoVerified ? _successGreen : ColorConstants.borderSubtle,
width: _photoVerified ? 2 : 1,
),
boxShadow: _photoVerified
? [
BoxShadow(
color: _successGreen.withValues(alpha: 0.1),
blurRadius: 12,
offset: const Offset(0, 4),
),
]
: [],
),
child: _parcelImage != null
? ClipRRect(
borderRadius: BorderRadius.circular(DesignConstants.radiusXl),
child: Stack(
fit: StackFit.expand,
children: [
Image.file(_parcelImage!, fit: BoxFit.cover),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 14.w,
vertical: 10.h,
),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Colors.black.withValues(alpha: 0.6),
Colors.transparent,
],
),
),
child: Row(
children: [
Icon(
LucideIcons.circleCheck,
color: Colors.white,
size: 16.sp,
),
SizedBox(width: 6.w),
Text(
'Photo captured — tap to retake',
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
fontFamily: FontConstants.fontFamily,
),
),
],
),
),
),
Positioned(
top: 10.h,
right: 10.w,
child: Container(
padding: EdgeInsets.all(6.r),
decoration: BoxDecoration(
color: _successGreen,
shape: BoxShape.circle,
),
child: Icon(
LucideIcons.check,
color: Colors.white,
size: 14.sp,
),
),
),
],
),
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.all(18.r),
decoration: BoxDecoration(
color: ColorConstants.neutralLight,
shape: BoxShape.circle,
),
child: Icon(
LucideIcons.camera,
color: ColorConstants.secondaryText,
size: 36.sp,
),
),
SizedBox(height: 10.h),
Text(
'Tap to take parcel photo',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w500,
color: ColorConstants.onSurfaceVariant,
fontFamily: FontConstants.fontFamily,
),
),
SizedBox(height: 4.h),
Text(
'Required for pickup confirmation',
style: TextStyle(
fontSize: 11.sp,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
),
),
],
),
),
);
}
Widget _buildParcelDetailsCard() {
return _buildCard([
_fieldLabel('Weight (kg)'),
SizedBox(height: 6.h),
TextField(
controller: _weightController,
keyboardType: const TextInputType.numberWithOptions(decimal: true),
onChanged: (_) => setState(() {}),
decoration: _inputDecoration('e.g. 2.5').copyWith(
suffixText: 'kg',
suffixStyle: TextStyle(
fontSize: 13.sp,
color: ColorConstants.onSurfaceVariant,
fontFamily: FontConstants.fontFamily,
),
),
style: TextStyle(fontSize: 15.sp, fontFamily: FontConstants.fontFamily),
),
SizedBox(height: 16.h),
_fieldLabel('Description (Optional)'),
SizedBox(height: 6.h),
TextField(
controller: _descriptionController,
maxLines: 3,
decoration: _inputDecoration('Item description, fragile, etc.'),
style: TextStyle(fontSize: 15.sp, fontFamily: FontConstants.fontFamily),
),
SizedBox(height: 16.h),
]);
}
Widget _fieldLabel(String label, {bool required = false}) {
return Row(
children: [
Text(
label,
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w500,
color: ColorConstants.onSurface,
fontFamily: FontConstants.fontFamily,
),
),
if (required) ...[
SizedBox(width: 4.w),
Text(
'*',
style: TextStyle(
fontSize: 13.sp,
color: _primaryRed,
fontWeight: FontWeight.w600,
fontFamily: FontConstants.fontFamily,
),
),
],
],
);
}
Widget _buildConfirmSlider() {
if (_isConfirming) {
return Container(
height: ButtonSizes.primary,
decoration: BoxDecoration(
color: ColorConstants.primary,
borderRadius: BorderRadius.circular(DesignConstants.radiusXl),
),
child: const Center(
child: SizedBox(
width: 22,
height: 22,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
),
),
);
}
if (!_isFormValid) {
return Container(
height: ButtonSizes.primary,
decoration: BoxDecoration(
color: ColorConstants.dividerColor,
borderRadius: BorderRadius.circular(DesignConstants.radiusXl),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
LucideIcons.lock,
color: ColorConstants.secondaryText,
size: 18.sp,
),
SizedBox(width: 8.w),
Text(
!_photoVerified
? 'Take a photo to continue'
: _otpController.text.isEmpty
? (_isDelivery
? 'Enter OTP to continue'
: 'Enter pickup code to continue')
: 'Complete all fields to continue',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w500,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
),
),
],
),
);
}
return SliderButton(
properties: SliderButtonProperties(
height: ButtonSizes.primary,
buttonSize: 48,
width: MediaQuery.of(context).size.width - 32.w,
backgroundColor: ColorConstants.primary,
dismissThresholds: 0.85,
action: () async {
debugPrint(
'[PICKUP_VERIFY_SLIDER] Action called, form valid: $_isFormValid',
);
if (!_isFormValid) {
debugPrint(
'[PICKUP_VERIFY_SLIDER] Form not valid, returning false',
);
return false;
}
_confirmPickup();
debugPrint(
'[PICKUP_VERIFY_SLIDER] _confirmPickup called, returning true',
);
return true;
},
label: Text(
'Slide to Confirm Pickup',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15.sp,
fontWeight: FontWeight.w600,
color: Colors.white,
fontFamily: FontConstants.fontFamily,
),
),
icon: Container(
margin: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: ColorConstants.pureSurface,
shape: BoxShape.circle,
),
child: Icon(
LucideIcons.check,
size: 22.sp,
color: ColorConstants.primary,
),
),
),
);
}
Widget _buildCard(List<Widget> children) {
return Container(
width: double.infinity,
padding: EdgeInsets.all(16.r),
decoration: BoxDecoration(
color: ColorConstants.pureSurface,
borderRadius: BorderRadius.circular(DesignConstants.radiusXl),
border: Border.all(color: ColorConstants.borderSubtle),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: children,
),
);
}
InputDecoration _inputDecoration(String hint) {
return InputDecoration(
hintText: hint,
hintStyle: TextStyle(
fontSize: 14.sp,
color: ColorConstants.secondaryText,
fontFamily: FontConstants.fontFamily,
),
filled: true,
fillColor: ColorConstants.daylightSurface,
contentPadding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 12.h),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(DesignConstants.radiusLg),
borderSide: BorderSide(color: ColorConstants.borderStrong),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(DesignConstants.radiusLg),
borderSide: BorderSide(color: ColorConstants.borderStrong),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(DesignConstants.radiusLg),
borderSide: BorderSide(color: ColorConstants.primary, width: 1.5),
),
);
}
}