first commit
This commit is contained in:
873
lib/view/map_view/location.dart
Normal file
873
lib/view/map_view/location.dart
Normal file
@@ -0,0 +1,873 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:google_places_flutter/google_places_flutter.dart';
|
||||
import 'package:google_places_flutter/model/prediction.dart';
|
||||
import 'package:nearledaily/constants/color_constants.dart';
|
||||
import 'package:nearledaily/view/cart/cart_view.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:geocoding/geocoding.dart';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../../modules/authentication/auth.dart';
|
||||
import '../../constants/font_constants.dart';
|
||||
import '../../domain/provider/authentication/location.dart';
|
||||
import '../../main.dart';
|
||||
import '../../widgets/text_widget.dart';
|
||||
|
||||
class LocationPage extends StatefulWidget {
|
||||
const LocationPage({super.key});
|
||||
|
||||
@override
|
||||
State<LocationPage> createState() => _LocationPageState();
|
||||
}
|
||||
|
||||
class _LocationPageState extends State<LocationPage> with RouteAware {
|
||||
final CustomerLocationProvider locationProvider = CustomerLocationProvider();
|
||||
|
||||
List<Authentication> fetchedLocations = [];
|
||||
bool isLoading = true;
|
||||
|
||||
String? newAddress;
|
||||
String? newLat;
|
||||
String? newLong;
|
||||
|
||||
int? selectedLocationId;
|
||||
Authentication? selectedLocation;
|
||||
|
||||
String searchQuery = "";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_fetchLocations();
|
||||
}
|
||||
|
||||
@override
|
||||
void didPopNext() {
|
||||
_fetchLocations();
|
||||
super.didPopNext();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
routeObserver.subscribe(this, ModalRoute.of(context)!);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
routeObserver.unsubscribe(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _fetchLocations() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
final id = prefs.getInt('customerId');
|
||||
setState(() => isLoading = true);
|
||||
try {
|
||||
final locations = await locationProvider.fetchCustomerLocations(id!);
|
||||
setState(() {
|
||||
fetchedLocations = locations;
|
||||
});
|
||||
} catch (e) {
|
||||
print('Error fetching locations: $e');
|
||||
} finally {
|
||||
setState(() => isLoading = false);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _addNewAddress() async {
|
||||
await Get.to(() => const MapPickerPage())?.then((result) async {
|
||||
if (result == true) {
|
||||
print("Refreshing locations now ✅");
|
||||
await _fetchLocations();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Widget _badge({
|
||||
required IconData icon,
|
||||
required String label,
|
||||
required bool isSelected,
|
||||
}) {
|
||||
const primaryColor = Color(0xFF662582);
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 220), // ✅ prevents overflow
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? const Color(0xFFF3E8FA) : Colors.grey.shade100,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
size: 10,
|
||||
color: isSelected ? primaryColor : Colors.grey.shade500,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Flexible( // ✅ allows text to shrink and ellipsis
|
||||
child: Text(
|
||||
label,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
color: isSelected ? primaryColor : Colors.grey.shade500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _addressCard({
|
||||
required String address,
|
||||
required String doorNo,
|
||||
required String landmark,
|
||||
required VoidCallback onTap,
|
||||
required bool isSelected,
|
||||
bool isAddNew = false,
|
||||
}) {
|
||||
const primaryColor = Color(0xFF662582);
|
||||
|
||||
if (isAddNew) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 5),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
border: Border.all(
|
||||
color: primaryColor.withOpacity(0.35),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 34,
|
||||
height: 34,
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFFF3E8FA),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.add_location_alt_rounded,
|
||||
size: 17,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
ReusableTextWidget(
|
||||
text: "Add new address",
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
color: primaryColor,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeInOut,
|
||||
margin: const EdgeInsets.symmetric(vertical: 5),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
border: Border.all(
|
||||
color: isSelected ? primaryColor : Colors.grey.withOpacity(0.25),
|
||||
width: isSelected ? 1.5 : 0.5,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Icon circle
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
width: 34,
|
||||
height: 34,
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? const Color(0xFFF3E8FA)
|
||||
: Colors.grey.shade100,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.location_on_rounded,
|
||||
size: 17,
|
||||
color: isSelected ? primaryColor : Colors.grey.shade500,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 12),
|
||||
|
||||
// Address + badges — Expanded so it never overflows
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Main address bold (first 2 parts)
|
||||
ReusableTextWidget(
|
||||
text: address.split(',').take(2).join(',').trim(),
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
color: Colors.black.withOpacity(0.87),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
// Rest of address muted
|
||||
ReusableTextWidget(
|
||||
text: address.split(',').skip(2).join(',').trim(),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
color: Colors.grey.shade500,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// Badges — each individually constrained
|
||||
if (doorNo.isNotEmpty || landmark.isNotEmpty)
|
||||
Wrap(
|
||||
spacing: 6,
|
||||
runSpacing: 4,
|
||||
children: [
|
||||
if (doorNo.isNotEmpty)
|
||||
_badge(
|
||||
icon: Icons.door_front_door_outlined,
|
||||
label: "Door: $doorNo",
|
||||
isSelected: isSelected,
|
||||
),
|
||||
if (landmark.isNotEmpty)
|
||||
_badge(
|
||||
icon: Icons.near_me_outlined,
|
||||
label: "Near: $landmark",
|
||||
isSelected: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 10),
|
||||
|
||||
// Radio indicator
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
width: 18,
|
||||
height: 18,
|
||||
margin: const EdgeInsets.only(top: 2),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: isSelected
|
||||
? primaryColor
|
||||
: Colors.grey.withOpacity(0.4),
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
child: AnimatedScale(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
scale: isSelected ? 1 : 0,
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
decoration: const BoxDecoration(
|
||||
color: primaryColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildAddressList() {
|
||||
List<Widget> list = [];
|
||||
|
||||
// 1️⃣ Add API fetched addresses
|
||||
for (var loc in fetchedLocations) {
|
||||
final addressText = loc.address ?? '';
|
||||
if (addressText.toLowerCase().contains(searchQuery.toLowerCase())) {
|
||||
list.add(_addressCard(
|
||||
address: addressText,
|
||||
doorNo: loc.doorno ?? '',
|
||||
landmark: loc.landmark ?? '',
|
||||
isSelected: selectedLocationId == loc.locationid,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
selectedLocationId = loc.locationid;
|
||||
selectedLocation = loc;
|
||||
});
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// 2️⃣ Add new address (default, unchanged)
|
||||
if (newAddress != null &&
|
||||
newAddress!.toLowerCase().contains(searchQuery.toLowerCase())) {
|
||||
list.add(_addressCard(
|
||||
address: newAddress!,
|
||||
doorNo: '',
|
||||
landmark: '',
|
||||
isSelected: selectedLocationId == -1,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
selectedLocationId = -1;
|
||||
selectedLocation = Authentication(
|
||||
locationid: 0,
|
||||
customerid: "0",
|
||||
address: newAddress ?? "",
|
||||
suburb: "",
|
||||
city: "",
|
||||
state: "",
|
||||
landmark: "",
|
||||
doorno: "",
|
||||
postcode: "",
|
||||
latitude: newLat ?? "",
|
||||
longitude: newLong ?? "",
|
||||
);
|
||||
});
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
// 3️⃣ Always show "Add New Address" option
|
||||
list.add(_addressCard(
|
||||
address: "Add new address",
|
||||
doorNo: '',
|
||||
landmark: '',
|
||||
isSelected: false,
|
||||
isAddNew: true,
|
||||
onTap: _addNewAddress,
|
||||
));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void _showPaymentBottomSheet() {
|
||||
if (selectedLocation != null) {
|
||||
print("Selected Location Details:");
|
||||
print("locationid: ${selectedLocation!.locationid}");
|
||||
print("customerid: ${selectedLocation!.customerid}");
|
||||
print("address: ${selectedLocation!.address}");
|
||||
print("suburb: ${selectedLocation!.suburb}");
|
||||
print("city: ${selectedLocation!.city}");
|
||||
print("state: ${selectedLocation!.state}");
|
||||
print("landmark: ${selectedLocation!.landmark}");
|
||||
print("doorno: ${selectedLocation!.doorno}");
|
||||
print("postcode: ${selectedLocation!.postcode}");
|
||||
print("latitude: ${selectedLocation!.latitude}");
|
||||
print("longitude: ${selectedLocation!.longitude}");
|
||||
|
||||
Navigator.pop(context, selectedLocation);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
top: false,
|
||||
bottom: true,
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 1,
|
||||
leadingWidth: double.infinity,
|
||||
centerTitle: false,
|
||||
leading: Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.arrow_back, color: Colors.black),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
ReusableTextWidget(
|
||||
text: "Select Location",
|
||||
color: Colors.black,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add_location_alt, color: Color(0xFF662582)),
|
||||
tooltip: "Add New Location",
|
||||
onPressed: _addNewAddress,
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
TextField(
|
||||
onChanged: (val) {
|
||||
setState(() => searchQuery = val);
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: "Search Address",
|
||||
prefixIcon: const Icon(Icons.search),
|
||||
filled: true,
|
||||
fillColor: Colors.grey.shade100,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
Expanded(
|
||||
child: isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: ListView(
|
||||
children: _buildAddressList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: ElevatedButton(
|
||||
onPressed: selectedLocationId == null ? null : _showPaymentBottomSheet,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF662582),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
child: ReusableTextWidget(
|
||||
text: "Confirm Address",
|
||||
color: Colors.white,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
fontSize: 16,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
class MapPickerPage extends StatefulWidget {
|
||||
const MapPickerPage({super.key});
|
||||
|
||||
@override
|
||||
State<MapPickerPage> createState() => _MapPickerPageState();
|
||||
}
|
||||
|
||||
class _MapPickerPageState extends State<MapPickerPage> {
|
||||
LatLng? selectedLatLng;
|
||||
String? selectedAddress;
|
||||
GoogleMapController? mapController;
|
||||
|
||||
LatLng currentLatLng = const LatLng(11.0168, 76.9558); // default Coimbatore
|
||||
static const String googleApiKey = "AIzaSyBhkGfnq27sN0wV5y_S-M2KojpFTk_by-Q";
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_checkPermissionAndGetLocation();
|
||||
}
|
||||
|
||||
|
||||
// Search function
|
||||
|
||||
|
||||
Future<void> _checkPermissionAndGetLocation() async {
|
||||
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) {
|
||||
Get.snackbar("Location Disabled", "Please enable location services");
|
||||
return;
|
||||
}
|
||||
|
||||
LocationPermission permission = await Geolocator.checkPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
}
|
||||
if (permission == LocationPermission.deniedForever) {
|
||||
Get.snackbar("Permission Denied",
|
||||
"Location permission is permanently denied, please enable it in settings");
|
||||
return;
|
||||
}
|
||||
|
||||
if (permission == LocationPermission.whileInUse ||
|
||||
permission == LocationPermission.always) {
|
||||
await _goToCurrentLocation();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _getAddressFromLatLng(LatLng latLng) async {
|
||||
setState(() {
|
||||
selectedAddress = "Loading address...";
|
||||
});
|
||||
try {
|
||||
List<Placemark> placemarks =
|
||||
await placemarkFromCoordinates(latLng.latitude, latLng.longitude);
|
||||
|
||||
if (placemarks.isNotEmpty) {
|
||||
final place = placemarks.first;
|
||||
setState(() {
|
||||
selectedAddress =
|
||||
"${place.name}, ${place.locality}, ${place.administrativeArea}, ${place.postalCode}";
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
selectedAddress = "Unknown location";
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
selectedAddress = "Failed to get address";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _goToCurrentLocation() async {
|
||||
try {
|
||||
Position position = await Geolocator.getCurrentPosition(
|
||||
desiredAccuracy: LocationAccuracy.high);
|
||||
LatLng latLng = LatLng(position.latitude, position.longitude);
|
||||
|
||||
setState(() {
|
||||
selectedLatLng = latLng;
|
||||
});
|
||||
|
||||
mapController?.animateCamera(CameraUpdate.newLatLngZoom(latLng, 16));
|
||||
await _getAddressFromLatLng(latLng);
|
||||
} catch (e) {
|
||||
// Get.snackbar();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
top: false,
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
title: const Text("Pick Location"),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: _goToCurrentLocation,
|
||||
icon: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black26,
|
||||
blurRadius: 4,
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Icon(Icons.my_location, color: Colors.black),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
GoogleMap(
|
||||
initialCameraPosition:
|
||||
CameraPosition(target: currentLatLng, zoom: 14),
|
||||
onMapCreated: (controller) => mapController = controller,
|
||||
onTap: (latLng) async {
|
||||
setState(() {
|
||||
selectedLatLng = latLng;
|
||||
});
|
||||
await _getAddressFromLatLng(latLng);
|
||||
},
|
||||
markers: selectedLatLng != null
|
||||
? {
|
||||
Marker(
|
||||
markerId: const MarkerId("picked"),
|
||||
position: selectedLatLng!)
|
||||
}
|
||||
: {},
|
||||
myLocationEnabled: true,
|
||||
myLocationButtonEnabled: false,
|
||||
),
|
||||
// Floating button for current location
|
||||
|
||||
// Address card
|
||||
if (selectedAddress != null)
|
||||
Positioned(
|
||||
bottom: 80,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Text(
|
||||
selectedAddress!,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
],
|
||||
),
|
||||
bottomNavigationBar: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: ElevatedButton(
|
||||
onPressed: selectedLatLng == null
|
||||
? null
|
||||
: () async {
|
||||
String address = selectedAddress ?? "";
|
||||
String suburb = "";
|
||||
String city = "";
|
||||
String state = "";
|
||||
String postcode = "";
|
||||
|
||||
try {
|
||||
List<Placemark> placemarks =
|
||||
await placemarkFromCoordinates(
|
||||
selectedLatLng!.latitude,
|
||||
selectedLatLng!.longitude);
|
||||
if (placemarks.isNotEmpty) {
|
||||
final place = placemarks.first;
|
||||
suburb = place.subLocality ?? "";
|
||||
city = place.locality ?? "";
|
||||
state = place.administrativeArea ?? "";
|
||||
postcode = place.postalCode ?? "";
|
||||
|
||||
final result = await Get.to(() => AddressDetailsPage(
|
||||
address: address,
|
||||
suburb: suburb,
|
||||
city: city,
|
||||
state: state,
|
||||
postcode: postcode,
|
||||
latitude: selectedLatLng!.latitude.toString(),
|
||||
longitude: selectedLatLng!.longitude.toString(),
|
||||
));
|
||||
|
||||
if (result == true) {
|
||||
Get.back(result: true);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error parsing placemark: $e");
|
||||
}
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: ColorConstants.primaryColor,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16)),
|
||||
child: const Text(
|
||||
"Confirm Location",
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class AddressDetailsPage extends StatefulWidget {
|
||||
final String address;
|
||||
final String? suburb;
|
||||
final String? city;
|
||||
final String? state;
|
||||
final String? postcode;
|
||||
final String? latitude;
|
||||
final String? longitude;
|
||||
|
||||
const AddressDetailsPage({
|
||||
super.key,
|
||||
required this.address,
|
||||
this.suburb,
|
||||
this.city,
|
||||
this.state,
|
||||
this.postcode,
|
||||
this.latitude,
|
||||
this.longitude,
|
||||
});
|
||||
|
||||
@override
|
||||
State<AddressDetailsPage> createState() => _AddressDetailsPageState();
|
||||
}
|
||||
|
||||
class _AddressDetailsPageState extends State<AddressDetailsPage> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
late TextEditingController addressController;
|
||||
late TextEditingController doorController;
|
||||
late TextEditingController landmarkController;
|
||||
|
||||
bool isLoading = false;
|
||||
|
||||
final CustomerLocationProvider provider = CustomerLocationProvider();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
addressController = TextEditingController(text: widget.address);
|
||||
doorController = TextEditingController();
|
||||
landmarkController = TextEditingController();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
addressController.dispose();
|
||||
doorController.dispose();
|
||||
landmarkController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void submitAddress() async {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
|
||||
setState(() => isLoading = true);
|
||||
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
final id = prefs.getInt('customerId');
|
||||
|
||||
final success = await provider.createCustomerLocation(
|
||||
|
||||
|
||||
customerId: id!, // Replace with your dynamic customer ID
|
||||
address: addressController.text,
|
||||
doorNo: doorController.text,
|
||||
landmark: landmarkController.text,
|
||||
suburb: widget.suburb ?? "",
|
||||
city: widget.city ?? "",
|
||||
state: widget.state ?? "",
|
||||
postcode: widget.postcode ?? "",
|
||||
latitude: widget.latitude ?? "",
|
||||
longitude: widget.longitude ?? "",
|
||||
defaultAddress: "Yes",
|
||||
primaryAddress: 1,
|
||||
status: 1,
|
||||
);
|
||||
|
||||
|
||||
|
||||
setState(() => isLoading = false);
|
||||
Get.until((route) => route.settings.name == '/LocationPage');
|
||||
|
||||
if (success == true) {
|
||||
print("API Success ✅");
|
||||
Get.snackbar("Success", "Address submitted successfully");
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 800));
|
||||
Get.back(result: true);
|
||||
} else {
|
||||
print("API failed ❌");
|
||||
Get.snackbar("Error", "Failed to submit address");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.grey[200],
|
||||
appBar: AppBar(title: const Text("Edit Address"),backgroundColor: Colors.grey[200],),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
children: [
|
||||
_buildTextField("Address", addressController),
|
||||
const SizedBox(height: 12),
|
||||
_buildTextField("Door Number", doorController),
|
||||
const SizedBox(height: 12),
|
||||
_buildTextField("Landmark", landmarkController),
|
||||
const SizedBox(height: 20),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF662582), // Purple color
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
minimumSize: const Size(double.infinity, 50), // full width
|
||||
),
|
||||
onPressed: isLoading ? null : submitAddress,
|
||||
child: isLoading
|
||||
? const CircularProgressIndicator(color: Colors.white)
|
||||
: const Text(
|
||||
"Submit Address",
|
||||
style: TextStyle(color: Colors.white, fontSize: 16),
|
||||
),
|
||||
)
|
||||
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTextField(String label, TextEditingController controller) {
|
||||
return TextFormField(
|
||||
controller: controller,
|
||||
decoration: InputDecoration(
|
||||
labelText: label,
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
validator: (value) => value == null || value.isEmpty ? "Enter $label" : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user