How do I correctly cast a dynamic list to a List<List<String>> without runtime errors when .cast() has no effect? - flutter

I'm receiving data in a MethodCall object, which means that I cannot receive it as any type other than dynamic:
dynamic listOfObjects = methodCall.arguments;
but because I am the one sending the data from the platform-specific code, I know that the data is guaranteed to be of type List<List<String>>.
I want to inflate this data into a collection of concrete Dart object types:
List<DartObject> dartObjects =
methodCall.arguments.map((raw) => DartObject(
prop1: raw[0],
prop2: raw[1],
prop3: raw[2],
)).toList();
but this code fails with this error:
type 'List<dynamic>' is not a subtype of type 'List<String>' in type cast
I've tried extensively to solve this issue on my own:
Dart's own documentation on fixing common type problems,
responses to similar Flutter issues, and
answers to similar Stack Overflow questions
all say to use the List's cast() method, but even this falls over at runtime with the same error:
(call.arguments as List).cast<List<String>>()
// => type 'List<dynamic>' is not a subtype of type 'List<String>' in type cast
I'm sure I must be missing something obvious at this point. What am I doing wrong?
In short, how do I correctly cast to List<List<String>> without copying everything into a new data structure (i.e. without the use of .from or .map)?

The following code should work:
(call.arguments as List<dynamic>).map((e) => (e as List<dynamic>).cast<String>())

Related

How to cast both sides of Either?

I am trying to have Either<Failure, Concrete> where Concrete extends Abstract class, but I cannot get it to work. If I try to cast the left side I get the error:
type 'Right<Failure, Concrete>' is not a subtype of type 'Either<Failure, Abstract>' in type cast
if I try for the right side I get:
type 'Left<Object, Concrete>' is not a subtype of type 'Either<Failure, Concrete>' in type cast
And bimap does not seems to solve it. Is there an easy way to do it? tks

Null check operator used on a null value instagram clone

[VERBOSE-2:ui_dart_state.cc(209)] Unhandled Exception: type 'Null' is not a subtype of type 'Map<String, dynamic>' in type cast
This might have basically happened while parsing json data from server into dart object. The issue basically arises in general whenever the members of the dart class are not nullable in which case they can't be left null at the time of initialisation. Due to which while you use the fromJson factory or function the value assigned to this parameter doesn't have that key available in it as a consequence you receive this error.
This could also arise while using named pushing of routes via arguments.

Why direct usage of generic value is not possible but same is possible if returned from method in Dart

Why is it assigning a value to a generic field is not possible when assigned directly, but same is possible when using a variable reference or method return value (here, same value is assigned to the variable and method returns the same value)?
class User {}
class Teacher extends User {}
class Student extends User {}
Future<User> getUser() {
return Future.value(Student());
}
void main() {
Future<Future<User>> fut = Future.value(getUser()); // <----- No error
Future<Future<User>> fut2 = Future.value(Future.value(Student())); // <----- Getting error
Future<User> userFut3 = Future.value(Student());
Future<Future<User>> fut3 = Future.value(userFut3); // <----- No error
}
Getting below error when Future.value(Future.value(Student())) assigned directly.
Error: The argument type 'Student' can't be assigned to the parameter type 'FutureOr<Future<User>>?'.
The issue here is that the parameter of Future<T>.value has the type FutureOr<T>. It can be either a future or a value.
Also, Dart type inference works by "pushing down" a context type, then trying to make the expression work at that type, and finally pushing the final static type back up.
If an expression like Future.value(...) has a context type, the missing type argument is always inferred from the context type.
When you write
Future<Future<User>> fut2 = Future.value(Future.value(Student()));
the context type of the outer Future.value, the type we know it should have, is Future<Future<User>>. That makes its argument have context type FutureOr<Future<User>>.
The argument is Future.value(Student()), where we don't yet know anything about Student() because we haven't gotten to it in the type inference yet, we're still working out way down towards it.
A Future<X> can satisfy that FutureOr<Future<User>> in two ways, either by being a Future<User> or by being a Future<Future<User>>.
Type inference then guesses that it's the latter. It's wrong, but it can't see that yet. The way type inference works, it has to use the context type when there is one, but the context type is ambiguous, and it ends up choosing the wrong option.
You are hitting an edge case of the type inference where the context type can be satisfied in two different ways, and the downwards type inference chooses the wrong one. It's a good heuristic that if you have a FutureOr<...> context type, and you see a Future constructor, you want the Future-part of the FutureOr<...>. It breaks down when you have FutureOr<Future<...>>. So, don't do that!
My recommendation, in complete generality, is to never have a Future<Future<anything>> in your program. Not only does it avoid problems like this, but it's also a better model for your code.
A future which eventually completes to something which eventually completes to a value ... just make it eventually complete to that value directly. Waiting for the intermediate future is just needless busywork.
Because in the function you defined the return type and dart knows the return type, but when assigning directly dart does not know Future.value(Student()) has a type of Future<User>. to fix this you have to tell the dart the type of the value, like this: Future.value((Future.value(Student())) as Future<User>);
this way dart will know the type of this value and treat it as a Future<User>.

String is not a subtype of int

I want to show suggestions in the search bar. But the highlighted portion of the image is not working i.e when the user inputs some query, it throws an error.
type 'String' is not a subtype of type 'int' of 'index'
Whatever your snapshot.data actually is, you are not handling it correctly.
I suggest you don't operate on raw maps (I guess you got them from a json webservice response?) but instead use model classes in your application, so that you have type safety and don't have to guess and get runtime errors when your guesses turned out to be wrong.
I don't know your models or your backend, so I can only link you this general guideline:
Flutter: Data and Backend - Serializing JSON inside model classes
String is not a subtype of int
This clearly states that, somewhere in your code, you had to pass an int but instead you are passing a String.
If my guess is right, one among this snapshot.data['shop_items']['productTitle'], is supposed to be an int, instead of a String.

The name 'string' isn't a type and can't be used in an 'is' expression

During unit testing of a function returning different types of objects, I need to check if the type of returned object is the same as expected. Therefore, I need to pass multiple classes inside a variable. Then I need to use this variable with the is operator to check types.
final string = String;
assert('foo' is string);
But I am getting
error: The name 'string' isn't a type and can't be used in an 'is' expression.
I read somewhere that a library called Dart:mirrors can solve this problem but I haven't seen an actual example.
In unit testing, you know the expected answer. There shouldn't be a need to make your types variables.
Instead, just assert with the strong typed
assert('foo' is String);
I found the answer. The trick to create an instance of the type that I want to assert, then use runtimeType property.
If a class is called User from a.dart and another one is also called User from b.dart, runtimeType won't be the same
final string = 'anything'.runtimeType;
assert('foo'.runtimeType is string);