-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs(example): add pickleball score tracking example (#88)
- Loading branch information
1 parent
246e9ac
commit 7ab1ed6
Showing
7 changed files
with
342 additions
and
0 deletions.
There are no files selected for viewing
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 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import 'package:rearch/rearch.dart'; | ||
import 'package:scorus/score.dart'; | ||
import 'package:scorus/serving_player.dart'; | ||
|
||
/// Represents a team. | ||
enum Team { | ||
/// The first team. | ||
team1, | ||
|
||
/// The second team. | ||
team2, | ||
} | ||
|
||
/// Provides [next]. | ||
extension NextTeam on Team { | ||
/// Returns the next [Team] up for possesion. | ||
Team get next => switch (this) { | ||
Team.team1 => Team.team2, | ||
Team.team2 => Team.team1, | ||
}; | ||
} | ||
|
||
/// Manages the current team with possesion. | ||
({ | ||
Team teamWithPossesion, | ||
void Function() givePossesionToNextTeam, | ||
void Function() resetTeamWithPossesion, | ||
}) teamWithPossesionManager(CapsuleHandle use) { | ||
const startingTeam = Team.team1; | ||
final (team, setTeam) = use.state(startingTeam); | ||
return ( | ||
teamWithPossesion: team, | ||
givePossesionToNextTeam: () => setTeam(team.next), | ||
resetTeamWithPossesion: () => setTeam(startingTeam), | ||
); | ||
} | ||
|
||
/// Returns which team and which player on that team is serving. | ||
(Team, ServingPlayer) currServingTeamAndPlayerCapsule(CapsuleHandle use) { | ||
final teamWithPossesion = use(teamWithPossesionManager).teamWithPossesion; | ||
final servingPlayerOnTeamWithPossesion = switch (teamWithPossesion) { | ||
Team.team1 => use(team1ServingPlayerManager).servingPlayer, | ||
Team.team2 => use(team2ServingPlayerManager).servingPlayer, | ||
}; | ||
return (teamWithPossesion, servingPlayerOnTeamWithPossesion); | ||
} | ||
|
||
/// Action capsule that returns a function to reset the game. | ||
void Function() resetGameAction(CapsuleHandle use) { | ||
final resets = [ | ||
use(team1ScoreManager).resetScore, | ||
use(team2ScoreManager).resetScore, | ||
use(team1ServingPlayerManager).resetServingPlayer, | ||
use(team2ServingPlayerManager).resetServingPlayer, | ||
use(teamWithPossesionManager).resetTeamWithPossesion, | ||
]; | ||
final runTxn = use.transactionRunner(); | ||
return () => runTxn(() { | ||
for (final reset in resets) { | ||
reset(); | ||
} | ||
}); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
// ignore_for_file: public_member_api_docs | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter_rearch/flutter_rearch.dart'; | ||
import 'package:scorus/game_management.dart'; | ||
import 'package:scorus/score.dart'; | ||
import 'package:scorus/serving_player.dart'; | ||
import 'package:scorus/volley.dart'; | ||
|
||
void main() { | ||
runApp(const ScorusApp()); | ||
} | ||
|
||
class ScorusApp extends StatelessWidget { | ||
const ScorusApp({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return const RearchBootstrapper( | ||
child: MaterialApp(home: ScorusBody()), | ||
); | ||
} | ||
} | ||
|
||
class ScorusBody extends RearchConsumer { | ||
const ScorusBody({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context, WidgetHandle use) { | ||
final scoreTextStyle = Theme.of(context).textTheme.displayLarge; | ||
final (servingTeam, servingPlayer) = use(currServingTeamAndPlayerCapsule); | ||
|
||
return Scaffold( | ||
appBar: AppBar( | ||
title: const Text('Scorus'), | ||
actions: [ | ||
IconButton( | ||
icon: const Icon(Icons.restart_alt_rounded), | ||
onPressed: use(resetGameAction), | ||
), | ||
], | ||
), | ||
body: Column( | ||
children: [ | ||
Expanded( | ||
child: InkWell( | ||
onTap: use(team1WonVolleyAction), | ||
child: Stack( | ||
children: [ | ||
ColoredBox( | ||
color: Colors.red, | ||
child: Center( | ||
child: Text( | ||
'${use(team1ScoreManager).score}', | ||
style: scoreTextStyle, | ||
), | ||
), | ||
), | ||
if (servingTeam == Team.team1) | ||
ServingPlayerCard( | ||
servingPlayer: servingPlayer, | ||
anchorOnTop: true, | ||
), | ||
], | ||
), | ||
), | ||
), | ||
Expanded( | ||
child: InkWell( | ||
onTap: use(team2WonVolleyAction), | ||
child: Stack( | ||
children: [ | ||
ColoredBox( | ||
color: Colors.blue, | ||
child: Center( | ||
child: Text( | ||
'${use(team2ScoreManager).score}', | ||
style: scoreTextStyle, | ||
), | ||
), | ||
), | ||
if (servingTeam == Team.team2) | ||
ServingPlayerCard( | ||
servingPlayer: servingPlayer, | ||
anchorOnTop: false, | ||
), | ||
], | ||
), | ||
), | ||
), | ||
], | ||
), | ||
); | ||
} | ||
} | ||
|
||
class ServingPlayerCard extends StatelessWidget { | ||
const ServingPlayerCard({ | ||
required this.servingPlayer, | ||
required this.anchorOnTop, | ||
super.key, | ||
}); | ||
|
||
final ServingPlayer servingPlayer; | ||
final bool anchorOnTop; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final servingPlayerNumber = switch (servingPlayer) { | ||
ServingPlayer.player1 => 1, | ||
ServingPlayer.player2 => 2, | ||
}; | ||
return Positioned( | ||
left: 0, | ||
right: 0, | ||
top: anchorOnTop ? 32 : null, | ||
bottom: anchorOnTop ? null : 32, | ||
child: Center( | ||
child: Card( | ||
color: Colors.white.withOpacity(0.1), | ||
child: Padding( | ||
padding: const EdgeInsets.all(16), | ||
child: Text( | ||
'Player $servingPlayerNumber serving', | ||
style: Theme.of(context).textTheme.displaySmall, | ||
), | ||
), | ||
), | ||
), | ||
); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import 'package:rearch/rearch.dart'; | ||
|
||
/// Defines what a score management [Capsule] can do. | ||
typedef ScoreManager = ({ | ||
int score, | ||
void Function() incrementScore, | ||
void Function() resetScore, | ||
}); | ||
|
||
/// Manages the score for the first team. | ||
ScoreManager team1ScoreManager(CapsuleHandle use) => use.teamScore(); | ||
|
||
/// Manages the score for the second team. | ||
ScoreManager team2ScoreManager(CapsuleHandle use) => use.teamScore(); | ||
|
||
extension on SideEffectRegistrar { | ||
SideEffectRegistrar get use => this; | ||
ScoreManager teamScore() { | ||
final (score, setScore) = use.state(0); | ||
return ( | ||
score: score, | ||
incrementScore: () => setScore(score + 1), | ||
resetScore: () => setScore(0) | ||
); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import 'package:rearch/rearch.dart'; | ||
|
||
/// Represents the currently serving player. | ||
enum ServingPlayer { | ||
/// Player #1. | ||
player1, | ||
|
||
/// Player #2. | ||
player2, | ||
} | ||
|
||
/// Provides [next]. | ||
extension NextPlayer on ServingPlayer { | ||
/// Returns the next [ServingPlayer] in the serving order. | ||
ServingPlayer get next => switch (this) { | ||
ServingPlayer.player1 => ServingPlayer.player2, | ||
ServingPlayer.player2 => ServingPlayer.player1, | ||
}; | ||
} | ||
|
||
/// Represents what a [ServingPlayer] manager should be able to do. | ||
typedef ServingPlayerManager = ({ | ||
ServingPlayer servingPlayer, | ||
void Function() giveServeToNextPlayer, | ||
void Function() resetServingPlayer, | ||
}); | ||
|
||
/// Manages the [ServingPlayer] for the first team. | ||
ServingPlayerManager team1ServingPlayerManager(CapsuleHandle use) => | ||
use.servingPlayer(startingPlayer: ServingPlayer.player2); | ||
|
||
/// Manages the [ServingPlayer] for the second team. | ||
ServingPlayerManager team2ServingPlayerManager(CapsuleHandle use) => | ||
use.servingPlayer(startingPlayer: ServingPlayer.player1); | ||
|
||
extension on SideEffectRegistrar { | ||
SideEffectRegistrar get use => this; | ||
ServingPlayerManager servingPlayer({required ServingPlayer startingPlayer}) { | ||
final (servingPlayer, setServingPlayer) = use.state(startingPlayer); | ||
return ( | ||
servingPlayer: servingPlayer, | ||
giveServeToNextPlayer: () => setServingPlayer(servingPlayer.next), | ||
resetServingPlayer: () => setServingPlayer(startingPlayer), | ||
); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import 'package:rearch/rearch.dart'; | ||
import 'package:scorus/game_management.dart'; | ||
import 'package:scorus/score.dart'; | ||
import 'package:scorus/serving_player.dart'; | ||
|
||
/// An action capsule that when invoked indicates team 1 won a volley. | ||
void Function() team1WonVolleyAction(CapsuleHandle use) => use.volleyWinner( | ||
thisTeam: Team.team1, | ||
teamWithPossesion: use(teamWithPossesionManager).teamWithPossesion, | ||
incrementThisTeamScore: use(team1ScoreManager).incrementScore, | ||
giveServeToOtherTeamNextPlayer: | ||
use(team2ServingPlayerManager).giveServeToNextPlayer, | ||
otherTeamServingPlayer: use(team2ServingPlayerManager).servingPlayer, | ||
givePossesionToNextTeam: | ||
use(teamWithPossesionManager).givePossesionToNextTeam, | ||
); | ||
|
||
/// An action capsule that when invoked indicates team 2 won a volley. | ||
void Function() team2WonVolleyAction(CapsuleHandle use) => use.volleyWinner( | ||
thisTeam: Team.team2, | ||
teamWithPossesion: use(teamWithPossesionManager).teamWithPossesion, | ||
incrementThisTeamScore: use(team2ScoreManager).incrementScore, | ||
giveServeToOtherTeamNextPlayer: | ||
use(team1ServingPlayerManager).giveServeToNextPlayer, | ||
otherTeamServingPlayer: use(team1ServingPlayerManager).servingPlayer, | ||
givePossesionToNextTeam: | ||
use(teamWithPossesionManager).givePossesionToNextTeam, | ||
); | ||
|
||
extension on SideEffectRegistrar { | ||
SideEffectRegistrar get use => this; | ||
void Function() volleyWinner({ | ||
required Team thisTeam, | ||
required Team teamWithPossesion, | ||
required void Function() incrementThisTeamScore, | ||
required void Function() giveServeToOtherTeamNextPlayer, | ||
required ServingPlayer otherTeamServingPlayer, | ||
required void Function() givePossesionToNextTeam, | ||
}) { | ||
final runTxn = use.transactionRunner(); | ||
return () => runTxn(() { | ||
if (teamWithPossesion == thisTeam) { | ||
incrementThisTeamScore(); | ||
} else { | ||
giveServeToOtherTeamNextPlayer(); | ||
if (otherTeamServingPlayer == ServingPlayer.player2) { | ||
givePossesionToNextTeam(); | ||
} | ||
} | ||
}); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
name: scorus | ||
description: "Pickleball score tracker" | ||
publish_to: none | ||
|
||
environment: | ||
sdk: '>=3.2.5 <4.0.0' | ||
flutter: '>=3.16.9 <4.0.0' | ||
|
||
dependencies: | ||
flutter: | ||
sdk: flutter | ||
flutter_rearch: ^1.4.0 | ||
rearch: ^1.5.0 | ||
|
||
dev_dependencies: | ||
flutter_test: | ||
sdk: flutter | ||
|
||
flutter: | ||
uses-material-design: true |