Update: 2026-07-11 19:26:05
This commit is contained in:
@@ -40,6 +40,9 @@ class RpLocationField extends StatefulWidget {
|
|||||||
/// Optional trailing "remove" affordance (used by stops).
|
/// Optional trailing "remove" affordance (used by stops).
|
||||||
final VoidCallback? onRemove;
|
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
|
/// Whether the field participates in search. When false it behaves as a
|
||||||
/// read-only value chip that still opens the map picker on tap.
|
/// read-only value chip that still opens the map picker on tap.
|
||||||
final bool searchable;
|
final bool searchable;
|
||||||
@@ -58,6 +61,7 @@ class RpLocationField extends StatefulWidget {
|
|||||||
this.badge,
|
this.badge,
|
||||||
this.onResultFavorite,
|
this.onResultFavorite,
|
||||||
this.onRemove,
|
this.onRemove,
|
||||||
|
this.onTapField,
|
||||||
this.searchable = true,
|
this.searchable = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -123,7 +127,7 @@ class _RpLocationFieldState extends State<RpLocationField> {
|
|||||||
controller: widget.controller,
|
controller: widget.controller,
|
||||||
onChanged: widget.searchable ? _onChanged : null,
|
onChanged: widget.searchable ? _onChanged : null,
|
||||||
readOnly: !widget.searchable,
|
readOnly: !widget.searchable,
|
||||||
onTap: widget.searchable ? null : widget.onPickOnMap,
|
onTap: widget.onTapField ?? (widget.searchable ? null : widget.onPickOnMap),
|
||||||
style: RP.fieldValue,
|
style: RP.fieldValue,
|
||||||
cursorColor: widget.accent,
|
cursorColor: widget.accent,
|
||||||
textInputAction: TextInputAction.search,
|
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_location_field.dart';
|
||||||
import 'rp_map_pick_overlay.dart';
|
import 'rp_map_pick_overlay.dart';
|
||||||
import 'rp_theme.dart';
|
import 'rp_theme.dart';
|
||||||
|
import 'rp_search_dialog.dart';
|
||||||
|
|
||||||
/// The route-planning surface anchored to the bottom of the map.
|
/// The route-planning surface anchored to the bottom of the map.
|
||||||
///
|
///
|
||||||
@@ -267,6 +268,15 @@ class _Expanded extends StatelessWidget {
|
|||||||
onEmpty: s.clearPlacesStart,
|
onEmpty: s.clearPlacesStart,
|
||||||
onPickOnMap: RpActions.pickOtherPickupOnMap,
|
onPickOnMap: RpActions.pickOtherPickupOnMap,
|
||||||
onResultTap: (i) => RpActions.selectStart(s.placesStart[i]),
|
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.
|
// Regular order: origin = current location, changeable on the map.
|
||||||
@@ -276,11 +286,19 @@ class _Expanded extends StatelessWidget {
|
|||||||
controller: s.placeStartController,
|
controller: s.placeStartController,
|
||||||
hintText: s.currentLocationString,
|
hintText: s.currentLocationString,
|
||||||
results: const [],
|
results: const [],
|
||||||
|
onPickOnMap: RpActions.pickMyStartOnMap,
|
||||||
|
onResultTap: (_) {},
|
||||||
searchable: false,
|
searchable: false,
|
||||||
onQuery: (_) {},
|
onQuery: (_) {},
|
||||||
onEmpty: () {},
|
onEmpty: () {},
|
||||||
onPickOnMap: RpActions.pickMyStartOnMap,
|
onTapField: () {
|
||||||
onResultTap: (_) {},
|
RpSearchDialog.show(
|
||||||
|
context: Get.context!,
|
||||||
|
accent: RP.origin,
|
||||||
|
hintText: s.currentLocationString,
|
||||||
|
isOrigin: true,
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,6 +321,15 @@ class _Expanded extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
onPickOnMap: RpActions.pickDestinationOnMap,
|
onPickOnMap: RpActions.pickDestinationOnMap,
|
||||||
onResultTap: (i) => RpActions.selectDestination(i, s.placesDestination[i]),
|
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) {
|
onResultFavorite: (i) {
|
||||||
final res = s.placesDestination[i];
|
final res = s.placesDestination[i];
|
||||||
RpActions.addFavorite(
|
RpActions.addFavorite(
|
||||||
|
|||||||
Reference in New Issue
Block a user