Implementing asynchronous operations with a Left
and Right
pattern in Dart, especially with Flutter and Provider, would involve using an external package like dartz
. This package is not included in Dart by default, but it introduces functional programming concepts like Either
, Left
, and Right
. Here’s how you can structure it:
dartz
PackageFirst, add dartz
to your pubspec.yaml
:
dependencies:
flutter:
sdk: flutter
dartz: ^0.10.0 # Check for the latest version
Create a simple User model:
class User {
final String name;
// Add other fields as needed
User(this.name);
// Add fromJson, toJson if you’re fetching from an API
}
This class will handle fetching data and return Either
:
import 'package:dartz/dartz.dart';
class UserService {
Future<Either<String, List<User>>> fetchUsers() async {
try {
// Simulating network request
await Future.delayed(Duration(seconds: 2));
return Right([User('Alice'), User('Bob')]);
} catch (e) {
return Left('Failed to fetch users');
}
}
}
This will use the ChangeNotifier
from the Provider package:
import 'package:flutter/material.dart';
import 'user_service.dart';
class UserListNotifier extends ChangeNotifier {
final UserService _userService = UserService();
List<User> users = [];
bool isLoading = false;
String errorMessage = '';
void fetchUsers() async {
isLoading = true;
notifyListeners();
final result = await _userService.fetchUsers();
result.fold(
(left) {
errorMessage = left;
users = [];
},
(right) {
users = right;
errorMessage = '';
},
);
isLoading = false;
notifyListeners();
}
}
In your main widget file:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'user_list_notifier.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => UserListNotifier(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: UserListScreen(),
);
}
}
class UserListScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(“Users”)),
body: Center(
child: Consumer<UserListNotifier>(
builder: (context, notifier, child) {
if (notifier.isLoading) return CircularProgressIndicator();
if (notifier.errorMessage.isNotEmpty) {
return Text(notifier.errorMessage);
}
return ListView.builder(
itemCount: notifier.users.length,
itemBuilder: (context, index) {
return ListTile(title: Text(notifier.users[index].name));
},
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => Provider.of<UserListNotifier>(context, listen: false).fetchUsers(),
child: Icon(Icons.refresh),
),
);
}
}
Either
with Providerdartz
).Using Either
can be beneficial for projects where clear and consistent error handling is crucial, and the team is comfortable with functional programming concepts. However, for simpler projects or teams not familiar with these concepts, traditional error handling might be more straightforward.
— Rishi Banerjee
July 2023