Answer
Overview
Dart provides built-in support for asynchronous programming using Future, async/await, and Stream. These allow your app to do time-consuming work (like API calls or file I/O) without blocking the UI.
Future
A
text
FuturedartFuture<String> fetchUsername() async { await Future.delayed(Duration(seconds: 2)); // Simulate network delay return 'Alice'; } void main() async { print('Fetching...'); String name = await fetchUsername(); print('Username: $name'); // Prints after 2 seconds }
async / await
- marks a function as asynchronous — it always returns atext
asynctextFuture - pauses execution inside the async function until the Future completestext
await
dartFuture<void> loadData() async { try { final response = await http.get(Uri.parse('https://api.example.com/data')); final data = jsonDecode(response.body); print(data); } catch (e) { print('Error: $e'); } }
Without await (parallel execution)
dartFuture<void> loadParallel() async { // Both start simultaneously final userFuture = fetchUser(); final postsFuture = fetchPosts(); // Wait for both to complete final user = await userFuture; final posts = await postsFuture; } // Or with Future.wait final results = await Future.wait([fetchUser(), fetchPosts()]);
Stream
A
text
Streamtext
Futuredart// Creating a Stream Stream<int> countDown() async* { for (int i = 5; i >= 0; i--) { await Future.delayed(Duration(seconds: 1)); yield i; // Emit one value at a time } } // Consuming a Stream void main() async { await for (int value in countDown()) { print(value); // 5, 4, 3, 2, 1, 0 } }
StreamController (Custom Stream)
dartimport 'dart:async'; final controller = StreamController<String>(); // Add data to stream controller.sink.add('Hello'); controller.sink.add('World'); // Listen to stream controller.stream.listen((data) { print(data); // Hello, World }); // Always close when done controller.close();
StreamBuilder in Flutter
dartStreamBuilder<QuerySnapshot>( stream: FirebaseFirestore.instance.collection('messages').snapshots(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return CircularProgressIndicator(); } if (snapshot.hasError) return Text('Error'); final docs = snapshot.data!.docs; return ListView.builder( itemCount: docs.length, itemBuilder: (context, index) => ListTile(title: Text(docs[index]['text'])), ); }, )
Future vs Stream
| Feature | Future | Stream |
|---|---|---|
| Values emitted | One | Many (over time) |
| Completion | Single result | Can be ongoing |
| Use case | HTTP request, DB read | Firestore, WebSocket, timer |
| Flutter widget | text | text |
| Listen | text text | text text |
Handling Errors
dart// Future error handling try { final data = await fetchData(); } catch (e) { print('Error: $e'); } // Stream error handling stream.listen( (data) => print(data), onError: (e) => print('Stream error: $e'), onDone: () => print('Stream closed'), );
Key Principle: Use
for a single async result. UsetextFuturefor continuous/real-time data. Always handle errors and dispose of StreamControllers and subscriptions.textStream