When working with asynchronous API calls in Flutter using Dart, it’s crucial to provide a good user experience by showing a loading indicator. Without it, users may think the app is stuck or unresponsive.
In this tutorial, you’ll learn how to display a loading spinner (CircularProgressIndicator) while an API call is being processed. We’ll use FutureBuilder
and setState
methods, and include a simple UI to demonstrate how to show and hide the loading indicator dynamically.

This approach is especially useful for beginners building apps using Flutter. For a better understanding of asynchronous programming in Dart, check out Dart Asynchronous Programming Guide.
Dependencies
No additional packages needed, just use http
for API calls:
dependencies: flutter: sdk: flutter http: ^0.13.5
Step-by-step Code Implementation
1. Main UI (main.dart)
import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import 'api_service.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Loading Demo', home: ApiCallPage(), ); } }
2. API Call Page (api_call_page.dart)
import 'package:flutter/material.dart'; import 'api_service.dart'; class ApiCallPage extends StatefulWidget { @override _ApiCallPageState createState() => _ApiCallPageState(); } class _ApiCallPageState extends State<ApiCallPage> { bool isLoading = false; String result = ''; void fetchData() async { setState(() { isLoading = true; }); final data = await ApiService.getData(); setState(() { isLoading = false; result = data ?? 'Failed to load data'; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("API Loading Example")), body: Center( child: isLoading ? CircularProgressIndicator() : Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(result), SizedBox(height: 20), ElevatedButton( onPressed: fetchData, child: Text("Fetch Data"), ), ], ), ), ); } }
3. API Service (api_service.dart)
import 'package:http/http.dart' as http; import 'dart:convert'; class ApiService { static Future<String?> getData() async { try { final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1')); if (response.statusCode == 200) { final data = json.decode(response.body); return data['title']; } else { return "Error: ${response.statusCode}"; } } catch (e) { return "Exception: $e"; } } }
This simple architecture separates UI from API logic and handles loading state efficiently. You can reuse this pattern across your app for various network operations.