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)
    Once the project is generated, unzip it and open it in your IDE.

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.

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete