“The argument type ‘String?’ can’t be assigned to the parameter type ‘String'” when using stdin.readLineSync()

Welcome to Dart. What you are experience is the new null-safety feature (introduced with Dart 2.12.0) where variables by default cannot contain the value null. This is statically checked so you will get an error even before your program are executed.

In your example the problem is the following line:

var input = stdin.readLineSync();

If we check the manual we can see this method have the following signature:

String? readLineSync (
    {Encoding encoding = systemEncoding,
    bool retainNewlines = false}
) 

https://api.dart.dev/stable/2.12.2/dart-io/Stdin/readLineSync.html

String? means it is a nullable type and is therefore allowed to contain any String or null. If the type instead was String it would mean the result can only be a String and never null.

This means that your variable input now has the type String?. This (introduced with 2.12.0) is a problem if you take a look at the second line of your example:

var idade = int.parse(input);

Since the signature of int.parse is:

int parse (

    String source,
    {int? radix,
    @deprecated int onError(
        String source
    )}

) 

https://api.dart.dev/stable/2.12.2/dart-core/int/parse.html

Which takes a String (which can therefore never be null). You are therefore not allowed to give your String? as parameter to a method which only handles String. The opposite would have been allowed (if method takes String? and you give it a String).

So what can we do about this situation? Well, you need to handle the case of null or tell Dart that it should just crash your program if input are null.

So you could handle it like:

import 'dart:io';

void main() {
  print("Entre com a sua idade: ");
  var input = stdin.readLineSync();
  
  if (input != null) {
    var idade = int.parse(input);

    if (idade >= 18) {
      print("É maior de idade");
    } else {
      print("É menor de idade");
    }
  } else {
    print('Input was null!');
  }
}

As you can see, this is allowed even if input are technically still of the type String?. But Dart can see that your if statement will prevent input to have the value null so it will be “promoted” to String as long as you are inside this if-statement.

Another solution is to tell Dart that it should stop complain about statically errors about input and just assume input is String when compiling the code:

import 'dart:io';

void main() {
  print("Entre com a sua idade: ");
  var input = stdin.readLineSync()!;
  var idade = int.parse(input);

  if (idade >= 18) {
    print("É maior de idade");
  } else {
    print("É menor de idade");
  }
}

(The change is the ! added after readLineSync())

Dart will in this case instead add its own null-check and crash the program if stdin.readLineSync does give a null value. This is to ensure that we are still never going to give int.parse a null value which could make your code crash somewhere else. By adding the null-check where we get the questionable value, we can ensure we fails fast and at the place where things was not as we expected it to be.

You can read a lot more about null-safety in Dart here (also linked to by Stephen): https://dart.dev/null-safety/understanding-null-safety

Leave a Comment