Skip to content

Instantly share code, notes, and snippets.

@caseycrogers
Created September 28, 2024 00:06
Show Gist options
  • Save caseycrogers/8e42eae3cbfbe4161200c401619446b5 to your computer and use it in GitHub Desktop.
Save caseycrogers/8e42eae3cbfbe4161200c401619446b5 to your computer and use it in GitHub Desktop.
/// 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