Most Flutter apps talk to REST APIs. This tutorial covers HTTP GET, model parsing, and UI states, without over-engineering architecture on day one.
What you will build
- A `Job` model from JSON
- A service class that calls `https://jsonplaceholder.typicode.com/posts` (stand-in for your API)
- A `FutureBuilder` list screen with loading, error, and empty states
- Pull-to-refresh
Prerequisites
- Flutter SDK 3.16+ installed
- Android emulator or iOS simulator
- Dart async/await basics
Step-by-step
- Create a project: `flutter create jobs_viewer && cd jobs_viewer`.
- Add `http: ^1.2.0` to `pubspec.yaml` and run `flutter pub get`.
- Create `lib/models/job.dart` with `Job.fromJson` factory constructor.
- Create `lib/services/job_api.dart` with `Future<List<Job>> fetchJobs()` using `http.get`.
- Build `lib/screens/job_list_screen.dart` with `FutureBuilder`, show `CircularProgressIndicator` while waiting.
- Wrap the list in `RefreshIndicator` and call `setState` to re-fetch on pull.
Model parsing
class Job {
const Job({ required this.id, required this.title, required this.body });
final int id;
final String title;
final String body;
factory Job.fromJson(Map<String, dynamic> json) {
return Job(
id: json['id'] as int,
title: json['title'] as String,
body: json['body'] as String,
);
}
}API service
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<List<Job>> fetchJobs() async {
final res = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));
if (res.statusCode != 200) throw Exception('Failed to load jobs');
final list = jsonDecode(res.body) as List<dynamic>;
return list.map((e) => Job.fromJson(e as Map<String, dynamic>)).toList();
}Improve from here
- Swap `FutureBuilder` for Riverpod or Bloc when state grows
- Add authenticated headers and token refresh
- Cache responses with `shared_preferences` for offline read
This pattern is how Kabim bootstraps mobile apps before adding full clean architecture.


