Created
September 28, 2024 00:06
-
-
Save caseycrogers/8e42eae3cbfbe4161200c401619446b5 to your computer and use it in GitHub Desktop.
This file contains hidden or 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 outlines a few different approaches for converting an arbitrary if...else statement chain into an expression. | |
/// Here will be our example scenario based off of a real scenario in my production app: | |
/// A. Evaluate to a `NewGameButton` widget if `game` is finished and a new game hasn't been started yet. | |
/// B. Evaluate to a blank widget (`SizedBox`) if `game` has just started but is loading. | |
/// C. Otherwise evaluate to a `GameHUD` widget. | |
/// Here is the above as a statement. IMO this is by far the most readable, but is only applicable in contexts | |
/// where a statement can be used. | |
final game = Game.watch(context); | |
if (!game.isPlaying) { | |
return NewGameButton(); | |
} | |
if (game.isLoading) { | |
return SizedBox(); | |
} | |
return GameHUD(); | |
/// Here it is with a record and a switch. I really don't love this approach as, whnile it is very terse, the `isPlaying` | |
/// and the `isLoading` checks are entirely independent checks and bundling them into a record IMO really hurts readability. | |
/// This would get far worse the more bool checks you add as it's not obvious which element in the case refers to which name | |
/// in the original record (I guess you could make it a named record, but now we're getting really complex and tedious). | |
final game = Game.watch(context); | |
return AnimatedSwitcher( | |
duration: kDuration, | |
child: switch ((game.isPlaying, game.isLoading)) { | |
(false, _) => NewGameButton(), | |
// It's not obvious at a glance that `true` is testing `game.isLoading` | |
(_, true) => SizedBox(), | |
// Switches with a default case feel like 🤢, but alternatively writing out the final case also feels a bit tedious and | |
// awkward. | |
_ => GameHud(), | |
}, | |
); | |
/// Here it is using an IIFE to frankenstein the statement syntax into an expression. This may be my favorite approach | |
/// in Flutter as it exists today, but IIFE's haven't gotten much traction in the community-maybe some evangelism would be | |
/// effective here? | |
final game = Game.watch(context); | |
return AnimatedSwitcher( | |
duration: kDuration, | |
child: (){ | |
if (!game.isPlaying) { | |
return NewGameButton(); | |
} | |
if (game.isLoading) { | |
return SizedBox(); | |
} | |
return GameHUD(); | |
}(), | |
); | |
/// And here is what it looks like with a nested ternary (often what I do and super terse, but 💩 readability). | |
final game = Game.watch(context); | |
return AnimatedSwitcher( | |
duration: kDuration, | |
child: !game.isPlaying ? NewGameButton() : game.isLoading ? SizedBox() : GameHUD(), | |
); | |
/// Here is the glorious world where Flutter adds support for if...else expressions. We lose return short circuiting, | |
/// but it's still quite nice. | |
/// See issue: https://github.com/dart-lang/language/issues/3374 | |
final game = Game.watch(context); | |
return AnimatedSwitcher( | |
duration: kDuration, | |
/// Curly bracess are optional. Ternary syntax is banished (I hate ternary syntax so much it's so needlessly confusing). | |
child: if (!game.isPlaying) { | |
print('We can add side effects here and it\'ll still just evaluate to the last expression in the block'); | |
NewGameButton(); | |
} else if (game.isLoading) { | |
SizedBox(); | |
} else { | |
GameHUD(); | |
}, | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment