Creating function with variable number of arguments or parameters in Dart
You can't do that for now.
I don't really know if varargs will come back - they were there some times ago but have been removed.
However it is possible to emulate varargs with Emulating functions. See the below code snippet.
typedef OnCall = dynamic Function(List arguments);
class VarargsFunction {
VarargsFunction(this._onCall);
final OnCall _onCall;
noSuchMethod(Invocation invocation) {
if (!invocation.isMethod || invocation.namedArguments.isNotEmpty)
super.noSuchMethod(invocation);
final arguments = invocation.positionalArguments;
return _onCall(arguments);
}
}
main() {
final superHeroes = VarargsFunction((arguments) {
for (final superHero in arguments) {
print("There's no stopping ${superHero}");
}
}) as dynamic;
superHeroes('UberMan', 'Exceptional Woman', 'The Hunk');
}
Dart does indirectly support var-args as long as you as you aren't too much into syntactic brevity.
void testFunction([List<dynamic> args=[]])
{
for(dynamic arg:args)
{
// Handle each arg...
}
}
testFunction([0, 1, 2, 3, 4, 5, 6]);
testFunction();
testFunction([0, 1, 2]);
Note: You can do the same thing with named parameters, but you'll have to handle things internally, just in case if the user (of that function; which could be you) decides to not pass any value to that named parameter.
I would like to thank @Ladicek for indirectly letting me know that a word like brevity exists in English.
I played around a little with Alexandre Ardhuin's answer and found that we can tweak a couple of things to make this work in the current version of Dart:
class VarArgsClass {
noSuchMethod(InvocationMirror invocation) {
if (invocation.memberName == 'superheroes') {
this.superheroes(invocation.positionalArguments);
}
}
void superheroes(List<String> heroNames) {
for (final superHero in heroNames) {
print("There's no stopping ${superHero}!");
}
}
}
main() {
new VarArgsClass().superheroes('UberMan', 'Exceptional Woman', 'The Hunk');
}
This has lots of problems, including:
- A warning is generated wherever you call
superheroes()
because the signature doesn't match your parameters. - More manual checking would need to be done to make sure the list of arguments passed to superheroes is really a
List<String>
. - Needing to check the member name in
noSuchMethod()
makes it more likely you'll forget to change the 'superheroes' string if you change the method name. - Reflection makes the code path harder to trace.
BUT if you are fine with all of those issues, then this gets the job done.
This version:
- Works with both positional and keyword arguments.
- Supports typing of the return value.
- Works with modern Dart.
typedef VarArgsCallback = void Function(List<dynamic> args, Map<String, dynamic> kwargs);
class VarArgsFunction {
final VarArgsCallback callback;
static var _offset = 'Symbol("'.length;
VarArgsFunction(this.callback);
void call() => callback([], {});
@override
dynamic noSuchMethod(Invocation inv) {
return callback(
inv.positionalArguments,
inv.namedArguments.map(
(_k, v) {
var k = _k.toString();
return MapEntry(k.substring(_offset, k.length - 2), v);
},
),
);
}
}
void main() {
dynamic myFunc = VarArgsFunction((args, kwargs) {
print('Got args: $args, kwargs: $kwargs');
});
myFunc(1, 2, x: true, y: false); // Got args: [1, 2], kwargs: {x: true, y: false}
}
Thanks, Alexandre for your answer!
If you are really into syntactic brevity, just declare a function/method with say 10 optional positional parameters and be done. It's unlikely someone will call that with more than 10 arguments.
If it sounds like a hack, that's because it is a hack. But I've seen the Dart team doing the same :-)
For example:
void someMethod(arg0, [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9]) {
final args = [arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9];
args.removeWhere((value) => value == null);
/* do something the the args List */
print(args);
}