flutterdartvaluechangelistener

How can you call a void function in Flutter without passing a parameter?


I'm new to Flutter and am working through the intro course on Udacity. In one of the tasks, I was trying to follow the code and I can't make much sense of it. Here's the code from the solution of the project (I've cut and paste the parts that matter, and also legal disclaimer I do not own any of this code, it's from the sample Flutter Udacity project):

Widget build(BuildContext context) {
    final input = Padding(
      padding: _padding,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [

          TextField(...),

          _createDropdown(_fromValue.name, _updateFromConversion),
        ],
      ),
    );
}

void _updateFromConversion(dynamic unitName) {
    setState(() {
      _fromValue = _getUnit(unitName);
    });
    if (_inputValue != null) {
      _updateConversion();
    }
  }

Widget _createDropdown(String currentValue, ValueChanged<dynamic> onChanged) {
    return Container(
      margin: EdgeInsets.only(top: 16.0),
      decoration: BoxDecoration(...),

      padding: EdgeInsets.symmetric(vertical: 8.0),
      child: Theme(...),

        child: DropdownButtonHideUnderline(
          child: ButtonTheme(
            alignedDropdown: true,
            child: DropdownButton(
              value: currentValue,
              items: _unitMenuItems,
              onChanged: onChanged,
              style: Theme.of(context).textTheme.title,
            ),
          ),
        ),
      ),
    );
}

Here's where I'm stuck. _updateFromConversion requires an input parameter unitName. But when they call it, in _createDropdown, they don't pass any. So how does _updateFromConversion know what unitName is? Also, is _updateFromConversion executed before _createDropdown, or is it executed when the "onChanged" property of DropdownButton is set?

Second question: they're passing that function with return type void into _createDropdown, which is expecting ValueChanged. Shouldn't this throw an error?

If someone can explain the flow of this code and what I am missing I would greatly appreciate it. Thank you!


Solution

  • You seem to be misunderstanding the assignment of a variable to a function for a function call.

    Let me try to show it with example code.

    void _updateFromConversion(dynamic unitName) {
        print("Unit name: $unitName");
    }
    
    class SomeClass {
        void Function(dynamic arg) myFunction;
    }
    
    void main() {
        final c = SomeClass()..myFunction = _updateFromConversion;
        print("Created c. Calling its function");
        c.myFunction("foo");
        print("Done");
    }
    

    When you run this code, you will see this printed:

    Created c. Calling its function
    Unit name: foo
    Done
    

    This shows that the _updateFromConversion function is not called when you create the SomeClass instance in SomeClass()..myFunction = _updateFromConversion;. This is only an assignment (it assigns to the field myFunction the value _updateFromConversion... yes, in Dart a function itself can be a value)!

    You should know that because there's no () after the function name, and function invocation in Dart always must contain the list of arguments between () even if it's empty.

    So, here's where the function is invoked:

    c.myFunction("foo");
    

    See? There's a list of arguments containing a single value, "foo". That's why the function then prints Unit name: foo, because the argument unitName takes the value "foo".

    TL;DR

    This is a function invocation:

    c.myFunction("foo");
    

    This is NOT:

    c.myFunction;