Flutter REST API

Building complete REST API integrations

🔗 What is REST API?

REST API is an architectural style for building web services. Flutter apps use REST APIs to perform CRUD operations—Create, Read, Update, Delete—communicating with servers using HTTP methods.


// REST API endpoints
GET    /api/users      // Get all users
POST   /api/users      // Create user
PUT    /api/users/1    // Update user
DELETE /api/users/1    // Delete user
                                    

Output:

✓ REST endpoints defined

REST API Operations

📖

Read (GET)

Fetch data from server

Future<List<User>> getUsers() async {
  final response = await http.get(
    Uri.parse('$baseUrl/users')
  );
  return parseUsers(response.body);
}

Create (POST)

Add new data to server

Future<User> createUser(User user) async {
  final response = await http.post(
    Uri.parse('$baseUrl/users'),
    body: json.encode(user.toJson())
  );
  return User.fromJson(json.decode(response.body));
}
✏️

Update (PUT)

Modify existing data

Future<User> updateUser(int id, User user) async {
  final response = await http.put(
    Uri.parse('$baseUrl/users/$id'),
    body: json.encode(user.toJson())
  );
  return User.fromJson(json.decode(response.body));
}
🗑️

Delete (DELETE)

Remove data from server

Future<void> deleteUser(int id) async {
  await http.delete(
    Uri.parse('$baseUrl/users/$id')
  );
}

🔹 Complete REST API Service

Create a reusable API service class:

import 'package:http/http.dart' as http;
import 'dart:convert';

class ApiService {
  static const String baseUrl = 'https://jsonplaceholder.typicode.com';

  // GET - Fetch all users
  Future<List<User>> getUsers() async {
    final response = await http.get(Uri.parse('$baseUrl/users'));
    
    if (response.statusCode == 200) {
      List jsonList = json.decode(response.body);
      return jsonList.map((json) => User.fromJson(json)).toList();
    } else {
      throw Exception('Failed to load users');
    }
  }

  // GET - Fetch single user
  Future<User> getUser(int id) async {
    final response = await http.get(Uri.parse('$baseUrl/users/$id'));
    
    if (response.statusCode == 200) {
      return User.fromJson(json.decode(response.body));
    } else {
      throw Exception('Failed to load user');
    }
  }

  // POST - Create user
  Future<User> createUser(User user) async {
    final response = await http.post(
      Uri.parse('$baseUrl/users'),
      headers: {'Content-Type': 'application/json'},
      body: json.encode(user.toJson()),
    );
    
    if (response.statusCode == 201) {
      return User.fromJson(json.decode(response.body));
    } else {
      throw Exception('Failed to create user');
    }
  }

  // PUT - Update user
  Future<User> updateUser(int id, User user) async {
    final response = await http.put(
      Uri.parse('$baseUrl/users/$id'),
      headers: {'Content-Type': 'application/json'},
      body: json.encode(user.toJson()),
    );
    
    if (response.statusCode == 200) {
      return User.fromJson(json.decode(response.body));
    } else {
      throw Exception('Failed to update user');
    }
  }

  // DELETE - Delete user
  Future<void> deleteUser(int id) async {
    final response = await http.delete(Uri.parse('$baseUrl/users/$id'));
    
    if (response.statusCode != 200) {
      throw Exception('Failed to delete user');
    }
  }
}

Output:

✓ Complete REST API service created

🔹 Using the API Service

Implement CRUD operations in your Flutter app:

class UserScreen extends StatefulWidget {
  @override
  _UserScreenState createState() => _UserScreenState();
}

class _UserScreenState extends State<UserScreen> {
  final ApiService _apiService = ApiService();
  List<User> users = [];
  bool isLoading = false;

  @override
  void initState() {
    super.initState();
    fetchUsers();
  }

  Future<void> fetchUsers() async {
    setState(() => isLoading = true);
    try {
      users = await _apiService.getUsers();
    } catch (e) {
      print('Error: $e');
    } finally {
      setState(() => isLoading = false);
    }
  }

  Future<void> addUser() async {
    User newUser = User(name: 'John Doe', email: '[email protected]');
    try {
      User createdUser = await _apiService.createUser(newUser);
      setState(() => users.add(createdUser));
    } catch (e) {
      print('Error: $e');
    }
  }

  Future<void> removeUser(int id) async {
    try {
      await _apiService.deleteUser(id);
      setState(() => users.removeWhere((user) => user.id == id));
    } catch (e) {
      print('Error: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Users')),
      body: isLoading
          ? Center(child: CircularProgressIndicator())
          : ListView.builder(
              itemCount: users.length,
              itemBuilder: (context, index) {
                return ListTile(
                  title: Text(users[index].name),
                  subtitle: Text(users[index].email),
                  trailing: IconButton(
                    icon: Icon(Icons.delete),
                    onPressed: () => removeUser(users[index].id),
                  ),
                );
              },
            ),
      floatingActionButton: FloatingActionButton(
        onPressed: addUser,
        child: Icon(Icons.add),
      ),
    );
  }
}

Output:

Users

John Doe - [email protected] [Delete]

Jane Smith - [email protected] [Delete]

[+ Add Button]

🔹 Authentication with REST API

Add authentication tokens to requests:

class AuthApiService {
  static const String baseUrl = 'https://api.example.com';
  String? _token;

  // Login
  Future<void> login(String email, String password) async {
    final response = await http.post(
      Uri.parse('$baseUrl/auth/login'),
      headers: {'Content-Type': 'application/json'},
      body: json.encode({'email': email, 'password': password}),
    );

    if (response.statusCode == 200) {
      _token = json.decode(response.body)['token'];
    } else {
      throw Exception('Login failed');
    }
  }

  // Authenticated GET request
  Future<List<User>> getUsers() async {
    final response = await http.get(
      Uri.parse('$baseUrl/users'),
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer $_token',
      },
    );

    if (response.statusCode == 200) {
      List jsonList = json.decode(response.body);
      return jsonList.map((json) => User.fromJson(json)).toList();
    } else {
      throw Exception('Failed to load users');
    }
  }
}

Output:

✓ Login successful

Token: eyJhbGciOiJIUzI1NiIsInR5cCI6...

✓ Users fetched with authentication

🔹 Pagination and Filtering

Handle large datasets with pagination:

class PaginatedApiService {
  static const String baseUrl = 'https://api.example.com';

  Future<PaginatedResponse> getUsers({
    int page = 1,
    int limit = 10,
    String? search,
  }) async {
    Map<String, String> queryParams = {
      'page': page.toString(),
      'limit': limit.toString(),
    };

    if (search != null && search.isNotEmpty) {
      queryParams['search'] = search;
    }

    final uri = Uri.parse('$baseUrl/users').replace(
      queryParameters: queryParams,
    );

    final response = await http.get(uri);

    if (response.statusCode == 200) {
      return PaginatedResponse.fromJson(json.decode(response.body));
    } else {
      throw Exception('Failed to load users');
    }
  }
}

class PaginatedResponse {
  final List<User> users;
  final int totalPages;
  final int currentPage;

  PaginatedResponse({
    required this.users,
    required this.totalPages,
    required this.currentPage,
  });

  factory PaginatedResponse.fromJson(Map<String, dynamic> json) {
    return PaginatedResponse(
      users: (json['data'] as List)
          .map((user) => User.fromJson(user))
          .toList(),
      totalPages: json['totalPages'],
      currentPage: json['currentPage'],
    );
  }
}

Output:

Page 1 of 5

Showing 10 users

Search: "john" - 3 results

🧠 Test Your Knowledge

Which HTTP method is used to create a new resource in REST API?