builder_plus 1.1.7
builder_plus: ^1.1.7 copied to clipboard
A Flutter package for simplified async operations with built-in loading, error, and empty states management.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:builder_plus/builder_plus.dart';
// A custom class to illustrate typing
typedef Id = int;
class Exemple {
final Id id;
final String nom;
Exemple(this.id, this.nom);
@override
String toString() => 'Exemple(id: $id, nom: $nom)';
}
// Simulate an asynchronous operation on an Exemple
Future<Exemple> traiterExemple(Exemple exemple,
{bool throwError = false, bool empty = false}) async {
await Future.delayed(const Duration(seconds: 2));
if (throwError) throw Exception('Network error');
if (empty)
return Future.value(Exemple(exemple.id, exemple.nom.toUpperCase()));
return Exemple(exemple.id, exemple.nom.toUpperCase());
}
// Simulate a stream of operations on an Exemple
Stream<Exemple?> streamerExemple(Exemple exemple,
{bool throwError = false, bool empty = false}) async* {
yield null;
await Future.delayed(const Duration(seconds: 1));
if (throwError) throw Exception('Stream error');
if (empty)
yield null;
else
yield Exemple(exemple.id, exemple.nom.toUpperCase());
await Future.delayed(const Duration(seconds: 1));
yield Exemple(exemple.id, '${exemple.nom.toUpperCase()} (final)');
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'builder_plus Example',
theme: ThemeData(primarySwatch: Colors.blue),
home: const ExempleScreen(),
);
}
}
class ExempleScreen extends StatefulWidget {
const ExempleScreen({super.key});
@override
State<ExempleScreen> createState() => _ExempleScreenState();
}
class _ExempleScreenState extends State<ExempleScreen> {
bool throwFutureError = false;
bool throwStreamError = false;
bool showEmpty = false;
@override
Widget build(BuildContext context) {
final exemple = Exemple(1, 'demo');
return Scaffold(
appBar: AppBar(title: const Text('FutureWorker & StreamWorker Example')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Checkbox(
value: throwFutureError,
onChanged: (v) =>
setState(() => throwFutureError = v ?? false),
),
const Text('Future Error'),
const SizedBox(width: 16),
Checkbox(
value: throwStreamError,
onChanged: (v) =>
setState(() => throwStreamError = v ?? false),
),
const Text('Stream Error'),
const SizedBox(width: 16),
Checkbox(
value: showEmpty,
onChanged: (v) => setState(() => showEmpty = v ?? false),
),
const Text('Empty State'),
],
),
const SizedBox(height: 24),
const Text('FutureWorker',
style: TextStyle(fontWeight: FontWeight.bold)),
FutureWorker<Exemple?>(
future: traiterExemple(exemple,
throwError: throwFutureError, empty: showEmpty),
builder: (context, resultat) => Text(
'After processing: \n${resultat.toString()}',
style: const TextStyle(fontSize: 18),
),
loadingBuilder: (context) => const OptionLoading(size: 32),
emptyBuilder: (context) => const OptionEmpty(
text: 'No data received',
icon: Icons.hourglass_empty,
),
errorBuilder: (context, error) => OptionError(
error: error.toString(),
onRetry: () => setState(() {}),
retryButtonText: 'Retry',
),
),
const SizedBox(height: 32),
const Text('StreamWorker',
style: TextStyle(fontWeight: FontWeight.bold)),
StreamWorker<Exemple?>(
stream: streamerExemple(exemple,
throwError: throwStreamError, empty: showEmpty),
builder: (context, resultat) => Text(
'Stream: \n${resultat.toString()}',
style: const TextStyle(fontSize: 18),
),
loadingBuilder: (context) =>
const OptionLoading(size: 32, color: Colors.orange),
emptyBuilder: (context) => const OptionEmpty(
text: 'Empty stream',
icon: Icons.stream,
iconColor: Colors.orange,
),
errorBuilder: (context, error) => OptionError(
error: error.toString(),
onRetry: () => setState(() {}),
retryButtonText: 'Restart stream',
),
),
],
),
),
);
}
}