In Flutter/Dart the examples sometimes show fat arrow and sometimes dont. Here are examples:
RaisedButton(
onPressed: () {
setState(() {
_myTxt = "Text Changed";
});
},
Elsewhere you see:
void main() => runApp(MyApp());
The fat arrow syntax is simply a short hand for returning an expression and is similar to (){ return expression; }.
According to the docs.
Note: Only an expression—not a statement—can appear between the arrow (=>) and the semicolon (;). For example, you can’t put an if statement there, but you can use a conditional expression
void main(){
final cls = TestClass();
cls.displayAnInt((){
//you can create statements here and then return a value
int num1 = 55;
int num2 = 1;
int sum = num1 + num2;
return sum;
});
cls.displayAnInt(() => 55 + 1); // simply return an int expression
}
class TestClass{
displayAnInt(makeIntFunc){
int intValue = makeIntFunc();
print('The int value is $intValue');
}
}
From the code above, You can see that multiline statement can be made when the callback function is used and then a value is returned, while the fat arrow simply has an expression with no return keyword.
Considering your answer about fat arrows not supporting multiline statements in dart. This is quite understandable since doing () => {somtheing} would imply you are returning a map and it would expect to see something like () => {'name':'John', 'age':25} and not () => { _myTxt = "Text Changed";_myTxt = "Never Mind"; } .
=> is used to return a value of an anonymous function.
() {} lets you execute multiple statements.
while
() => {myVar} or () => myVar; allows one single statement.
() => myVar; is short and simple when returning one statement.
The same logic goes for creating non anonymous functions too.
Single statement func
func() => y = x + x;
Multiple statement func
func () {
x = x + x;
print(x + ' value of x');
};
I found that the mean the exact same thing. The only difference is that you can use (you don't have to) the fat arrow if there is only one statement. Following is the above RaisedButton declaration with the fat arrow. Notice I had to remove two curly braces and one semi-colon:
RaisedButton(
onPressed: () {
setState(() =>
_myTxt = "Text Changed"
);
},
If you are used to other languages that allow you to put multiple statements after a fat arrow you'll you'll find that you can't in dart and if you try you'll get an error as in the following:
this wont work
RaisedButton(
onPressed: () {
setState(() => {
_myTxt = "Text Changed";
_myTxt = "Never Mind";
});
},
They are both for expressing anonymous functions. The fat arrow is for returning a single line, braces are for returning a code block.
A fat arrow trying to return a code block will not compile.
There seems to be one difference at least in case of Dart version 2.10:
If the expression to be executed is a Future, then the execution order is not the same.
=>
new Future(() => print('future #1 of 2'))
.then((_) => new Future(() => print('future #1a (a new future)')))
.then((_) => print('future #1b'));
new Future(() => print('future #2 of 2'))
.then((_) => new Future(() => print('future #2a (aa new futue)' )))
.then((_) => print('future #2b'));
The result is:
future #1 of 2
future #2 of 2
future #1a (a new future)
future #1b
future #2a (aa new futue)
future #2b`
{} :
new Future(() => print('future #1 of 2'))
.then((_) => new Future(() => print('future #1a (a new future)')))
.then((_) => print('future #1b'));
new Future(() => print('future #2 of 2'))
.then((_) { new Future(() => print('future #2a (aa new futue)' )); })
.then((_) => print('future #2b'));
The result is
future #1 of 2
future #2 of 2
future #2b
future #1a (a new future)
future #1b
future #2a (a new futue)
Fat Aarrow => Single line of code => Expression form, does not use return statement, the expression is automatically returned
void main() => runApp(MyApp()); // you cannot specify return here. This
is the turned value from the function. This is shorthand form
No fat arrow, uses {}, can have multiple statements, have to use return statement if we want to return a value, if not return can be skipped
setState(() {
_myTxt = "Text Changed";
});
Related
We are able to define an async inline method in different ways, Is there any difference in the result or under the hood?
1.
Future<void> counter() async => await Future<void>.delayed(
const Duration(seconds: 5),
() => print('Result'),
);
2.
Future<void> counter() async => Future<void>.delayed(
const Duration(seconds: 5),
() => print('Result'),
);
3.
Future<void> counter() => Future<void>.delayed(
const Duration(seconds: 5),
() => print('Result'),
);
Timing, if anything.
The async => await version waits for the delayed future created by Future.delayed to complete before returning, then it completes the returned future.
The async => version should do precisely the same, since the await is implicit in the return.
The => version returns the future created by Future.delayed directly.
In either case, the returned future will be completed after the 5 second duration has passed. It may or may not go through an extra intermediate microtask.
Got some example.
void main() {
A a = A();
transform(a);
print(a.str + ' sync');
}
Future<void> transform(A a) async {
await Future.delayed(Duration(seconds: 3), () => a.str = 'B');
print(a.str + ' async');
}
class A {
String str = 'A';
}
And output:
A sync
B async
And next example
void main() {
A a = A();
transform(a);
print(a.str + ' sync');
}
Future<void> transform(A a) async {
Function f = () {
a.str = 'B';
};
await Future.delayed(Duration(seconds: 3), f());
print(a.str + ' async');
}
class A {
String str = 'A';
}
With output:
B sync
B async
Am i right thinking that f() is executed but just not returned in the second case, so i've got the a.str value modified imidiatly and returned later? Or what the right answer?
In the second example you have the following line:
await Future.delayed(Duration(seconds: 3), f());
What you are doing here is executing the function pointed at by the variable f and uses the result from this method as the second argument to Future.delaye. The reason for this is that you have () right after the name of the variable f, which is used to indicate you want to execute the method pointed at by f without any arguments.
What you should have done instead (if you want to send the function itself as the second argument for Future.delayed) is just use f:
await Future.delayed(Duration(seconds: 3), f);
await Future.delayed(Duration(seconds: 3),(){f();});
here what i wanted
Why is the follow code valid? I thought there should be compile-time errors. I thought the return in the body return an int(value 1). If not, it must be returning a Future, which does not comply to the return type either. What has happened with the await?
void longRun() async {
return await Future.delayed(
Duration(seconds: 3),
()=>1,
);
}
If I assign the await part to a variable, like the following, the compiler starts to complain, which is obvious and easy to understand. But why doesn't the code above complain?
void longRun() async {
var foo = await Future.delayed(
Duration(seconds: 3),
()=>1,
);
return foo;
}
Keep studying I've found more confusing thing: all the following versions work. They seem identical, wired.
version1:
void longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
version2:
Future<void> longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
version3:
Future<int> longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
version4:
Future longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
version5:
Future longRun() async {
await Future.delayed(Duration(seconds:2), ()=>1);
}
version6:
void longRun() async {
await Future.delayed(Duration(seconds:2), ()=>1);
}
This is mostly about the behavior of =>, not about await. Normally () => 1 can be thought of as shorthand for () { return 1; }, but that's actually an oversimplification.
=> is allowed for void functions as a convenience so that people can write things like:
bool functionWithSideEffect() {
print('Some side effect');
return true;
}
void foo() => functionWithSideEffect();
int _someValue = 0;
set someValue(int value) => _someValue = value;
even though these aren't legal:
var foo() {
// error: A value of type 'bool' can't be returned from the function 'foo'
// because it has a return type of 'void'.
return functionWithSideEffect();
}
set someValue(int value) {
// error: A value of type 'int' can't be returned from the function
// 'someValue' because it has a return type of 'void'.
return _someValue = value;
}
The crux of your confusion is that () => 1 either could be int Function() or void Function(), and type inference picks different things given different constraints.
When you do:
void longRun() async {
return await Future.delayed(
Duration(seconds: 3),
()=>1,
);
}
then type inference works outside-in, propagating the void return type of longRun through the unconstrained, returned expression. The Future.delayed call is inferred to be Future<void>.delayed and the () => 1 callback is inferred to be void Function(). (Arguably a void async function returning Future<void> could be treated as error.)
In contrast, when you do:
void longRun() async {
var foo = await Future.delayed(
Duration(seconds: 3),
()=>1,
);
return foo;
}
now type inference runs in the other direction (inside-out): foo has no explicit type, so it does not constrain the right-hand-side. Therefore () => 1 is instead assumed to be int Function(), which causes Future.delayed to be inferred to be Future<int>.delayed and foo to be inferred as an int. Since foo is an int, attempting to return it from a function declared to return void is an error.
version2:
Future<void> longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
You've changed longRun's return type from void to Future<void>. async functions usually should return a Future but may also return void to be fire-and-forget. The only difference from version 1 is that callers can now wait (fire a callback) when longRun completes. () => 1 is still inferred to be void Function().
version3:
Future<int> longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
Same as version2 except longRun returns Future<int> instead of Future<void>. Now () => 1 is inferred to be int Function().
version4:
Future longRun() async {
return await Future.delayed(Duration(seconds:2), ()=>1);
}
longRun's return type is now Future, which means Future<dynamic>. () => 1 is inferred to be dynamic Function().
version5:
Future longRun() async {
await Future.delayed(Duration(seconds:2), ()=>1);
}
Same as version4 except there is no explicit return statement. Now the Future.delayed expression is no longer constrained by longRun's return type, and inference will flow inside-out; () => 1 is assumed to be int Function().
version6:
void longRun() async {
await Future.delayed(Duration(seconds:2), ()=>1);
}
Same as version5 except that longRun returns void and is a fire-and-forget function. Callers cannot be notified when longRun completes.
(Note that in the above, when I write () => 1 is inferred to be int Function() or void Function(), it really should be FutureOr<int> Function() or FutureOr<void> Function(), but that's not the important part.)
Edit:
Pre-emptively addressing some potential follow-up questions:
Why is:
void longRun() async {
int f() {
return 1;
}
return await Future<void>.delayed(
Duration(seconds: 3),
f,
);
}
okay, but:
void longRun() async {
return await Future<void>.delayed(
Duration(seconds: 3),
// info: Don't assign to void.
() {
return 1;
},
);
}
generates an analysis complaint? Both versions are legal because substituting a Future<U> for a Future<T> is legal when U is a subtype of T. When T is void, U can be anything; the value can just be ignored. (In Dart, partly for historical reasons when it didn't always have a void type, void actually means that the value cannot be used and not that there is no value. void x = 42; is legal, but attempting to read the value of x would be an error. This is why the "Don't assign to void" analysis complaint isn't categorized as an error.)
In the second version that uses an anonymous function, the tighter locality of the return statement allows the analyzer to perform more extensive checks.
I have a Map<String, Stream<dynamic>> and I want to convert it into a Stream<Map<String, dynamic>> which should yield the latest snapshot of values from the former. I need something like Rx.combineLatest( mapOfStreams.values, (list) => list ) but somehow keeping map keys.
I've found this solution, which seems to work (supposing keys and values of Map are always aligned), but I feel there must be something better:
final keys = mapOfStreams.keys.toList();
Rx.combineLatest( mapOfStreams.values, (list) => list.asMap().map( (i, value) => MapEntry(keys[i], value) ) );
This is my way
void main() async {
final streams = <String, Stream<dynamic>>{
'a': Stream<int>.periodic(const Duration(milliseconds: 200), (i) => i),
'b': Stream<int>.periodic(const Duration(milliseconds: 500), (i) => i),
'c': Stream<String>.value('Hello'),
};
final final$ = Rx.combineLatest(
streams.entries
.map((entry) => entry.value.map((v) => MapEntry(entry.key, v))),
(List<MapEntry<String, dynamic>> values) => Map.fromEntries(values),
);
final$.listen(print);
await Future.delayed(const Duration(seconds: 10));
}
I'm trying to push a route to display a ListView item when that item is tapped (onTap). The onTap property for InkWell is defined as a VoidCallback. However, I would like to be able to receive a return value from the callback, indicating whether the user has modified said item in the dialog that was opened.
Assigning a Future<Null> async function to onTap seems to be OK, as per the Shrine Demo (shrine_home.dart in the flutter_gallery example):
Future<Null> _showOrderPage(Product product) async {
final Order order = _shoppingCart[product] ?? new Order(product: product);
final Order completedOrder = await Navigator.push(context, new ShrineOrderRoute(
order: order,
builder: (BuildContext context) {
return new OrderPage(
order: order,
products: _products,
shoppingCart: _shoppingCart,
);
}
));
assert(completedOrder.product != null);
if (completedOrder.quantity == 0)
_shoppingCart.remove(completedOrder.product);
}
Why and how is this working? Thanks.
Dart allows you to use a function with a non-void return value as a function with a void return value. The function will still return values as normal, but the analyzer will infer that they're of the void type and will complain if you try to use them, unless you cast them back to something else.
typedef void VoidCallback();
typedef int IntCallback();
final IntCallback i = () => 42;
void main() {
final VoidCallback v = i;
print("${v()}"); // prints 42
print(v() as int); // also prints 42
print(v().toString()); // also prints 42, but analyzer complains:
// method 'toString' isn't defined for class 'void'
}
That is why you can use a function that returns a Future<Null> as a VoidCallback.
Oops. Just realized the assignment was actually via a lambda, which is a VoidCallback:
onPressed: () { _showOrderPage(product); }
Still - it seems to be working for me in another place, where I assign a Future<Null> async function to the onPressed method of FloatingActionButton...