Files
tripz-llc/apps/rider/lib/features/home/view/destination_screen.dart
T

1042 lines
32 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:intaleq_maps/intaleq_maps.dart';
import '../../../core/l10n/app_localizations.dart';
import '../../../core/design/tokens.dart';
import '../../../core/design/tripz_colors.dart';
import '../../../core/di.dart';
import '../cubit/destination_cubit.dart';
import '../cubit/home_cubit.dart';
import '../data/maps_repository.dart';
import '../data/models/place_model.dart';
const int _maxStops = 2;
class DestinationScreen extends StatefulWidget {
final LatLng? currentLocation;
const DestinationScreen({super.key, this.currentLocation});
@override
State<DestinationScreen> createState() => _DestinationScreenState();
}
class _DestinationScreenState extends State<DestinationScreen> {
final _destController = TextEditingController();
final _destFocusNode = FocusNode();
final _stopControllers = <TextEditingController>[];
final _stopFocusNodes = <FocusNode>[];
int _editingStopIndex = -1;
String? _pickupAddressName;
bool _forSelf = true;
@override
void initState() {
super.initState();
_destFocusNode.requestFocus();
_loadReverseGeocodePickup();
}
Future<void> _loadReverseGeocodePickup() async {
final loc = widget.currentLocation;
if (loc != null) {
try {
final repo = getIt<MapsRepository>();
final place = await repo.reverseGeocode(loc.latitude, loc.longitude);
if (mounted && place.name.isNotEmpty) {
setState(() {
_pickupAddressName = place.name;
});
}
} catch (_) {}
}
}
void _swapLocations() {
setState(() {
final oldDest = _destController.text;
if (_stopControllers.isNotEmpty) {
_destController.text = _stopControllers.first.text;
_stopControllers.first.text = oldDest;
} else if (_pickupAddressName != null && _pickupAddressName!.isNotEmpty) {
_destController.text = _pickupAddressName!;
_pickupAddressName = oldDest.isNotEmpty ? oldDest : _pickupAddressName;
}
});
}
void _showAiVoiceSearchDialog(BuildContext context) {
final voiceController = TextEditingController();
final l10n = AppLocalizations.of(context)!;
final cs = Theme.of(context).colorScheme;
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (modalCtx) {
return Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(modalCtx).viewInsets.bottom + T.s24,
top: T.s24,
left: T.pagePadding,
right: T.pagePadding,
),
decoration: BoxDecoration(
color: cs.surface,
borderRadius:
const BorderRadius.vertical(top: Radius.circular(T.r24)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 64,
height: 64,
decoration: BoxDecoration(
color: cs.primaryContainer,
shape: BoxShape.circle,
),
child: Icon(Icons.mic_rounded,
color: cs.onPrimaryContainer, size: 36),
),
const SizedBox(height: T.s16),
Text(
'البحث الصوتي الذكي (AI Voice)',
style: Theme.of(modalCtx).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: T.s8),
Text(
'قل وجهتك بصوتك أو اكتب وصف المكان (مثل: مجمع الأبحاث، الشميساني)',
textAlign: TextAlign.center,
style: Theme.of(modalCtx).textTheme.bodyMedium?.copyWith(
color: cs.onSurfaceVariant,
),
),
const SizedBox(height: T.s16),
TextField(
controller: voiceController,
autofocus: true,
decoration: InputDecoration(
hintText: 'تحدث أو اكتب المكان...',
prefixIcon: const Icon(Icons.graphic_eq_rounded),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(T.r12),
),
),
onSubmitted: (text) {
Navigator.pop(modalCtx);
if (text.trim().isNotEmpty) {
_destController.text = text;
final loc = widget.currentLocation;
context.read<DestinationCubit>().searchVoiceDirectly(
text,
lat: loc?.latitude,
lng: loc?.longitude,
);
}
},
),
const SizedBox(height: T.s16),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () => Navigator.pop(modalCtx),
child: Text(l10n.cancel),
),
const SizedBox(width: T.s8),
ElevatedButton.icon(
onPressed: () {
final text = voiceController.text;
Navigator.pop(modalCtx);
if (text.trim().isNotEmpty) {
_destController.text = text;
final loc = widget.currentLocation;
context.read<DestinationCubit>().searchVoiceDirectly(
text,
lat: loc?.latitude,
lng: loc?.longitude,
);
}
},
icon: const Icon(Icons.search_rounded),
label: const Text('بحث بالذكاء الاصطناعي'),
),
],
),
],
),
);
},
);
}
@override
void dispose() {
_destController.dispose();
_destFocusNode.dispose();
for (final c in _stopControllers) {
c.dispose();
}
for (final f in _stopFocusNodes) {
f.dispose();
}
super.dispose();
}
void _addStop() {
if (_stopControllers.length >= _maxStops) return;
setState(() {
_stopControllers.add(TextEditingController());
_stopFocusNodes.add(FocusNode());
_editingStopIndex = _stopControllers.length - 1;
});
_stopFocusNodes.last.requestFocus();
}
void _removeStop(int index) {
setState(() {
_stopControllers[index].dispose();
_stopFocusNodes[index].dispose();
_stopControllers.removeAt(index);
_stopFocusNodes.removeAt(index);
if (_editingStopIndex == index) {
_editingStopIndex = -1;
} else if (_editingStopIndex > index) {
_editingStopIndex--;
}
});
}
void _selectPlace(dynamic result) {
context.pop(result);
}
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
final theme = Theme.of(context);
final cs = theme.colorScheme;
final tripz = context.tripzColors;
return Scaffold(
backgroundColor: Colors.transparent,
extendBodyBehindAppBar: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
leading: IconButton(
onPressed: () => context.pop(),
icon: const Icon(Icons.arrow_forward),
color: cs.onSurface,
style: IconButton.styleFrom(
backgroundColor: cs.surface,
shadowColor: Colors.black12,
elevation: 2,
),
),
title: Text(
l10n.selectDestination,
style: theme.textTheme.titleMedium,
),
centerTitle: true,
),
body: Stack(
children: [
// Map background
Positioned.fill(
child: Container(
color: cs.surfaceContainerHighest,
child: Opacity(
opacity: 0.5,
child: Image.asset(
'assets/images/destination_selection_img.png',
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => const SizedBox.shrink(),
),
),
),
),
// Map overlay gradient
Positioned.fill(
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.black12, Colors.black38],
),
),
),
),
// Draggable sheet
Positioned.fill(
child: DraggableScrollableSheet(
initialChildSize: 0.55,
minChildSize: 0.15,
maxChildSize: 0.88,
snap: true,
snapSizes: const [0.15, 0.55, 0.88],
builder: (context, scrollController) {
return Container(
decoration: BoxDecoration(
color: cs.surface,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(T.r24)),
boxShadow: const [T.shadowLg],
),
child: Column(
children: [
// Drag handle
Center(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: T.s12),
child: Container(
width: 48,
height: 4,
decoration: BoxDecoration(
color: cs.outlineVariant,
borderRadius: BorderRadius.circular(2),
),
),
),
),
Expanded(
child: ListView(
controller: scrollController,
padding: EdgeInsets.zero,
children: [
_buildFieldsSection(
context, l10n, theme, cs, tripz),
const SizedBox(height: T.s16),
_buildSavedPlaces(context, theme, cs),
const SizedBox(height: T.s8),
_buildAutocompleteResults(context, l10n, theme, cs),
_buildPickFromMap(context, l10n, cs),
const SizedBox(height: T.s32),
],
),
),
],
),
);
},
),
),
],
),
);
}
Widget _buildFieldsSection(
BuildContext context,
AppLocalizations l10n,
ThemeData theme,
ColorScheme cs,
TripzColors tripz,
) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: T.pagePadding),
child: Column(
children: [
// Rider selection chip — "For me / For someone else"
Row(
children: [
FilterChip(
selected: _forSelf,
label: const Text('طلب لنفسي'),
avatar: const Icon(Icons.person_rounded, size: 16),
onSelected: (val) => setState(() => _forSelf = true),
),
const SizedBox(width: T.s8),
FilterChip(
selected: !_forSelf,
label: const Text('طلب لشخص آخر'),
avatar: const Icon(Icons.people_rounded, size: 16),
onSelected: (val) => setState(() => _forSelf = false),
),
const Spacer(),
IconButton(
onPressed: _swapLocations,
icon: const Icon(Icons.swap_vert_rounded),
tooltip: 'تبديل الأماكن',
color: cs.primary,
),
],
),
const SizedBox(height: T.s8),
// Origin — green dot, reverse geocoded
_OriginRow(
l10n: l10n,
theme: theme,
cs: cs,
tripz: tripz,
addressName: _pickupAddressName,
),
// Stops
for (var i = 0; i < _stopControllers.length; i++)
_StopRow(
index: i,
controller: _stopControllers[i],
focusNode: _stopFocusNodes[i],
isEditing: _editingStopIndex == i,
l10n: l10n,
theme: theme,
cs: cs,
tripz: tripz,
onStopTapped: () {
setState(() => _editingStopIndex = i);
context.read<DestinationCubit>().onQueryChanged('');
_stopFocusNodes[i].requestFocus();
},
onStopChanged: (v) =>
context.read<DestinationCubit>().onQueryChanged(v),
onStopRemoved: () => _removeStop(i),
),
// Dotted connector between last stop / origin and destination
_DottedConnector(color: cs.outlineVariant),
// Destination — red square, active with AI Voice Search button
_DestinationRow(
controller: _destController,
focusNode: _destFocusNode,
isEditing: _editingStopIndex == -1,
l10n: l10n,
theme: theme,
cs: cs,
tripz: tripz,
onFieldTapped: () {
setState(() => _editingStopIndex = -1);
context.read<DestinationCubit>().onQueryChanged('');
_destFocusNode.requestFocus();
},
onChanged: (v) =>
context.read<DestinationCubit>().onQueryChanged(v),
onVoiceSearchPressed: () => _showAiVoiceSearchDialog(context),
),
const SizedBox(height: T.s8),
// Add stop button
if (_stopControllers.length < _maxStops)
Align(
alignment: Alignment.centerRight,
child: TextButton.icon(
onPressed: _addStop,
icon: const Icon(Icons.add, size: 18),
label: Text(l10n.addStop),
style: TextButton.styleFrom(
foregroundColor: cs.primary,
padding: const EdgeInsets.symmetric(vertical: T.s8),
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
),
),
const SizedBox(height: T.s12),
Divider(color: cs.outlineVariant.withValues(alpha: 0.3)),
const SizedBox(height: T.s12),
],
),
);
}
Widget _buildSavedPlaces(
BuildContext context,
ThemeData theme,
ColorScheme cs,
) {
final l10n = AppLocalizations.of(context)!;
return SizedBox(
height: 52,
child: ListView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: T.pagePadding),
children: [
_SavedPlaceChip(
icon: Icons.home_rounded,
label: l10n.homePlace,
onTap: () {},
),
const SizedBox(width: T.s12),
_SavedPlaceChip(
icon: Icons.work_rounded,
label: l10n.workPlace,
onTap: () {},
),
const SizedBox(width: T.s12),
_SavedPlaceChip(
icon: Icons.star_rounded,
label: l10n.favorites,
onTap: () {},
),
],
),
);
}
Widget _buildAutocompleteResults(
BuildContext context,
AppLocalizations l10n,
ThemeData theme,
ColorScheme cs,
) {
return BlocBuilder<DestinationCubit, DestinationState>(
builder: (context, state) {
if (state.searching) {
return const Padding(
padding: EdgeInsets.symmetric(vertical: T.s24),
child: Center(child: CircularProgressIndicator()),
);
}
if (state.results.isEmpty && state.query.length >= 2) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: T.s24),
child: Center(
child: Text(l10n.noResults, style: theme.textTheme.bodyLarge),
),
);
}
return Column(
children: [
for (var i = 0; i < state.results.length; i++)
_SearchResultTile(
place: state.results[i],
onTap: () => _selectPlace(state.results[i]),
),
],
);
},
);
}
Widget _buildPickFromMap(
BuildContext context,
AppLocalizations l10n,
ColorScheme cs,
) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: T.pagePadding,
vertical: T.s8,
),
child: _PickFromMapTile(
onTap: () => _selectPlace({'pickFromMap': true}),
),
);
}
}
// ── Origin row ──
class _OriginRow extends StatelessWidget {
final AppLocalizations l10n;
final ThemeData theme;
final ColorScheme cs;
final TripzColors tripz;
final String? addressName;
const _OriginRow({
required this.l10n,
required this.theme,
required this.cs,
required this.tripz,
this.addressName,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: T.s4),
child: Row(
children: [
SizedBox(
width: 48,
child: Center(
child: Container(
width: 12,
height: 12,
decoration: BoxDecoration(
color: tripz.success,
shape: BoxShape.circle,
border: Border.all(
color: tripz.success.withValues(alpha: 0.25),
width: 4,
),
),
),
),
),
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: T.s12,
vertical: T.s12,
),
decoration: BoxDecoration(
color: cs.surfaceContainerLow,
borderRadius: BorderRadius.circular(T.r12),
border: Border.all(
color: cs.outlineVariant.withValues(alpha: 0.3),
),
),
child: Text(
(addressName != null && addressName!.isNotEmpty)
? addressName!
: l10n.pickupLocation,
style: theme.textTheme.bodyLarge?.copyWith(
color: (addressName != null && addressName!.isNotEmpty)
? cs.onSurface
: cs.onSurfaceVariant,
fontWeight: (addressName != null && addressName!.isNotEmpty)
? FontWeight.w600
: FontWeight.normal,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
),
],
),
);
}
}
// ── Stop row ──
class _StopRow extends StatelessWidget {
final int index;
final TextEditingController controller;
final FocusNode focusNode;
final bool isEditing;
final AppLocalizations l10n;
final ThemeData theme;
final ColorScheme cs;
final TripzColors tripz;
final VoidCallback onStopTapped;
final ValueChanged<String> onStopChanged;
final VoidCallback onStopRemoved;
const _StopRow({
required this.index,
required this.controller,
required this.focusNode,
required this.isEditing,
required this.l10n,
required this.theme,
required this.cs,
required this.tripz,
required this.onStopTapped,
required this.onStopChanged,
required this.onStopRemoved,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: T.s4),
child: Row(
children: [
SizedBox(
width: 48,
child: Center(
child: Container(
width: 16,
height: 16,
decoration: BoxDecoration(
color: cs.surface,
shape: BoxShape.circle,
border: Border.all(color: cs.onSurfaceVariant, width: 2),
),
child: Center(
child: Text(
'${index + 1}',
style: theme.textTheme.labelSmall?.copyWith(
color: cs.onSurfaceVariant,
fontSize: 10,
height: 1,
),
),
),
),
),
),
Expanded(
child: GestureDetector(
onTap: onStopTapped,
child: Container(
decoration: BoxDecoration(
color: isEditing ? cs.surface : cs.surfaceContainerLow,
borderRadius: BorderRadius.circular(T.r12),
border: Border.all(
color: isEditing
? cs.primary
: cs.outlineVariant.withValues(alpha: 0.3),
width: isEditing ? 2 : 1,
),
boxShadow: isEditing ? const [T.shadowSm] : null,
),
child: Row(
children: [
Expanded(
child: TextField(
controller: controller,
focusNode: focusNode,
readOnly: !isEditing,
decoration: InputDecoration(
hintText: '${l10n.addStop} ${index + 1}',
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(
horizontal: T.s12,
vertical: T.s12,
),
hintStyle: theme.textTheme.bodyLarge?.copyWith(
color: cs.onSurfaceVariant.withValues(alpha: 0.7),
),
),
style: theme.textTheme.bodyLarge?.copyWith(
color: cs.onSurface,
),
onChanged: onStopChanged,
),
),
if (isEditing && controller.text.isNotEmpty)
IconButton(
onPressed: onStopRemoved,
icon: const Icon(Icons.close, size: 18),
color: cs.onSurfaceVariant,
splashRadius: 18,
),
],
),
),
),
),
],
),
);
}
}
// ── Dotted connector line ──
class _DottedConnector extends StatelessWidget {
final Color color;
const _DottedConnector({required this.color});
@override
Widget build(BuildContext context) {
return SizedBox(
width: 48,
child: CustomPaint(
size: const Size(48, 20),
painter: _DottedLinePainter(color: color),
),
);
}
}
class _DottedLinePainter extends CustomPainter {
final Color color;
_DottedLinePainter({required this.color});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..strokeWidth = 1.5
..style = PaintingStyle.stroke;
const dashHeight = 3.0;
const gapHeight = 3.0;
double y = 4;
while (y < size.height) {
canvas.drawLine(
Offset(size.width / 2, y),
Offset(size.width / 2, y + dashHeight),
paint,
);
y += dashHeight + gapHeight;
}
}
@override
bool shouldRepaint(covariant _DottedLinePainter old) => old.color != color;
}
// ── Destination row ──
class _DestinationRow extends StatelessWidget {
final TextEditingController controller;
final FocusNode focusNode;
final bool isEditing;
final AppLocalizations l10n;
final ThemeData theme;
final ColorScheme cs;
final TripzColors tripz;
final VoidCallback onFieldTapped;
final ValueChanged<String> onChanged;
final VoidCallback? onVoiceSearchPressed;
const _DestinationRow({
required this.controller,
required this.focusNode,
required this.isEditing,
required this.l10n,
required this.theme,
required this.cs,
required this.tripz,
required this.onFieldTapped,
required this.onChanged,
this.onVoiceSearchPressed,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: T.s4),
child: Row(
children: [
SizedBox(
width: 48,
child: Center(
child: Container(
width: 16,
height: 16,
decoration: BoxDecoration(
color: cs.error,
borderRadius: BorderRadius.circular(3),
),
child: Icon(Icons.stop_rounded, size: 12, color: cs.onError),
),
),
),
Expanded(
child: GestureDetector(
onTap: onFieldTapped,
child: Container(
decoration: BoxDecoration(
color: cs.surface,
borderRadius: BorderRadius.circular(T.r12),
border: Border.all(
color: isEditing ? cs.primary : cs.outlineVariant,
width: isEditing ? 2 : 1,
),
boxShadow: isEditing ? const [T.shadowSm] : null,
),
child: Row(
children: [
Expanded(
child: TextField(
controller: controller,
focusNode: focusNode,
readOnly: !isEditing,
decoration: InputDecoration(
hintText: l10n.whereTo,
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(
horizontal: T.s16,
vertical: T.s12,
),
hintStyle: theme.textTheme.bodyLarge?.copyWith(
color: cs.onSurfaceVariant.withValues(alpha: 0.7),
),
),
style: theme.textTheme.bodyLarge?.copyWith(
color: cs.onSurface,
),
onChanged: onChanged,
),
),
if (onVoiceSearchPressed != null)
IconButton(
onPressed: onVoiceSearchPressed,
icon: Icon(Icons.mic_rounded, color: cs.primary),
tooltip: 'البحث الصوتي الذكي (AI Voice)',
),
],
),
),
),
),
],
),
);
}
}
// ── Saved place chip ──
class _SavedPlaceChip extends StatelessWidget {
final IconData icon;
final String label;
final VoidCallback onTap;
const _SavedPlaceChip({
required this.icon,
required this.label,
required this.onTap,
});
@override
Widget build(BuildContext context) {
final cs = Theme.of(context).colorScheme;
final theme = Theme.of(context);
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(T.r12),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: T.s12, vertical: T.s8),
decoration: BoxDecoration(
color: cs.surfaceContainerLow,
borderRadius: BorderRadius.circular(T.r12),
border: Border.all(color: cs.outlineVariant.withValues(alpha: 0.3)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 32,
height: 32,
decoration: BoxDecoration(
color: cs.surface,
shape: BoxShape.circle,
boxShadow: const [T.shadowSm],
),
child: Icon(icon, color: cs.primary, size: 20),
),
const SizedBox(width: T.s8),
Text(
label,
style: theme.textTheme.titleSmall?.copyWith(
color: cs.onSurface,
fontSize: 16,
),
),
],
),
),
);
}
}
// ── Autocomplete search result tile ──
class _SearchResultTile extends StatelessWidget {
final PlaceModel place;
final VoidCallback onTap;
const _SearchResultTile({required this.place, required this.onTap});
@override
Widget build(BuildContext context) {
final cs = Theme.of(context).colorScheme;
final theme = Theme.of(context);
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(T.r8),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: T.s12,
horizontal: T.pagePadding,
),
child: Row(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: cs.surfaceContainerHighest,
shape: BoxShape.circle,
),
child: Icon(
Icons.location_on_outlined,
color: cs.onSurfaceVariant,
size: 20,
),
),
const SizedBox(width: T.s12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
place.name,
style: theme.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w500,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
if (place.address != null && place.address!.isNotEmpty) ...[
const SizedBox(height: T.s4),
Text(
place.address!,
style: theme.textTheme.bodySmall?.copyWith(
color: cs.onSurfaceVariant,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
],
),
),
Icon(Icons.chevron_right_rounded, color: cs.onSurfaceVariant),
],
),
),
);
}
}
// ── Pick from map tile ──
class _PickFromMapTile extends StatelessWidget {
final VoidCallback onTap;
const _PickFromMapTile({required this.onTap});
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
final cs = Theme.of(context).colorScheme;
final theme = Theme.of(context);
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(T.r12),
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: T.s12,
vertical: T.s12,
),
decoration: BoxDecoration(
color: cs.surfaceContainerHigh,
borderRadius: BorderRadius.circular(T.r12),
),
child: Row(
children: [
Icon(Icons.map_rounded, color: cs.primary, size: T.iconMd),
const SizedBox(width: T.s12),
Text(
l10n.setLocationOnMap,
style: theme.textTheme.bodyLarge?.copyWith(
color: cs.onSurface,
fontWeight: FontWeight.w500,
),
),
],
),
),
);
}
}