Update: 2026-07-11 19:26:05

This commit is contained in:
Hamza-Ayed
2026-07-11 19:26:05 +03:00
parent ce2f3d5675
commit 8353c4cef4
3 changed files with 205 additions and 3 deletions
@@ -40,6 +40,9 @@ class RpLocationField extends StatefulWidget {
/// Optional trailing "remove" affordance (used by stops).
final VoidCallback? onRemove;
/// Optional callback to fire when tapping the text field.
final VoidCallback? onTapField;
/// Whether the field participates in search. When false it behaves as a
/// read-only value chip that still opens the map picker on tap.
final bool searchable;
@@ -58,6 +61,7 @@ class RpLocationField extends StatefulWidget {
this.badge,
this.onResultFavorite,
this.onRemove,
this.onTapField,
this.searchable = true,
});
@@ -123,7 +127,7 @@ class _RpLocationFieldState extends State<RpLocationField> {
controller: widget.controller,
onChanged: widget.searchable ? _onChanged : null,
readOnly: !widget.searchable,
onTap: widget.searchable ? null : widget.onPickOnMap,
onTap: widget.onTapField ?? (widget.searchable ? null : widget.onPickOnMap),
style: RP.fieldValue,
cursorColor: widget.accent,
textInputAction: TextInputAction.search,
@@ -0,0 +1,171 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_rider/controller/home/map/location_search_controller.dart';
import 'package:siro_rider/controller/home/map/map_engine_controller.dart';
import 'package:siro_rider/controller/home/map/ride_lifecycle_controller.dart';
import 'rp_actions.dart';
import 'rp_location_field.dart';
import 'rp_search_results.dart';
import 'rp_theme.dart';
class RpSearchDialog extends StatelessWidget {
final Color accent;
final String hintText;
final bool isOrigin;
const RpSearchDialog({
super.key,
required this.accent,
required this.hintText,
required this.isOrigin,
});
static void show({
required BuildContext context,
required Color accent,
required String hintText,
required bool isOrigin,
}) {
showGeneralDialog(
context: context,
barrierDismissible: true,
barrierLabel: 'Search',
barrierColor: RP.sheet,
transitionDuration: const Duration(milliseconds: 300),
pageBuilder: (context, animation, secondaryAnimation) {
return RpSearchDialog(
accent: accent,
hintText: hintText,
isOrigin: isOrigin,
);
},
transitionBuilder: (context, animation, secondaryAnimation, child) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 1),
end: Offset.zero,
).animate(CurvedAnimation(
parent: animation,
curve: Curves.easeOutCubic,
)),
child: child,
);
},
);
}
void _onSelect(int index, dynamic place, BuildContext context) {
Navigator.of(context).pop();
if (isOrigin) {
RpActions.selectStart(place);
} else {
RpActions.selectDestination(index, place);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: RP.sheet,
body: SafeArea(
child: GetBuilder<LocationSearchController>(
builder: (s) {
return Column(
children: [
// Header
Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
IconButton(
icon: const Icon(Icons.arrow_back_rounded),
onPressed: () => Navigator.of(context).pop(),
),
const SizedBox(width: 8),
Expanded(
child: Text(
hintText,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: RP.textStrong,
),
),
),
],
),
),
// Field
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: RpLocationField(
accent: accent,
icon: isOrigin ? Icons.circle : Icons.flag_rounded,
controller: isOrigin
? s.placeStartController
: s.placeDestinationController,
hintText: hintText,
results: isOrigin ? s.placesStart : s.placesDestination,
searchable: true,
onQuery: (_) {
if (isOrigin) {
s.getPlacesStart();
} else {
s.getPlaces();
}
},
onEmpty: () {
if (isOrigin) {
s.clearPlacesStart();
} else {
s.clearPlacesDestination();
}
},
onPickOnMap: () {
Navigator.of(context).pop();
if (isOrigin) {
RpActions.pickOtherPickupOnMap();
} else {
RpActions.pickDestinationOnMap();
}
},
onResultTap: (i) => _onSelect(i, isOrigin ? s.placesStart[i] : s.placesDestination[i], context),
onResultFavorite: (i) {
final res = isOrigin ? s.placesStart[i] : s.placesDestination[i];
RpActions.addFavorite(
context,
res['latitude'],
res['longitude'],
(res['name_ar'] ?? res['name'] ?? 'Unknown Place').toString(),
);
},
),
),
const SizedBox(height: 16),
// Results
Expanded(
child: RpResultsList(
results: isOrigin ? s.placesStart : s.placesDestination,
accent: accent,
onTap: (i) => _onSelect(i, isOrigin ? s.placesStart[i] : s.placesDestination[i], context),
onFavorite: (i) {
final res = isOrigin ? s.placesStart[i] : s.placesDestination[i];
RpActions.addFavorite(
context,
res['latitude'],
res['longitude'],
(res['name_ar'] ?? res['name'] ?? 'Unknown Place').toString(),
);
},
),
),
],
);
},
),
),
);
}
}
@@ -13,6 +13,7 @@ import 'rp_favorites.dart';
import 'rp_location_field.dart';
import 'rp_map_pick_overlay.dart';
import 'rp_theme.dart';
import 'rp_search_dialog.dart';
/// The route-planning surface anchored to the bottom of the map.
///
@@ -267,6 +268,15 @@ class _Expanded extends StatelessWidget {
onEmpty: s.clearPlacesStart,
onPickOnMap: RpActions.pickOtherPickupOnMap,
onResultTap: (i) => RpActions.selectStart(s.placesStart[i]),
searchable: false,
onTapField: () {
RpSearchDialog.show(
context: Get.context!,
accent: RP.origin,
hintText: 'Search for a starting point'.tr,
isOrigin: true,
);
},
);
}
// Regular order: origin = current location, changeable on the map.
@@ -276,11 +286,19 @@ class _Expanded extends StatelessWidget {
controller: s.placeStartController,
hintText: s.currentLocationString,
results: const [],
onPickOnMap: RpActions.pickMyStartOnMap,
onResultTap: (_) {},
searchable: false,
onQuery: (_) {},
onEmpty: () {},
onPickOnMap: RpActions.pickMyStartOnMap,
onResultTap: (_) {},
onTapField: () {
RpSearchDialog.show(
context: Get.context!,
accent: RP.origin,
hintText: s.currentLocationString,
isOrigin: true,
);
},
);
}
@@ -303,6 +321,15 @@ class _Expanded extends StatelessWidget {
},
onPickOnMap: RpActions.pickDestinationOnMap,
onResultTap: (i) => RpActions.selectDestination(i, s.placesDestination[i]),
searchable: false,
onTapField: () {
RpSearchDialog.show(
context: Get.context!,
accent: RP.destination,
hintText: s.hintTextDestinationPoint,
isOrigin: false,
);
},
onResultFavorite: (i) {
final res = s.placesDestination[i];
RpActions.addFavorite(