195 lines
5.9 KiB
Dart
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,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|