Check this
https://testsvfcb.pythonanywhere.com
1. Setup Retrofit for Flutter
Retrofit simplifies API handling by auto-generating code based on annotations.
Step 1: Add Dependencies
Update your pubspec.yaml
with the following:
dependencies:
dio: ^5.0.0
retrofit: ^4.0.0
json_annotation: ^4.8.0
dev_dependencies:
retrofit_generator: ^7.0.0
build_runner: ^2.4.4
Run:
flutter pub get
File Structure
features/
└── restaurant_crud/
├── components/
│ ├── restaurant_list_component.dart
│ ├── restaurant_form_component.dart
│ └── restaurant_card_component.dart
└── screen/
├── restaurant_add_page.dart
├── restaurant_all_page.dart
├── restaurant_edit_page.dart
└── restaurant_single_page.dart
core/
└── models/
└── restaurant_model.dart
└── services/
└── restaurant_api_service.dart
main.dart
Step 1: Create the Restaurant Model
File: restaurant_model.dart
class Restaurant {
final String id;
final String name;
final String address;
final String phone;
final String email;
final bool status;
final String description;
final int userId;
final int categoryId;
Restaurant({
required this.id,
required this.name,
required this.address,
required this.phone,
required this.email,
required this.status,
required this.description,
required this.userId,
required this.categoryId,
});
// Factory method to convert JSON to object
factory Restaurant.fromJson(Map<String, dynamic> json) {
return Restaurant(
id: json['restaurant_uuid'] ?? '',
name: json['name'] ?? '',
address: json['address'] ?? '',
phone: json['phone'] ?? '',
email: json['email'] ?? '',
status: json['status'] ?? false,
description: json['description'] ?? '',
userId: json['user_id'] ?? 0,
categoryId: json['category_id'] ?? 0,
);
}
// Convert object to JSON for POST/PUT requests
Map<String, dynamic> toJson() {
return {
'name': name,
'address': address,
'phone': phone,
'email': email,
'status': status,
'description': description,
'user_id': userId,
'category_id': categoryId,
};
}
}
Step 2: Retrofit API Service
File: restaurant_api_service.dart
Using Retrofit, the following interface defines the CRUD API endpoints.
import 'package:dio/dio.dart';
import 'package:retrofit/retrofit.dart';
import '../models/restaurant_model.dart';
part 'restaurant_api_service.g.dart';
@RestApi(baseUrl: "https://testsvfcb.pythonanywhere.com")
abstract class RestaurantApiService {
factory RestaurantApiService(Dio dio, {String baseUrl}) = _RestaurantApiService;
@GET("/restaurant/")
Future<List<Restaurant>> getAllRestaurants();
@GET("/restaurant/{id}")
Future<Restaurant> getRestaurantById(@Path("id") String id);
@POST("/restaurant/")
Future<void> createRestaurant(@Body() Restaurant restaurant);
@PUT("/restaurant/{id}")
Future<void> updateRestaurant(@Path("id") String id, @Body() Restaurant restaurant);
@DELETE("/restaurant/{id}")
Future<void> deleteRestaurant(@Path("id") String id);
}
Step 3: Generate Retrofit Code
Run the following command to generate the required API implementation:
flutter pub run build_runner build
Step 4: Components
1. Restaurant List Component
File: restaurant_list_component.dart
import 'package:flutter/material.dart';
import '../../../core/models/restaurant_model.dart';
import '../../../core/services/restaurant_api_service.dart';
import 'restaurant_card_component.dart';
class RestaurantListComponent extends StatefulWidget {
const RestaurantListComponent({Key? key}) : super(key: key);
@override
State<RestaurantListComponent> createState() => _RestaurantListComponentState();
}
class _RestaurantListComponentState extends State<RestaurantListComponent> {
late final RestaurantApiService apiService;
List<Restaurant> restaurants = [];
@override
void initState() {
super.initState();
apiService = RestaurantApiService(Dio());
fetchRestaurants();
}
void fetchRestaurants() async {
final data = await apiService.getAllRestaurants();
setState(() {
restaurants = data;
});
}
void deleteRestaurant(String id) async {
await apiService.deleteRestaurant(id);
fetchRestaurants();
}
@override
Widget build(BuildContext context) {
return Wrap(
spacing: 10,
runSpacing: 10,
children: restaurants
.map((restaurant) => RestaurantCardComponent(
restaurant: restaurant,
onDelete: () => deleteRestaurant(restaurant.id),
))
.toList(),
);
}
}
2. Restaurant Card Component
File: restaurant_card_component.dart
import 'package:flutter/material.dart';
import '../../../core/models/restaurant_model.dart';
class RestaurantCardComponent extends StatelessWidget {
final Restaurant restaurant;
final VoidCallback onDelete;
const RestaurantCardComponent({
Key? key,
required this.restaurant,
required this.onDelete,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Name: ${restaurant.name}",
style: const TextStyle(fontWeight: FontWeight.bold)),
Text("Address: ${restaurant.address}"),
Text("Phone: ${restaurant.phone}"),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
icon: const Icon(Icons.edit, color: Colors.blue),
onPressed: () {
// Navigate to Edit Page
},
),
IconButton(
icon: const Icon(Icons.delete, color: Colors.red),
onPressed: onDelete,
),
],
),
],
),
),
);
}
}
3. Restaurant Add Page
File: restaurant_add_page.dart
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../../../core/model/restaurant_model.dart';
import '../../../core/services/restaurant_api_service.dart';
class RestaurantAddPage extends StatelessWidget {
final RestaurantApiService apiService = RestaurantApiService(Dio());
final TextEditingController nameController = TextEditingController();
final TextEditingController addressController = TextEditingController();
final FocusNode nameFocus = FocusNode();
final FocusNode addressFocus = FocusNode();
RestaurantAddPage({Key? key}) : super(key: key);
void saveRestaurant(BuildContext context) async {
nameFocus.unfocus();
addressFocus.unfocus();
final newRestaurant = Restaurant(
id: '',
name: nameController.text,
address: addressController.text,
phone: "09517477717",
email: "test@test.com",
status: true,
description: "This is restaurant",
userId: 1,
categoryId: 2,
);
try {
await apiService.createRestaurant(newRestaurant);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Restaurant saved successfully!")),
);
context.go('/restaurant');
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Error saving restaurant: $e")),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Add Restaurant")),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: nameController,
focusNode: nameFocus,
decoration: const InputDecoration(labelText: "Name"),
),
TextField(
controller: addressController,
focusNode: addressFocus,
decoration: const InputDecoration(labelText: "Address"),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () => saveRestaurant(context),
child: const Text("Save"),
),
],
),
),
);
}
}
- Restaurant All Page
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../../../core/model/restaurant_model.dart';
import '../../../core/services/restaurant_api_service.dart';
import '../components/restaurant_list.dart';
class RestaurantAllPage extends StatefulWidget {
const RestaurantAllPage({Key? key}) : super(key: key);
@override
_RestaurantAllPageState createState() => _RestaurantAllPageState();
}
class _RestaurantAllPageState extends State<RestaurantAllPage> {
late final RestaurantApiService apiService;
List<Restaurant> restaurants = [];
@override
void initState() {
super.initState();
apiService = RestaurantApiService(Dio());
fetchRestaurants();
}
void fetchRestaurants() async {
try {
final data = await apiService.getAllRestaurants();
setState(() {
restaurants = data;
});
} catch (e) {
print("Error fetching restaurants: $e");
}
}
void deleteRestaurant(String id) async {
await apiService.deleteRestaurant(id);
fetchRestaurants();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("All Restaurants"),
),
body:
ListView.builder(
itemCount: restaurants.length,
itemBuilder: (context, index) {
final restaurant = restaurants[index];
return Card(
margin: const EdgeInsets.symmetric(vertical: 8),
child: ListTile(
title: Text(restaurant.name),
subtitle: Text(restaurant.address),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.edit, color: Colors.blue),
onPressed: () {
context.go('/edit-restaurant/${restaurant.id}');
},
),
IconButton(
icon: const Icon(Icons.delete, color: Colors.red),
onPressed: () {
deleteRestaurant(restaurant.id);
},
),
],
),
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.go('/add-restaurant'); // Navigate to Add Page
},
child: const Icon(Icons.add),
),
);
}
}