Proper way to Handle Exceptions in Flutter

By | December 23, 2019

Today we will see how we can properly handle errors and exceptions in Flutter.

Watch Video Tutorial


For this example we will be doing a service call and handle exceptions related to that.

We will create a sample service here


 static const String url = 'https://jsonplaceholder.typicode.com/users';

 static Future<List<User>> getUsers() async {
    try {
      final response = await http.get(url);
      if (200 == response.statusCode) {
        List<User> users = parseUsers(response.body);
        return users;
      } else {
        return List<User>();
      }
    } catch (e) {
      throw Exception(e.message);
    }
  }

  static List<User> parseUsers(String responseBody) {
    final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
    return parsed.map<User>((json) => User.fromJson(json)).toList();
  }

// User.dart
class User {
  int id;
  String name;
  String email;

  User({this.id, this.name, this.email});

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'] as int,
      name: json['name'] as String,
      email: json['email'] as String,
    );
  }
}

In the above example we are catching all exceptions using a simple try catch block which is not suitable since there can be a variety of Exceptions in this scenario like a SocketException, HttpException or a FormatException.

So in that case how do we catch those exceptions separately and provide appropriate messages to the user.


Now the beauty of dart is you can throw any custom object as Exception. So lets create some custom classes to throw for each exception above.

class NoInternetException {
  String message;
  NoInternetException(this.message);
}

class NoServiceFoundException {
  String message;
  NoServiceFoundException(this.message);
}

class InvalidFormatException {
  String message;
  InvalidFormatException(this.message);
}

class UnknownException {
  String message;
  UnknownException(this.message);
}

Now we will modify our function to get the users above like below

import 'dart:io';

import 'package:http/http.dart' as http;
import 'dart:convert';
import 'User.dart';
import 'Exceptions.dart';

class Services {
  static const String url = 'https://jsonplaceholder.typicode.com/users';

  static Future<List<User>> getUsers() async {
    try {
      final response = await http.get(url);
      if (200 == response.statusCode) {
        List<User> users = parseUsers(response.body);
        return users;
        //throw Exception('Unknown Error');
      } else {
        return List<User>();
      }
    } on SocketException catch (e) {
      throw NoInternetException('No Internet');
    } on HttpException {
      throw NoServiceFoundException('No Service Found');
    } on FormatException {
      throw InvalidFormatException('Invalid Data Format');
    } catch (e) {
      throw UnknownException(e.message);
    }
  }

  static List<User> parseUsers(String responseBody) {
    final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
    return parsed.map<User>((json) => User.fromJson(json)).toList();
  }
}

In the UI, we will catch the exception like this

 list() {
    return FutureBuilder(
      future: users,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          List<User> users = snapshot.data;
          if (users.isEmpty) {
            return showError('No Users');
          }
          return Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: users
                .map(
                  (user) => Padding(
                    padding: EdgeInsets.all(10.0),
                    child: Text(
                      user.name,
                      style: TextStyle(
                        fontSize: 20.0,
                      ),
                    ),
                  ),
                )
                .toList(),
          );
        }
        if (snapshot.hasError) {
          if (snapshot.error is NoInternetException) {
            NoInternetException noInternetException =
                snapshot.error as NoInternetException;
            return showError(noInternetException.message);
          }
          if (snapshot.error is NoServiceFoundException) {
            NoServiceFoundException noServiceFoundException =
                snapshot.error as NoServiceFoundException;
            return showError(noServiceFoundException.message);
          }
          if (snapshot.error is InvalidFormatException) {
            InvalidFormatException invalidFormatException =
                snapshot.error as InvalidFormatException;
            return showError(invalidFormatException.message);
          }
          UnknownException unknownException =
              snapshot.error as UnknownException;
          return showError(unknownException.message);
        }
        return CircularProgressIndicator();
      },
    );
  }

In the above code, we catch each exception accordingly and show the correct error.


Leave a Reply

Your email address will not be published. Required fields are marked *