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

195 lines
5.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../core/l10n/app_localizations.dart';
import 'package:go_router/go_router.dart';
import '../../../core/design/tokens.dart';
import '../../../core/ui/tripz_scaffold.dart';
import '../../../core/ui/tripz_text_field.dart';
import '../cubit/destination_cubit.dart';
import '../data/models/place_model.dart';
import 'widgets/place_tile.dart';
/// شاشة البحث عن وجهة — full-screen مع autocomplete.
class DestinationScreen extends StatefulWidget {
const DestinationScreen({super.key});
@override
State<DestinationScreen> createState() => _DestinationScreenState();
}
class _DestinationScreenState extends State<DestinationScreen> {
final _controller = TextEditingController();
final _focusNode = FocusNode();
@override
void initState() {
super.initState();
_focusNode.requestFocus();
}
@override
void dispose() {
_controller.dispose();
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return TripzScaffold(
padding: false,
child: Column(
children: [
_SearchHeader(
controller: _controller,
focusNode: _focusNode,
l10n: l10n,
),
Expanded(
child: BlocBuilder<DestinationCubit, DestinationState>(
builder: (context, state) {
if (state.searching) {
return const Center(child: CircularProgressIndicator());
}
if (state.results.isEmpty && state.query.length >= 2) {
return Center(
child: Text(l10n.noResults, style: Theme.of(context).textTheme.bodyLarge),
);
}
return ListView.builder(
padding: EdgeInsets.zero,
itemCount: state.results.length,
itemBuilder: (context, index) {
final place = state.results[index];
return PlaceTile(
place: place,
onTap: () => context.pop<PlaceModel>(place),
);
},
);
},
),
),
SafeArea(
child: Padding(
padding: const EdgeInsets.all(T.pagePadding),
child: SizedBox(
width: double.infinity,
child: OutlinedButton.icon(
onPressed: () => context.pop<PlaceModel>(null),
icon: const Icon(Icons.map_outlined),
label: Text(l10n.chooseOnMap),
),
),
),
),
],
),
);
}
}
class _SearchHeader extends StatelessWidget {
final TextEditingController controller;
final FocusNode focusNode;
final AppLocalizations l10n;
const _SearchHeader({
required this.controller,
required this.focusNode,
required this.l10n,
});
@override
Widget build(BuildContext context) {
final cs = Theme.of(context).colorScheme;
return Container(
color: cs.surface,
padding: const EdgeInsets.only(
left: T.pagePadding,
right: T.pagePadding,
top: T.s8,
bottom: T.s12,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
IconButton(
onPressed: () => Navigator.of(context).pop(),
icon: const Icon(Icons.arrow_back_rounded),
),
Expanded(
child: TripzTextField(
controller: controller,
focusNode: focusNode,
hint: l10n.searchPlaces,
prefix: const Icon(Icons.search_rounded),
onChanged: (v) => context.read<DestinationCubit>().onQueryChanged(v),
),
),
],
),
const SizedBox(height: T.s8),
Row(
children: [
Column(
children: [
Container(
width: 10,
height: 10,
decoration: BoxDecoration(
color: cs.primary,
shape: BoxShape.circle,
),
),
Container(width: 1.5, height: 24, color: cs.outlineVariant),
Container(
width: 10,
height: 10,
decoration: BoxDecoration(
color: cs.error,
shape: BoxShape.circle,
),
),
],
),
const SizedBox(width: T.s12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
l10n.pickupLocation,
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: cs.onSurfaceVariant,
),
),
const SizedBox(height: T.s4),
Text(
l10n.pickupSet,
style: Theme.of(context).textTheme.bodyMedium,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: T.s16),
Text(
l10n.dropoffLocation,
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: cs.onSurfaceVariant,
),
),
],
),
),
],
),
],
),
);
}
}