Last active
June 28, 2018 16:27
-
-
Save yjbanov/0cdaf2c8ad3195bf49ab7cccffb69efe to your computer and use it in GitHub Desktop.
Conditional wrapping operator for Dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This build method attempts to conditionally insert a | |
// DecoratedBox widget into the widget hierarchy. Roughly | |
// speaking we want the widget tree to be like this: | |
// | |
// if (someCondition) { | |
// Container > DecoratedBox > Container | |
// } else { | |
// Container > Container | |
// } | |
build(_) { | |
Widget result = Container( | |
color: Colors.blue, | |
); | |
if (someCondition) { | |
result = DecoratedBox( | |
color: Colors.green, | |
child: result, | |
); | |
} | |
result = Container( | |
color: Colors.red, | |
child: result, | |
); | |
return result; | |
} | |
// What if we had two operators `wrap` and `unwrap`. We could | |
// call the pair "conditional wrapping operator". | |
// | |
// `wrap`/`unwrap` are applied to the beginning of an expression. | |
// | |
// In the AST of an expression `wrap` is an ancestor of an | |
// `unwrap`. They always come in pairs. Dangling `wrap`/`unwrap` | |
// are not allowed. | |
// | |
// Like `if`, you supply a boolean expression to the `wrap` | |
// operator. Everything between `wrap` and the nearest | |
// descendant `unwrap` is included only when that expression is | |
// `true`. If the expression is `false` the sub-expression marked | |
// by `unwrap` is hoisted up to the nearect ancestor `wrap`. | |
build(_) { | |
return Container( | |
color: Colors.red, | |
child: wrap (someCondition) DecoratedBox( | |
color: Colors.green, | |
child: unwrap Container( | |
color: Colors.blue, | |
), | |
), | |
); | |
} |
@lrhn, these are interesting ideas!
One good property of the onlyIf
trick is that it retains the tree structure of the whole expression. Its biggest disadvantage though is that it seems to only work if you want to conditionally inject exactly one intermediate widget. That would make it not very useful.
The conditionally
method is better semantically as it supports conditionally injecting a range of intermediate widgets. However, it breaks the tree structure visually by detaching the child expression from the main tree. Also, I'm afraid that using higher-order functions may be bad for performance, as we make extra function calls, and allocate a closure. I'd be less worried if I knew that in the end it all desugared into a series of if
blocks.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Alternatively, and possible without language change, make the constructor a factory function with a condition argument:
That requires an alternative version of every constructor, and it's a function, not a constructor, since it has to return the child type.
On the other hand, the child type must be valid at that point where we want to omit something anyway, so we have to handle that type discrepancy in either case.
The conditional wrapping here looks very much like a higher-order abstraction.
You have a child, you have a function that you call on that child conditionally. A helper function could be:
Dart has first class functions, so we can already put a value into a new context. If we add new syntax, it should probably be possible to desugar it to something like this.