MODEL
class Place {
int? id; // Nullable, because the ID will be auto-generated by the database
String name;
int city_id;
// Constructor to create a Place object directly with a name
Place({required [Link], required this.city_id});
// Constructor to create a Place from a Map (e.g., from a database row)
[Link](Map<String, dynamic> map)
: id = map['id'],
name = map['name'],
city_id = map['city_id'];
// Method to convert a City object back to a Map (useful for inserting/updating)
Map<String, dynamic> toMap() {
return {'name': name, 'city_id': city_id};
API
class API {
static final String _baseurl = '[Link]
Future<[Link]> getAllCities() async {
String url =
'$_baseurl/city'; // Assuming the endpoint for cities is /cities
var response = await [Link]([Link](url));
return response;
Future<[Link]> getAllplaces(String name) async {
String url =
'$_baseurl/place?name=$name'; // Assuming 'name' is a query parameter
try {
var response = await [Link](
[Link](url),
headers: {"Content-Type": "application/json"},
);
return response;
} catch (e) {
throw Exception('Error fetching places: $e');
Future<bool> deleteplace(String placename, String cityname) async {
String url = '$_baseurl/deleteplace';
try {
var response = await [Link](
[Link](url),
headers: {"Content-Type": "application/json"},
body: [Link]({
'placename': placename,
'cityname': cityname
}), // Send the city data
);
if ([Link] == 201) {
return true; // Success
} else {
return false; // Failed to add city
} catch (e) {
throw Exception('Error Deleting city: $e');
Future<bool> addplace(String placename, String cityname) async {
String url = '$_baseurl/addplace';
try {
var response = await [Link](
[Link](url),
headers: {"Content-Type": "application/json"},
body: [Link]({
'placename': placename,
'cityname': cityname
}), // Send the city data
);
if ([Link] == 200) {
return true; // Success
} else {
return false; // Failed to add city
} catch (e) {
throw Exception('Error adding city: $e');
Add Place Screen
class AddPLace extends StatefulWidget {
const AddPLace({[Link]});
@override
State<AddPLace> createState() => _AddPLaceState();
class _AddPLaceState extends State<AddPLace> {
API api = API();
bool _isLoading = false;
List<City> cityList = [];
final TextEditingController _placecontroller = TextEditingController();
String? selectedCity;
Future<void> _addplace() async {
if (selectedCity == null || _placecontroller.[Link]) {
[Link](context).showSnackBar(
SnackBar(content: Text('Please Select city name or Enter Place Name')),
);
return;
}
setState(() {
_isLoading = true;
});
try {
// Call the API method to save the city
bool success = await [Link](_placecontroller.text, selectedCity!);
if (success) {
// Show success message
[Link](context).showSnackBar(
SnackBar(content: Text('Place added successfully')),
);
_placecontroller.clear();
} else {
[Link](context).showSnackBar(
SnackBar(content: Text('Failed to add Place')),
);
} catch (e) {
[Link](context).showSnackBar(
SnackBar(content: Text('Error: $e')),
);
} finally {
setState(() {
_isLoading = false;
});
// Fetching cities from the API
Future<void> _getCities() async {
setState(() {
_isLoading = true;
});
try {
var response = await [Link]();
if ([Link] == 200) {
List<dynamic> mapList = jsonDecode([Link]);
cityList = [Link]((e) => [Link](e)).toList();
// Set the first city as the selected city by default
selectedCity = [Link] ? cityList[0].name : null;
// Fetch places for the default selected city
// if (selectedCity != null) {
// _getplaces(selectedCity!);
// }
[Link](context).showSnackBar(
SnackBar(content: Text('Cities fetched successfully')),
);
} else {
[Link](context).showSnackBar(
SnackBar(content: Text('Failed to fetch cities')),
);
} catch (e) {
[Link](context).showSnackBar(
SnackBar(content: Text('Error: $e')),
);
} finally {
setState(() {
_isLoading = false;
});
@override
void initState() {
[Link]();
_getCities(); // Fetch the cities when the screen loads
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize:
const [Link](150), // Adjusted height for better spacing
child: AppBar(
automaticallyImplyLeading: false, // Removes default back button
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [[Link].shade700, [Link].shade400],
begin: [Link],
end: [Link],
),
),
child: Center(
// Ensures content is centered
child: Column(
mainAxisAlignment:
[Link], // Centers content vertically
children: [
const Text(
"Add Place",
style: TextStyle(
color: [Link],
fontSize: 22,
fontWeight: [Link],
),
),
const SizedBox(height: 10),
Text(
"Set up and manage a new traffic control post location",
textAlign: [Link], // Centers text horizontally
style: TextStyle(
color: Colors
.grey[200], // Use light grey for better visibility
fontSize: 16,
fontWeight: [Link],
),
),
],
),
),
),
),
),
body: Stack(
children: [
// Main content
Padding(
padding: const [Link](16.0),
child: Column(
crossAxisAlignment: [Link],
children: [
const Text('Enter place name:'),
const SizedBox(height: 10),
TextFormField(
controller: _placecontroller, // Connect the controller
decoration: const InputDecoration(
labelText: 'Enter place name',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.location_city_rounded),
),
),
const SizedBox(height: 10),
// Dropdown for Place
const Text('Select City:'),
const SizedBox(height: 10),
Container(
width: [Link],
child: DropdownButton<String?>(
isExpanded: true,
hint: const Text(
'Choose City',
style: TextStyle(fontSize: 12),
),
value: selectedCity,
items: [Link]
?[
const DropdownMenuItem(
value: null,
child: Text('No cities available'),
),
] : [Link]((e) => DropdownMenuItem<String>(
value: e
.name, // The value should be a String, representing the city name
child: Text([Link]),
))
.toList(),
onChanged: (String? value) {
setState(() {
selectedCity =
value; // Update the selected city to the string value
});
}, ), ), ], ), ),
// Save Button Positioned at bottom
Positioned(
bottom: 90, // Adjust the distance from the bottom
left: 16,
right: 16,
child: SizedBox(
width: [Link],
child: ElevatedButton(
style: [Link](
backgroundColor: [Link],
minimumSize: const [Link](50),
elevation: 5,
),
onPressed: () {
_addplace();
setState(() {});
print('Save button pressed');
},
child: const Text(
'Save',
style: TextStyle(fontSize: 16, color: [Link]),
), ), ), ),
// Back Button Positioned at bottom
Positioned(
bottom: 30, // Adjust the distance from the bottom
left: 16,
right: 16,
child: SizedBox(
width: [Link],
child: ElevatedButton(
style: [Link](
backgroundColor: [Link],
minimumSize: const [Link](50),
elevation: 5,
),
onPressed: () {
// Add functionality for 'Back'
[Link](context); // Go back to the previous screen
},
child: const Text(
'Back',
style: TextStyle(fontSize: 16, color: [Link]),
), ), ), ), ],
),
bottomNavigationBar: BottomNavigationBar(
items: const [
BottomNavigationBarItem(
icon: Icon([Link]),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon([Link]),
label: 'Notifications',
),
BottomNavigationBarItem(
icon: Icon([Link]),
label: 'Profile',
),
BottomNavigationBarItem(
icon: Icon([Link]),
label: 'Settings',
),
],
currentIndex: 1,
selectedItemColor: [Link],
unselectedItemColor: [Link],
showUnselectedLabels: true,
onTap: (index) {
setState(() {});
}, ), ); }}
PlaceDetailScreen
class PlaceDetailScreen extends StatefulWidget {
const PlaceDetailScreen({[Link]});
@override
State<PlaceDetailScreen> createState() => _PlaceDetailScreenState();
class _PlaceDetailScreenState extends State<PlaceDetailScreen> {
API api = API();
bool _isLoading = false;
List<Place> placeList = [];
List<City> cityList = [];
String? selectedCity; // To hold the selected city for the dropdown
// Fetching the places based on the selected city
Future<void> _getplaces(String name) async {
if ([Link]) {
[Link](context).showSnackBar(
SnackBar(content: Text('Please Pass a city name')),
);
return;
setState(() {
_isLoading = true;
});
try {
var response = await [Link](name);
if ([Link] == 200) {
List<dynamic> placemap = [Link]([Link]);
placeList = [Link]((e) => [Link](e)).toList();
[Link](context).showSnackBar(
SnackBar(content: Text('Place fetched successfully')),
);
} else if ([Link] == 404) {
placeList = [];
[Link](context).showSnackBar(
SnackBar(content: Text('No places found for the specified city')),
);
} else {
placeList = [];
[Link](context).showSnackBar(
SnackBar(content: Text('Failed to fetch places')),
);
} catch (e) {
[Link](context).showSnackBar(
SnackBar(content: Text('Error: $e')),
);
} finally {
setState(() {
_isLoading = false;
});
} }
Future<void> _deleteplace(String placename, String cityname) async {
if ([Link] || [Link]) {
[Link](context).showSnackBar(
SnackBar(content: Text('Please Pass a city name and placename')),
);
return;
setState(() {
_isLoading = true;
});
try {
// Call the API method to delete the city
bool success = await [Link](placename, cityname);
if (success) {
// Show success message
[Link](context).showSnackBar(
SnackBar(content: Text('Place deleted successfully')),
);
} else {
[Link](context).showSnackBar(
SnackBar(content: Text('Failed to Delete Place')),
);
} catch (e) {
[Link](context).showSnackBar(
SnackBar(content: Text('Error: $e')),
);
} finally {
setState(() {
_isLoading = false;
});
} }
// Fetching cities from the API
Future<void> _getCities() async {
setState(() {
_isLoading = true;
});
try {
var response = await [Link]();
if ([Link] == 200) {
List<dynamic> mapList = jsonDecode([Link]);
cityList = [Link]((e) => [Link](e)).toList();
// Set the first city as the selected city by default
selectedCity = [Link] ? cityList[0].name : null;
// Fetch places for the default selected city
if (selectedCity != null) {
_getplaces(selectedCity!);
[Link](context).showSnackBar(
SnackBar(content: Text('Cities fetched successfully')),
);
} else {
[Link](context).showSnackBar(
SnackBar(content: Text('Failed to fetch cities')),
);
} catch (e) {
[Link](context).showSnackBar(
SnackBar(content: Text('Error: $e')),
);
} finally {
setState(() {
_isLoading = false;
});
} }
@override
void initState() {
[Link]();
_getCities(); // Fetch the cities when the screen loads
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: const [Link](150),
child: AppBar(
automaticallyImplyLeading: false,
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [[Link].shade700, [Link].shade400],
begin: [Link],
end: [Link],
), ),
child: Center(
child: Column(
mainAxisAlignment: [Link],
children: [
const Text(
"Place",
style: TextStyle(
color: [Link],
fontSize: 22,
fontWeight: [Link],
), ),
const SizedBox(height: 10),
Text(
"Set up and manage Places",
textAlign: [Link],
style: TextStyle(
color: [Link][200],
fontSize: 16,
fontWeight: [Link],
), ), ], ), ), ), ),
),
floatingActionButton: FloatingActionButton(
backgroundColor: [Link],
onPressed: () async {
[Link](
context,
MaterialPageRoute(builder: (context) => AddPLace()),
);
},
child: const Text(
'+',
style: TextStyle(
fontSize: 30,
color: [Link],
), ), ),
body: Stack(
children: [
Padding(
padding: const [Link](8.0),
child: Column(
children: [
// Dropdown for selecting a city
Container(
decoration: BoxDecoration(
borderRadius: [Link](12),
border: [Link](
color: [Link], // Set the border color
),
color: [Link], // Set the background color
),
padding: [Link](
horizontal: 10,
vertical: 5), // Add padding inside the container
child: DropdownButton<String>(
value: selectedCity,
hint: Text("Select City",
style: TextStyle(color: [Link])),
onChanged: (newValue) {
setState(() {
selectedCity = newValue;
});
if (selectedCity != null) {
_getplaces(
selectedCity!); // Fetch places for selected city
},
isExpanded:
true, // Make the dropdown expand to fit the container width
items: [Link]((city) {
return DropdownMenuItem<String>(
value: [Link],
child: Text(
[Link],
style:
TextStyle(color: [Link]), // Set text color
),
);
}).toList(),
),
),
// Show loading indicator while fetching data
if (_isLoading)
const Center(child: CircularProgressIndicator()),
// Display the list of places
Expanded(
child: [Link] > 0
? [Link](
itemCount: [Link],
itemBuilder: (context, index) {
Place place = placeList[index];
return Card(
margin: const [Link](8.0),
child: Padding(
padding: const [Link](16.0),
child: Row(
crossAxisAlignment:
[Link],
children: [
Column(
crossAxisAlignment:
[Link],
children: [
Text(
'Place ID: ${[Link]} \nPlace Name: ${[Link]}',
style: TextStyle(
fontSize: 16,
fontWeight: [Link],
), ),
SizedBox(height: 8),
], ),
SizedBox(
width: 50,
),
IconButton(
icon: const Icon([Link],
color: [Link]),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title:
const Text("Delete place"),
content: const Text(
"Are you sure you want to Delete Place ?",
),
actions: [
TextButton(
onPressed: () {
[Link](context);
},
child: const Text("Cancel"), ),
TextButton(
onPressed: () {
_deleteplace([Link],
selectedCity!);
setState(() {
_getplaces(
selectedCity!);
});
[Link](context);
},
child: const Text(
"Delete",
style: TextStyle(
color: [Link]),
), ), ], ); }, ); }, ), ], ), ),
); },
: Center(
child: Text('No Place Found'),
)), ], ),
),
Positioned(
bottom: 30,
left: 16,
right: 16,
child: SizedBox(
width: [Link],
child: ElevatedButton(
style: [Link](
backgroundColor: [Link],
minimumSize: const [Link](50),
elevation: 5,
),
onPressed: () {
[Link](context);
},
child: const Text( 'Back',style: TextStyle(fontSize: 16, color: [Link]),), ), ), ), ],
),