Answer
Overview
Using a REST API in Flutter involves making HTTP requests to a server and handling the JSON response. The most common packages are (simple) and texthttp (feature-rich).textdio
Setup — pubspec.yaml
yamldependencies: http: ^1.2.0 # or dio: ^5.4.0
Basic GET Request (http package)
dartimport 'package:http/http.dart' as http; import 'dart:convert'; Future<List<Post>> fetchPosts() async { final response = await http.get( Uri.parse('https://jsonplaceholder.typicode.com/posts'), headers: {'Content-Type': 'application/json'}, ); if (response.statusCode == 200) { final List<dynamic> json = jsonDecode(response.body); return json.map((e) => Post.fromJson(e)).toList(); } else { throw Exception('Failed to load posts: ${response.statusCode}'); } }
POST Request
dartFuture<Post> createPost(String title, String body) async { final response = await http.post( Uri.parse('https://jsonplaceholder.typicode.com/posts'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'title': title, 'body': body, 'userId': 1}), ); if (response.statusCode == 201) { return Post.fromJson(jsonDecode(response.body)); } else { throw Exception('Failed to create post'); } }
Using Dio (Recommended for Production)
dartimport 'package:dio/dio.dart'; final dio = Dio(BaseOptions( baseUrl: 'https://api.example.com headers: {'Authorization': 'Bearer $token'}, connectTimeout: Duration(seconds: 10), receiveTimeout: Duration(seconds: 10), )); // GET final response = await dio.get('/users'); final users = (response.data as List).map((e) => User.fromJson(e)).toList(); // POST final res = await dio.post('/users', data: {'name': 'Alice', 'email': 'alice@example.com'}); // With Interceptor (add auth token automatically) dio.interceptors.add(InterceptorsWrapper( onRequest: (options, handler) { options.headers['Authorization'] = 'Bearer $token'; handler.next(options); }, onError: (DioException error, handler) { if (error.response?.statusCode == 401) { // Handle token expiry } handler.next(error); }, ));
Model Class (JSON Serialization)
dartclass Post { final int id; final String title; final String body; Post({required this.id, required this.title, required this.body}); factory Post.fromJson(Map<String, dynamic> json) => Post( id: json['id'], title: json['title'], body: json['body'], ); Map<String, dynamic> toJson() => {'id': id, 'title': title, 'body': body}; }
Error Handling
dartFuture<List<Post>> fetchPostsSafely() async { try { final response = await http.get(Uri.parse('https://api.example.com/posts')) .timeout(Duration(seconds: 10)); switch (response.statusCode) { case 200: return (jsonDecode(response.body) as List) .map((e) => Post.fromJson(e)).toList(); case 401: throw UnauthorizedException(); case 404: throw NotFoundException(); default: throw ServerException(response.statusCode); } } on TimeoutException { throw NetworkTimeoutException(); } on SocketException { throw NoInternetException(); } }
Display in Widget with FutureBuilder
dartFutureBuilder<List<Post>>( future: fetchPosts(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return CircularProgressIndicator(); } if (snapshot.hasError) return Text('Error: ${snapshot.error}'); return ListView.builder( itemCount: snapshot.data!.length, itemBuilder: (context, i) => ListTile(title: Text(snapshot.data![i].title)), ); }, )
Best Practice: Use
for production apps — it includes interceptors, cancellation, file upload/download, and retry logic. Usetextdiofor simple/small apps.texthttp