I am very new to Dart/Flutter and I have a confusion regarding the => notation. The documentation says that the => notation is used as a shorthand to return a single expression.
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
My doubt comes when I am trying to set state in a flutter application.
RaisedButton(
onPressed: () => {
setState(() {
print('hello');
_products.add('More stuff');
})
},
child: Text('Add Product'),
),
Now when i change the setState method with => notation
RaisedButton(
onPressed: () => {
setState(() => {
print('hello'),
_products.add('More stuff'),
})
},
child: Text('Add Product'),
),
Both methods mentioned above work, that is they set the state as expected. All i had to do was change the semicolons to commas when using the fat arrow notation.
What is the logic behind this ? How is the fat arrow notation working with curly braces which contains multiple expressions within it.
Edit
As mentioned by Hemanth Raj the => returns a set and the code segment containing the => notation can be written as follows.
RaisedButton(
onPressed: () => {
setState(() {
return {
print('hello'),
_products.add('More stuff'),
};
})
},
child: Text('Add Product'),
),
How is the returned set containing a print function and _products.add actually updating the state. Shouldn't it throw some kind of error because usually setState is done by an expression such as _products.add('More stuff');.
This is one of the interesting questions that I would love to answer.
As the official documents say here, yes => is used as a shorthand syntax to { return ... } which means => will just return whatever is produced on the righthand side.
Also from Dart 2.2 and above, a Set can be defined with comma separated values enclosed in a {} as mentioned in docs here.
Hence, the syntax you are using, i.e {} with statements separated with a comma, it is treated as a Set by the => functions. Each element being a function call, () => { f(a) , f(b), g(a),} would return a Set with the elements returned by each function call.
This example might help you understand what is happening under the hood:
dynamic reflect(dynamic a){
return a;
}
void main() {
Function shortHand = () => {reflect(1),reflect('a'),reflect({}),reflect([]),}; // this function when called will return a Set<dynamic>
print(shortHand().runtimeType); // will print `_LinkedHashSet<dynamic>`
}
So the syntax
() => '...' returns a String,
() => [ ... , ..., ...] returns a List
and similarly () => { ... , ... , ... } actually returns a Set
Note: This method of returning set with comma separated function calls is not recommended, would request you also not to use it unless you wanted a Set to be returned as result
Reply to the Edit :
Let me breakdown the function call and results for you. So your code goes like this,
() => {
setState(() {
return {
print('hello'),
_products.add('More stuff'),
};
})
}
Here the => returns a Set with the result of setState, i.e it'll return { (result of setState call) } which might be { null }
As you have call setState the below code gets executed, which again returns a Set with { (result of print), (result of _product.add), }
() {
return {
print('hello'),
_products.add('More stuff'),
};
}
State will update, as you are executing _products.add('More stuff'), where 'More stuff' will be added to _products irrespective of where you call it. When setState is being called, the widget will be rebuilt with the _products with new data added.
Hope this helped!
For the record, the recommended syntax for what you are doing is:
RaisedButton(
onPressed: () {
setState(() {
print('hello');
_products.add('More stuff');
});
},
child: Text('Add Product'),
),
The syntax (args) => { statements } is not how Dart writes function bodies, you do either (args) { statements } or (args) => singleExpression.
Also, you need to terminate statements with semicolons, ;, not commas.
As others have pointed out, the syntax you use (args) => { print("something"), somethingElse } is actually creating a set (a Set<void> because the return type of print is void) and returning that.
This is a perfect storm of small syntax mistakes, which would seem reasonable to a JavaScript programmer, that comes together to actually mean something completely different in Dart.
And, just to make things even worse, the code works. The set literal will evaluate its expression in order, and nobody sees the created set anyway. The syntax just doesn't generalize — you can't change any of the expressions to, say, a for-loop (yet, you will be able to in the next version of Dart).
So, in Dart, never use => { unless you want to return a set or map.
Related
I know the usage of anonymous function with () {} but what () {}() mean?
I'm using it in a Text widget like this:
new Text(
() {
return "hello";
}(),
),
It works pretty well but why do I need to add () after the anonymous function?
Without the second set of parentheses, you're just declaring an anonymous function, nothing else. So without that second set, you would be passing a function reference to the Text widget.
Now when you add the second set of parentheses you're actually calling the function that you defined.
If you look at it another way it might make it more clear. If you give that anonymous function a name you'll get the same result.
Function test = () {
return "hello";
};
print(test());
is equivalent to
print(() {
return "hello";
}());
I am currently experimenting with the flutter framework and dart and stumbled across a seemingly strange behaviour I fail to understand. Even though the context in which the actual problem occurs is way more complicated, I was even able to replicate it in an extremely simplified showcase:
Stream<Either<String, int>> result1 = Stream.fromIterable([1, 2, 3, 4, 5])
.map((number) => number < 4 ? Right(1) : Left('error'))
.onErrorReturnWith((error) => Left('error'));
While the sample above compiles uncontradicted, I do get a compile error for the sample below:
Error: A value of type 'Left<String, dynamic>' can't be assigned to a
variable of type 'Right<dynamic, int>'
Stream<Either<String, int>> result2 = Stream.fromIterable([1, 2, 3, 4, 5])
.map((number) => Right(1))
.onErrorReturnWith((error) => Left('error'));
Is anyone capable to shed some light to this manner?
########################################################
Another example:
Future<Either<String, int>> someWebCall() {
Future<int> response = Future.delayed(Duration(milliseconds: 200), () {
throw SocketException('Well... ...it happens you know...');
});
return response.then((number) {
return number > 50.0 ? Right(number) : Left('number is too small...');
}).catchError((error) {
return Left('are you already questioning the meaning of your life?');
});
}
This compiles but ends with a runtime error:
type 'Future' is not a subtype of type 'Future<Either<String, int>>'
Then I tried to give as many hints to the compiler as I could coming up with this approach:
Future<Either<String, int>> someWebCall() {
Future<int> response = Future.delayed(Duration(milliseconds: 200), () {
throw SocketException('Well... ...it happens you know...');
});
return response.then<Either<String, int>>((number) {
return number > 50.0 ? Right(number) : Left('number is too small...') as Either<String, int>;
}).catchError((error) {
return Left('are you already questioning the meaning of your life?') as Either<String, int>;
});
}
Now I am getting:
type 'Left<String, dynamic>' is not a subtype of type 'Either<String, int>' in type cast
I really can't wrap my head around this
The type of the function (number) => Right(1) is Right<dynamic, int> Function(int), which means that the resulting stream of the map call is a Stream<Right<dynamic, int>>.
The onErrorReturnWith needs to return something of the same type as the elements of the stream it's called on, but it returns Left<String, dynamic>, not Right<dynamic, int>.
The simplest fix is to tell the map call what type to return:
...
.map<Either<String, int>>( .... )
Then the types should be what you expect (and not Either<dynamic, dynamic> like the first example likely inferred).
I finally figured out whats happening by diving into the types of dartz.
The problem is that the compiler is incapable of infering the type of Either in contexts where only either Left or Right is being used. I.e. Left('') the compiler can infer the Left part of Either as being as string and in Right(5) its capable of inferring the right part of Either as int. He is incapable however to figure out the other part respectively.
Using the code below works as indended.
Future<Either<String, int>> someWebCall() {
Future<int> response = Future.delayed(Duration(milliseconds: 200), () {
throw SocketException('Well... ...it happens you know...');
});
return response.then((number) {
return number > 50.0 ? Right(number) : Left('number is too small...');
}).catchError((error) {
return Left<String, int>('are you already questioning the meaning of your life?');
});
what I mean... when I write code in Flutter (Dart) every time I have to call some callbacks sometimes they are: (_){} while other times they are: (_) => {} .
With or without parameters _ present.
To my knowledge, they should both be called lamda functions; and lambda functions should be other kind of write funtions
(_){} has signature Null Function
(_) => {} has signature Map Function() and it is short form (lambda) for
() {
return {};
}
This variant returns Map.
Full example (dartpad):
typedef SimpleFun = Null Function();
typedef LambdaFun = Map Function();
void main() {
final SimpleFun simpleFun = (){};
final LambdaFun lambdaFun = ()=>{};
print(simpleFun()); // null
print(lambdaFun()); // {}
}
I hope this helps:
(_) => _ is a lamba expression.
A lambda function is a small function containing a single expression. It is used to perform a specific task and it can contain only that one expression
While
(_) {} is a function that can take as many expressions as you want to give it.
Hope this answers your question.
I have installed the Android Alarm Manager plugin in my Flutter v1.0.0 app by following the instructions at the link. But when I try to use AndroidAlarmManager.oneShot(...) nothing happens. There's not even an error in the console.
I am using a FutureBuilder widget to wait on the AndroidAlarmManager.initialize() future and one other future before my app begins rendering:
final combinedFutures = Future.wait<void>([
gameService.asyncLoad(),
AndroidAlarmManager.initialize(),
]);
FutureBuilder<void>(
future: combinedFutures,
builder: (context, snapshot) {
...
}
)
The FutureBuilder does end up rendering what it should so I know that the AndroidAlarmManager.initialize() future returns correctly.
Then in a button's onPressed function I do this:
AndroidAlarmManager.oneShot(
Duration(seconds: 5),
0,
() {
print("test");
},
wakeup: true,
);
The above should be calling print(test) after 5 seconds and waking up my phone but nothing is printed to the console and nothing happens on my phone. What is going wrong?
After debugging the library code I found that the AndroidAlarmManager.oneShot function calls the method PluginUtilities.getCallbackHandle(callback) where callback is the function passed in. In my case the callback function was:
() {
print("test");
}
For some reason PluginUtilities.getCallbackHandle was returning null when it should be returning a value of type CallbackHandle.
Looking at the PluginUtilities.getCallbackHandle documentation it says:
Get a handle to a named top-level or static callback function which can be easily passed between isolates.
The function I was passing is not top-level, static, or named. So I did the following inside my class and everything works fine now:
static void alarmTest() {
print("test");
}
void runAlarm() {
AndroidAlarmManager.oneShot(
Duration(seconds: 10),
0,
alarmTest,
wakeup: true,
).then((val) => print(val));
}
Is there an alternative to Navigator.push which doesn't require a context? Here is my problem.
I have a very nested and deep callstack and not each function has a BuildContext parameter. Is there any other way to get the current BuildContext than through passing it from one function to another?
test1.dart
Widget build(BuildContext) {
...
new GestureDetector(
onTap: () => foo1() // very deep callstack until it calls foo30
...
}
test2.dart
void foo30() {
// need context here like:
BuildContext context = IHopeThatExists.getCurrentContext();
// why do I need that? Navigator.push needs a context or is there an alternative? FYI: I don't need to go back to the previous page though
Navigator.push(context, ..)
}
You can use the Builder widget:
return Builder(
builder: (context) {
return RaisedButton(
onPressed: () {
Navigator.of(context).push(...);
},
...
);
},
);
If your call stack is that deep you might want to consider refactoring =D. But that aside, if you use a StatefulWidget it has a context property which is the same as what is passed to the build function.
And FYI - from the State.build docs:
The BuildContext argument is always the same as the context property
of this State object and will remain the same for the lifetime of this
object. The BuildContext argument is provided redundantly here so that
this method matches the signature for a WidgetBuilder.