Building a Full-Stack CRUD App with Flutter and Spring Boot: A Complete Guide
Creating a full-stack CRUD (Create, Read, Update, Delete) application with Flutter for the front end and Spring Boot for the back end is a great way to learn modern web and mobile development. Below is a step-by-step guide to build this application.
Prerequisites
- Java and Spring Boot knowledge
- Flutter setup and knowledge
- IDEs like IntelliJ IDEA or VS Code
Step 1: Create a Spring Boot Application (Back-end)
1.1. Set up Spring Boot Project
- Use Spring Initializr to generate a Spring Boot project:
- Project: Maven Project
- Language: Java
- Spring Boot: 3.x or latest
- Dependencies: Spring Web, Spring Data JPA, H2 Database (or any other database you prefer)
1.2. Create a Model
Create a Java class to represent the entity for the CRUD operation. For example, if you're managing "Product" information:
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Double price;
// Getters and Setters
}
1.3. Create a Repository
Create a repository interface to interact with the database.
public interface ProductRepository extends JpaRepository<Product, Long> {
}
1.4. Create a Service Layer
Create a service class to implement CRUD logic.
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
public List<Product> getAllProducts() {
return productRepository.findAll();
}
public Product createProduct(Product product) {
return productRepository.save(product);
}
public Product updateProduct(Long id, Product product) {
product.setId(id);
return productRepository.save(product);
}
public void deleteProduct(Long id) {
productRepository.deleteById(id);
}
}
1.5. Create a Controller
Expose the CRUD API via a REST controller.
@RestController
@RequestMapping("/api/products")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping
public List<Product> getAllProducts() {
return productService.getAllProducts();
}
@PostMapping
public Product createProduct(@RequestBody Product product) {
return productService.createProduct(product);
}
@PutMapping("/{id}")
public Product updateProduct(@PathVariable Long id, @RequestBody Product product) {
return productService.updateProduct(id, product);
}
@DeleteMapping("/{id}")
public void deleteProduct(@PathVariable Long id) {
productService.deleteProduct(id);
}
}
1.6. Application Configuration
If using a database like MySQL or PostgreSQL, update application.properties
to configure the database.
For example, for H2 (in-memory database):
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
Step 2: Test Spring Boot API
- Run the Spring Boot application by executing
mvn spring-boot:run
. - Test the API using Postman or any REST client:
GET /api/products
: Get all products.POST /api/products
: Create a new product.PUT /api/products/{id}
: Update an existing product.DELETE /api/products/{id}
: Delete a product.
Step 3: Create a Flutter Application (Front-end)
3.1. Set up Flutter Project
Create a new Flutter project using the command:
flutter create product_crud
cd product_crud
3.2. Add Dependencies
In pubspec.yaml
, add the necessary dependencies for HTTP requests:
dependencies:
flutter:
sdk: flutter
http: ^0.13.3
Run flutter pub get
to install the dependencies.
3.3. Create a Model in Flutter
Create a Product
model to match the Spring Boot model.
class Product {
final int id;
final String name;
final double price;
Product({required this.id, required this.name, required this.price});
factory Product.fromJson(Map<String, dynamic> json) {
return Product(
id: json['id'],
name: json['name'],
price: json['price'],
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'price': price,
};
}
}
3.4. Create HTTP Request Functions
Create functions to interact with the Spring Boot API using the http
package.
import 'package:http/http.dart' as http;
import 'dart:convert';
class ProductService {
final String baseUrl = "http://localhost:8080/api/products";
Future<List<Product>> getProducts() async {
final response = await http.get(Uri.parse(baseUrl));
if (response.statusCode == 200) {
List<dynamic> data = json.decode(response.body);
return data.map((e) => Product.fromJson(e)).toList();
} else {
throw Exception("Failed to load products");
}
}
Future<Product> createProduct(Product product) async {
final response = await http.post(
Uri.parse(baseUrl),
headers: {'Content-Type': 'application/json'},
body: json.encode(product.toJson()),
);
if (response.statusCode == 201) {
return Product.fromJson(json.decode(response.body));
} else {
throw Exception("Failed to create product");
}
}
Future<Product> updateProduct(int id, Product product) async {
final response = await http.put(
Uri.parse("$baseUrl/$id"),
headers: {'Content-Type': 'application/json'},
body: json.encode(product.toJson()),
);
if (response.statusCode == 200) {
return Product.fromJson(json.decode(response.body));
} else {
throw Exception("Failed to update product");
}
}
Future<void> deleteProduct(int id) async {
final response = await http.delete(Uri.parse("$baseUrl/$id"));
if (response.statusCode != 200) {
throw Exception("Failed to delete product");
}
}
}
3.5. Create a UI for CRUD Operations
Use Flutter widgets to display and interact with the products. Example for a basic list view:
import 'package:flutter/material.dart';
import 'product_service.dart';
import 'product.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Product CRUD',
theme: ThemeData(primarySwatch: Colors.blue),
home: ProductListPage(),
);
}
}
class ProductListPage extends StatefulWidget {
@override
_ProductListPageState createState() => _ProductListPageState();
}
class _ProductListPageState extends State<ProductListPage> {
final ProductService productService = ProductService();
late Future<List<Product>> futureProducts;
@override
void initState() {
super.initState();
futureProducts = productService.getProducts();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Products')),
body: FutureBuilder<List<Product>>(
future: futureProducts,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
} else if (snapshot.hasData) {
List<Product> products = snapshot.data!;
return ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return ListTile(
title: Text(product.name),
subtitle: Text('\$${product.price}'),
onTap: () {
// You can add update or delete functionality here
},
);
},
);
} else {
return Center(child: Text('No products found.'));
}
},
),
);
}
}
3.6. Run the Flutter Application
Run the Flutter application using flutter run
.
Step 4: Testing the Complete Application
- Ensure the Spring Boot API is running on
localhost:8080
. - Test the CRUD functionality in the Flutter app:
- Add, display, update, and delete products.
Final Thoughts
This guide covers setting up a full-stack CRUD application using Flutter for the front end and Spring Boot for the back end. You can extend this by adding more complex functionality, such as authentication, error handling, and persistent storage.