Simple error handling in Flutter with Sealed Classes

Aparna Rathore
2 min readAug 14, 2023

--

Sealed classes, also known as sum types or discriminated unions, are a powerful way to handle errors and states in Flutter applications. They provide a structured approach to defining and handling various states and errors that your app can encounter. Here’s how you can use sealed classes for simple error handling in Flutter:

**1. Define Sealed Classes:**
Create a file (e.g., `result_state.dart`) to define your sealed classes for different states and errors. Each sealed class represents a specific state or error with associated data if needed.

```dart
abstract class ResultState<T> {}

class Loading<T> extends ResultState<T> {}

class Success<T> extends ResultState<T> {
final T data;

Success(this.data);
}

class Error<T> extends ResultState<T> {
final String errorMessage;

Error(this.errorMessage);
}
```

**2. Handle Sealed Classes in Widgets:**
In your widget tree, you can use `ResultState` to represent different states and errors.

```dart
import ‘package:flutter/material.dart’;
import ‘package:your_app/result_state.dart’;

class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(‘Simple Error Handling’),
),
body: Center(
child: FutureBuilder<ResultState<String>>(
future: fetchData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text(‘Error: ${snapshot.error}’);
} else if (snapshot.hasData) {
final result = snapshot.data!;
if (result is Success) {
return Text(‘Data: ${result.data}’);
} else if (result is Error) {
return Text(‘Error: ${result.errorMessage}’);
} else {
return Text(‘Unknown State’);
}
} else {
return Text(‘Unknown State’);
}
},
),
),
);
}
}

Future<ResultState<String>> fetchData() async {
try {
// Simulate fetching data
await Future.delayed(Duration(seconds: 2));
return Success(‘Fetched data successfully’);
} catch (e) {
return Error(‘An error occurred’);
}
}

void main() {
runApp(MaterialApp(
home: MyWidget(),
));
}
```

**3. Use Sealed Classes in BLoCs:**
You can also use sealed classes in conjunction with BLoCs to manage state and error handling.

```dart
class MyBloc extends Bloc<MyEvent, ResultState<String>> {
MyBloc() : super(Loading());

@override
Stream<ResultState<String>> mapEventToState(MyEvent event) async* {
if (event is FetchDataEvent) {
yield Loading();
try {
final data = await fetchData();
yield Success(data);
} catch (e) {
yield Error(‘An error occurred’);
}
}
}
}
```

Using sealed classes in combination with BLoCs provides a structured way to manage different states and errors, making your code more organized and maintainable. This approach can help you handle various scenarios in a more comprehensive and consistent manner.

--

--