Add the package with flutter pub add http. Network calls take time, so they are async and give back a Future: a value that arrives later. await waits for it.
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<List<String>> fetchLessonTitles() async {
final response = await http.get(Uri.parse('https://example.com/api/lessons'));
if (response.statusCode != 200) {
throw Exception('Failed to load lessons (${response.statusCode})');
}
final List<dynamic> data = jsonDecode(response.body);
return data.map((item) => item['title'] as String).toList();
}Showing the result
FutureBuilder rebuilds as the Future moves from waiting to done. Start the request once in initState, not in build, which can run many times.
class _LessonListState extends State<LessonList> {
late final Future<List<String>> _titles;
@override
void initState() {
super.initState();
_titles = fetchLessonTitles();
}
@override
Widget build(BuildContext context) {
return FutureBuilder<List<String>>(
future: _titles,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
final titles = snapshot.data!;
return ListView(
children: [for (final title in titles) ListTile(title: Text(title))],
);
},
);
}
}The Laravel lesson “Building a JSON API” builds exactly this kind of endpoint, so you can connect your own app to your own server.
Code examples
import 'dart:convert';
void main() {
const body = '[{"id": 1, "title": "Dart Basics"}, {"id": 2, "title": "Widgets"}]';
final List<dynamic> data = jsonDecode(body);
final titles = data.map((item) => item['title'] as String).toList();
print('${titles.length} lessons: ${titles.join(', ')}');
}Expected output
2 lessons: Dart Basics, Widgets
jsonDecode turns the JSON text into a Dart list of maps; map picks the title from each one.
Common mistakes
Common mistake: Starting the request inside build
build runs again on every rebuild, so the request repeats. Create the Future once in initState and pass it to FutureBuilder.
Common mistake: Using localhost from the Android emulator
Inside the emulator, localhost is the emulator itself. Use 10.0.2.2 to reach a server running on your computer.
Check your understanding
3 quick questions about this lesson.