4.5 KiB
4.5 KiB
| name | description |
|---|---|
| widgets-async | Flutter async widgets - FutureBuilder, StreamBuilder, and handling asynchronous operations |
Async Widgets
Flutter provides widgets for building UIs based on asynchronous data.
FutureBuilder
Build widgets based on Future completion.
FutureBuilder<String>(
future: fetchData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
if (snapshot.hasData) {
return Text('Data: ${snapshot.data}');
}
return const Text('No data');
},
)
FutureBuilder States
FutureBuilder<String>(
future: fetchData(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return const Text('Not started');
case ConnectionState.waiting:
return const CircularProgressIndicator();
case ConnectionState.active:
return const Text('Loading...');
case ConnectionState.done:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
return Text('Data: ${snapshot.data}');
}
},
)
StreamBuilder
Build widgets based on Stream events.
StreamBuilder<int>(
stream: counterStream(),
initialData: 0,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
return Text('Count: ${snapshot.data}');
},
)
StreamBuilder with ConnectionState
StreamBuilder<String>(
stream: dataStream(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
if (!snapshot.hasData) {
return const Text('No data');
}
return Text('Data: ${snapshot.data}');
},
)
Complete Example
class DataWidget extends StatelessWidget {
const DataWidget({super.key});
Future<String> fetchData() async {
await Future.delayed(const Duration(seconds: 2));
return 'Data loaded';
}
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: fetchData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Scaffold(
body: Center(child: CircularProgressIndicator()),
);
}
if (snapshot.hasError) {
return Scaffold(
appBar: AppBar(title: const Text('Error')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error, size: 48, color: Colors.red),
const SizedBox(height: 16),
Text('Error: ${snapshot.error}'),
ElevatedButton(
onPressed: () {
// Retry
},
child: const Text('Retry'),
),
],
),
),
);
}
return Scaffold(
appBar: AppBar(title: const Text('Data')),
body: Center(
child: Text('Data: ${snapshot.data}'),
),
);
},
);
}
}
StreamBuilder with List
StreamBuilder<List<String>>(
stream: itemsStream(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const CircularProgressIndicator();
}
final items = snapshot.data!;
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(title: Text(items[index]));
},
);
},
)
Key Points
- Use
FutureBuilderfor one-time async operations - Use
StreamBuilderfor continuous data streams - Check
connectionStateto handle loading states - Use
hasErrorto check for errors - Use
hasDatato check if data is available - Provide
initialDatafor StreamBuilder when needed - Handle all connection states for better UX
- Use
snapshot.datato access the result - Use
snapshot.errorto access errors