0

I have a class:

class Foo {
  void bar() {}
}

And I'm using it like this:

Widget build() {
  late Foo foo;

  return Column(
    children: [
      ElevatedButton(
        onPressed: foo.bar, // Error: 
        child: Text('Use'),
      ),
      ElevatedButton(
        onPressed: () => foo = Foo(), // Assigning foo here
        child: Text('Initialize'),
      ),
    ],
  );
}

The late local variable 'foo' is definitely unassigned at this point. (Documentation)

As you can see I'm using the late keyword to give a hint to the analyzer that I'll be instantiating later, but I still see an error, why?


Note: I'm NOT looking for solutions on HOW to make it work but rather WHY it's not working?

iDecode
  • 22,623
  • 19
  • 99
  • 186

1 Answers1

0

Because you are creating the list right away as part of creating the Column object for the children argument. The list contains two ElevatedButton objects which are also created right away as part of creating the list. For creating the first ElevatedButton we provide two arguments, onPressed and child with values which are resolved and send to the ElevatedButton constructor.

The problem is then that to resolve foo.bar it needs get bar from foo. But foo is late and has definitely not been assigned any value at this point since no other code have been running which could provide a value for it.

Notice that when we provide arguments to methods/constructors the values we getting are resolved before we run code inside the method/constructor. Also, we are getting a copy of the reference, so foo.bar needs to be resolved to some value since we cannot use this as some kind of pointer to the foo variable inside build() and later check if it is set to a value inside the ElevatedButton object.

julemand101
  • 28,470
  • 5
  • 52
  • 48
  • Thanks for your answer, I knew almost everything you mentioned but my question was `onPressed: () => foo.bar()` is allowed but this is not `onPressed: foo.bar`. Do you mean that in former part, we are lazily invoking the call and in the later we are instantly invoking it? – iDecode Jan 09 '22 at 23:20
  • `onPressed: () => foo.bar()` gives `onPressed` a function, which when executed runs `foo.bar()`. `onPressed: foo.bar` means resolve what function `foo.bar` points to right now, and give that function as parameter to `onPressed`. In both cases, we need to know for sure what function we need to give `onPressed`. But in the first case, the provided function is going to be a unnamed function we create at the spot. That function contains then a call `foo.bar()`. But this is not needed to be resolved before we call this unnamed function. – julemand101 Jan 10 '22 at 00:30
  • Again, parameters needs to be resolved to a value before entering the function itself. See it as if you have a function taking a `String` parameter. This parameter can come from a lot of different operations, but when you are inside your function, the parameter is a `String` and you can request it as much you like and it will keep the value because the value have been resolved before going into your method. – julemand101 Jan 10 '22 at 00:36
  • I should add that `onPressed: () => foo.bar()` (different from your question) is potentially not safe since we don't know when the function given as `onPressed` is executed and so if `foo` have got any value at that point. But Dart does not complain about this risk because `foo` is marked `late` and it is your job as a developer to ensure this is safe. The reason you get an error for `onPressed: foo.bar` is, again, that we know for sure here that it is impossible for `foo` to have gotten any value in your example and Dart can therefore give you an warning even if the field is marked `late`. – julemand101 Jan 10 '22 at 00:50