Best practices in dart null safety

Issue

I’m currently trying to improve null safety in my flutter app, but having relatively less real-world experiences in working with null safety, I’m not confident in some of my decisions.

For example, my app requires user login, and so I have an Auth class that preserves the state of authentication.

class Auth {
  final String? token;
  final User? user;

  Auth(this.token, this.user);
}

In my app, I ensure that the user property is accessed only when the user is logged in, so it’s safe do the following:

final auth = Auth(some_token, some_user);

// and when I need to access the user

final user = auth.user!

which leads to the first question:

Is it recommended to use the null assertion operator in many places in the app?

I personally find it kind of uncomfortable to do things like auth.user!.id throughout the app, so I’m currently handling it like this:

class Auth {
  final User? _user;

  Auth(this._token, this._user);

  User get user {
    if (_user == null) {
      throw StateError('Invalid user access');
    } else {
      return _user!;
    }
  } 
}

but I’m not sure if this is a recommended practice in null-safety.


For the next question, I have a class that handles API calls:

class ApiCaller {
  final String token;
  
  ApiCaller(this.token);
  
  Future<Data> getDataFromBackend() async {
    // some code that requires the token
  }
}

// and is accessed through riverpod provider
final apiCallerProvider = Provider<ApiCaller>((ref) {
  final auth = ref.watch(authProvider);
  return ApiCaller(auth.token);
})

My ApiCaller is accessed through providers and thus the object is created when the App starts. Obviously, it requires token to be available and thus depends on Auth. However, token could also be null when the app starts and the user is not logged in.

Since I’m confident that apiCaller isn’t used when there is no existing user, doing this:

class ApiCaller {
  // make token nullable
  final String? token;
  
  ApiCaller(this.token);
  
  Future<Data> getDataFromBackend() async {
    // some code that requires the token
    // and use token! in all methods that need it
  }
}

final apiCallerProvider = Provider<ApiCaller>((ref) {
  final auth = ref.watch(authProvider);
  if (auth.token == null) {
    return ApiCaller()
  } else {
    return ApiCaller(auth.token);
  }
})

should be fine. However, this also makes me use a lot of token! throughout all methods, and I’m not too sure about that.

I could also simply do ApiCaller('') in the non-null token version, but this seems more of a workaround than a good practice.


Sorry for the lengthy questions. I tried looking for some better articles about real-world practices in null-safety but most are only language basics, so I hope some of you on here could give me some insights. Thanks in advance!

Solution

The simplest way to avoid using ! when you know a nullable variable is not null is by making a non-null getter like you did on your first question:

User get user {
    if (_user == null) {
      throw StateError('Invalid user access');
    } else {
      return _user!;
    }
  } 

I will let you know that there is no need to check if the value is null before throwing an error, the null check operator does exactly that:

Uset get user => _user!;

Unless of course you care a lot about the error itself and want to throw a different error.

As for your second question, that one is a bit trickier, you know you will not access the variable before it is initialized, but you have to initialize it before it has a value, thus your only option is to make it null, I personally don’t like to use the late keyword, but it was built expressly for this purpose, so you could use it. A late variable will not have a value until expressly assigned, and it will throw an error otherwise, another solution is to make a non-null getter like on the other page.

Also, you don’t need a null check here because the result is the same:

if (auth.token == null) {
    return ApiCaller()
  } else {
    return ApiCaller(auth.token);
  }

instead do this:

return ApiCaller(auth.token);

This does feel to me like a simple problem, you are just not used to working with null-safety, which means that to you, the ! looks ugly or unsafe, but the more you work with it the more you’ll become comfortable with it and the less it will look as bad code even if you use it a lot around your app.

Hopefylly, my answer is helpful to you

Answered By – h8moss

Answer Checked By – Gilberto Lyons (FlutterFixes Admin)

Leave a Reply

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