Beta-Version 0.5.3 (#136)
* Updated createGameView ListBuilder * Added ReorderableListView * Increment build no * Fixed bug with wrong medal icon * change not equal to greater than * Updated bool var * Fixed deletion error * Small translation improvements * Implemented first version of point overview * Visual improvements on table * Added details and sum row * Updated strings * Implemented new strings * Refactoring * Updated graph displayment * Moved new views to statistics section * Added seperator in main menu * Renaming * Updated sign * Updated colors & class name * Removed empty line * Updated round index * Updated types * Added new kamikaze button and bundles navigation functionality * Updated lock icon * Updated button position and design * Removed title row and changed segmendetControl Padding * Refactored logic and added comments * Updated comment * Chaned icon * Added comment * Removed print * Updated colors * Changed var name * Removed unused strings * Added gameMode * Changed creation variable * Updated mode selection * Updated strings * Changed mode order * Implemented default mode selection * Updated initState * Removed print * Removed print * Removed comments * Updated config service * Changed create game view * Changed icon * Updated strings * Updated config * Updated mode selection logic * Deleted getter * Removed not used code * Implemented reset logic for default game mode * Updated to 0.5.0 * Hotfix: Pixel Overflow * Changed the overall return type for gamemodes * Updated documentation * Fixed merge issues * Added Custom button * Updated strings * Updated buttons, implemented animatedOpacity * Keyboard still doesnt works * Fixed keyboard behaviour * Changed keyboard height * Added method getGameSessionById() * Updated gameSession class * id gets added to gameSession class at creation * Cleaned up file * Added docs and dependency * Removed toString * Implemented null safety * Added named parameter * Replaced button with custom button * Updated key * Updated addGameSessionMethod * Update README.md * Added Strings for popup * Implemented popup & confetti * Extracted code to method _playFinishAnimation() * Replaced tenary operator with Visibility Widget * Replaced tenary operator with Visibility Widget * Used variable again * Added delays in constants.dart * Removed confetti button * Updated strings * Removed print * Added dispose for confettiController * Implemented missing constant in code * Updated gameSession logic so more than one player can be winner * Updated strings * Updated winner popup * game names now can have up to 20 chars * Updated strings * Added sized box for visual enhancement * Centered the add player button and made it wider * New created player textfields get automatically focused * Added focus nodes for autofocus and navigation between textfields * Updated version number * Updated game title textfield with focus node and textaction * Added focusnodes to dispose * Update README.md * Fixed bug with no popup shown * Fixed bug with out of range error * Updated listener notification
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import 'package:cabo_counter/core/custom_theme.dart';
|
||||
import 'package:cabo_counter/data/game_session.dart';
|
||||
import 'package:cabo_counter/l10n/generated/app_localizations.dart';
|
||||
import 'package:cabo_counter/presentation/widgets/custom_button.dart';
|
||||
import 'package:cabo_counter/services/local_storage_service.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -74,21 +75,22 @@ class _RoundViewState extends State<RoundView> {
|
||||
return CupertinoPageScaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
transitionBetweenRoutes: true,
|
||||
leading: CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () =>
|
||||
{LocalStorageService.saveGameSessions(), Navigator.pop(context)},
|
||||
child: Text(AppLocalizations.of(context).cancel),
|
||||
),
|
||||
middle: Text(AppLocalizations.of(context).results),
|
||||
trailing: widget.gameSession.isGameFinished
|
||||
? const Icon(
|
||||
transitionBetweenRoutes: true,
|
||||
leading: CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () => {
|
||||
LocalStorageService.saveGameSessions(),
|
||||
Navigator.pop(context)
|
||||
},
|
||||
child: Text(AppLocalizations.of(context).cancel),
|
||||
),
|
||||
middle: Text(AppLocalizations.of(context).results),
|
||||
trailing: Visibility(
|
||||
visible: widget.gameSession.isGameFinished,
|
||||
child: const Icon(
|
||||
CupertinoIcons.lock,
|
||||
size: 25,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
))),
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
@@ -114,9 +116,10 @@ class _RoundViewState extends State<RoundView> {
|
||||
vertical: 10,
|
||||
),
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
height: 60,
|
||||
child: CupertinoSegmentedControl<int>(
|
||||
unselectedColor: CustomTheme.backgroundTintColor,
|
||||
unselectedColor:
|
||||
CustomTheme.mainElementBackgroundColor,
|
||||
selectedColor: CustomTheme.primaryColor,
|
||||
groupValue: _caboPlayerIndex,
|
||||
children: Map.fromEntries(widget.gameSession.players
|
||||
@@ -130,7 +133,7 @@ class _RoundViewState extends State<RoundView> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 6,
|
||||
vertical: 6,
|
||||
vertical: 8,
|
||||
),
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
@@ -154,27 +157,6 @@ class _RoundViewState extends State<RoundView> {
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
child: CupertinoListTile(
|
||||
title: Text(AppLocalizations.of(context).player),
|
||||
trailing: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 100,
|
||||
child: Center(
|
||||
child: Text(
|
||||
AppLocalizations.of(context).points))),
|
||||
const SizedBox(width: 20),
|
||||
SizedBox(
|
||||
width: 80,
|
||||
child: Center(
|
||||
child: Text(AppLocalizations.of(context)
|
||||
.kamikaze))),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
@@ -182,13 +164,15 @@ class _RoundViewState extends State<RoundView> {
|
||||
itemBuilder: (context, index) {
|
||||
final originalIndex = originalIndices[index];
|
||||
final name = rotatedPlayers[index];
|
||||
bool shouldShowMedal =
|
||||
index == 0 && widget.roundNumber > 1;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10, horizontal: 20),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: CupertinoListTile(
|
||||
backgroundColor: CupertinoColors.secondaryLabel,
|
||||
backgroundColor: CustomTheme.playerTileColor,
|
||||
title: Row(children: [
|
||||
Expanded(
|
||||
child: Row(children: [
|
||||
@@ -197,95 +181,70 @@ class _RoundViewState extends State<RoundView> {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Visibility(
|
||||
visible: index == 0,
|
||||
visible: shouldShowMedal,
|
||||
child: const SizedBox(width: 10),
|
||||
),
|
||||
Visibility(
|
||||
visible: index == 0,
|
||||
child: const Icon(FontAwesomeIcons.medal,
|
||||
visible: shouldShowMedal,
|
||||
child: const Icon(FontAwesomeIcons.crown,
|
||||
size: 15))
|
||||
]))
|
||||
]),
|
||||
subtitle: Text(
|
||||
'${widget.gameSession.playerScores[originalIndex]}'
|
||||
' ${AppLocalizations.of(context).points}'),
|
||||
trailing: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 100,
|
||||
child: CupertinoTextField(
|
||||
maxLength: 3,
|
||||
focusNode: _focusNodeList[originalIndex],
|
||||
keyboardType:
|
||||
const TextInputType.numberWithOptions(
|
||||
signed: true,
|
||||
decimal: false,
|
||||
),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
],
|
||||
textInputAction: index ==
|
||||
widget.gameSession.players
|
||||
.length -
|
||||
1
|
||||
? TextInputAction.done
|
||||
: TextInputAction.next,
|
||||
controller:
|
||||
_scoreControllerList[originalIndex],
|
||||
placeholder:
|
||||
AppLocalizations.of(context).points,
|
||||
textAlign: TextAlign.center,
|
||||
onSubmitted: (_) =>
|
||||
_focusNextTextfield(originalIndex),
|
||||
onChanged: (_) => setState(() {}),
|
||||
),
|
||||
trailing: SizedBox(
|
||||
width: 100,
|
||||
child: CupertinoTextField(
|
||||
maxLength: 3,
|
||||
focusNode: _focusNodeList[originalIndex],
|
||||
keyboardType:
|
||||
const TextInputType.numberWithOptions(
|
||||
signed: true,
|
||||
decimal: false,
|
||||
),
|
||||
const SizedBox(width: 50),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_kamikazePlayerIndex =
|
||||
(_kamikazePlayerIndex ==
|
||||
originalIndex)
|
||||
? null
|
||||
: originalIndex;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
width: 24,
|
||||
height: 24,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: _kamikazePlayerIndex ==
|
||||
originalIndex
|
||||
? CupertinoColors.systemRed
|
||||
: CupertinoColors
|
||||
.tertiarySystemFill,
|
||||
border: Border.all(
|
||||
color: _kamikazePlayerIndex ==
|
||||
originalIndex
|
||||
? CupertinoColors.systemRed
|
||||
: CupertinoColors.systemGrey,
|
||||
),
|
||||
),
|
||||
child: _kamikazePlayerIndex ==
|
||||
originalIndex
|
||||
? const Icon(
|
||||
CupertinoIcons.exclamationmark,
|
||||
size: 16,
|
||||
color: CupertinoColors.white,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 22),
|
||||
],
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
],
|
||||
textInputAction: index ==
|
||||
widget.gameSession.players.length - 1
|
||||
? TextInputAction.done
|
||||
: TextInputAction.next,
|
||||
controller:
|
||||
_scoreControllerList[originalIndex],
|
||||
placeholder:
|
||||
AppLocalizations.of(context).points,
|
||||
textAlign: TextAlign.center,
|
||||
onSubmitted: (_) =>
|
||||
_focusNextTextfield(originalIndex),
|
||||
onChanged: (_) => setState(() {}),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 10, 0, 0),
|
||||
child: Center(
|
||||
heightFactor: 1,
|
||||
child: CustomButton(
|
||||
onPressed: () async {
|
||||
if (await _showKamikazeSheet(context)) {
|
||||
if (!context.mounted) return;
|
||||
_endOfRoundNavigation(context, true);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
AppLocalizations.of(context).kamikaze,
|
||||
style: const TextStyle(
|
||||
color: CupertinoColors.destructiveRed,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -300,21 +259,14 @@ class _RoundViewState extends State<RoundView> {
|
||||
return Container(
|
||||
height: 80,
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
color: CustomTheme.backgroundTintColor,
|
||||
color: CustomTheme.mainElementBackgroundColor,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
CupertinoButton(
|
||||
onPressed: _areRoundInputsValid()
|
||||
? () async {
|
||||
List<int> bonusPlayersIndices = _finishRound();
|
||||
if (bonusPlayersIndices.isNotEmpty) {
|
||||
await _showBonusPopup(
|
||||
context, bonusPlayersIndices);
|
||||
}
|
||||
LocalStorageService.saveGameSessions();
|
||||
if (!context.mounted) return;
|
||||
Navigator.pop(context);
|
||||
? () {
|
||||
_endOfRoundNavigation(context, false);
|
||||
}
|
||||
: null,
|
||||
child: Text(AppLocalizations.of(context).done),
|
||||
@@ -322,21 +274,8 @@ class _RoundViewState extends State<RoundView> {
|
||||
if (!widget.gameSession.isGameFinished)
|
||||
CupertinoButton(
|
||||
onPressed: _areRoundInputsValid()
|
||||
? () async {
|
||||
List<int> bonusPlayersIndices =
|
||||
_finishRound();
|
||||
if (bonusPlayersIndices.isNotEmpty) {
|
||||
await _showBonusPopup(
|
||||
context, bonusPlayersIndices);
|
||||
}
|
||||
LocalStorageService.saveGameSessions();
|
||||
if (widget.gameSession.isGameFinished &&
|
||||
context.mounted) {
|
||||
Navigator.pop(context);
|
||||
} else if (context.mounted) {
|
||||
Navigator.pop(
|
||||
context, widget.roundNumber + 1);
|
||||
}
|
||||
? () {
|
||||
_endOfRoundNavigation(context, true);
|
||||
}
|
||||
: null,
|
||||
child: Text(AppLocalizations.of(context).next_round),
|
||||
@@ -399,6 +338,37 @@ class _RoundViewState extends State<RoundView> {
|
||||
];
|
||||
}
|
||||
|
||||
/// Shows a Cupertino action sheet to select the player who has Kamikaze.
|
||||
/// It returns true if a player was selected, false if the action was cancelled.
|
||||
Future<bool> _showKamikazeSheet(BuildContext context) async {
|
||||
return await showCupertinoModalPopup<bool?>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return CupertinoActionSheet(
|
||||
title: Text(AppLocalizations.of(context).kamikaze),
|
||||
message: Text(AppLocalizations.of(context).who_has_kamikaze),
|
||||
actions: widget.gameSession.players.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
final name = entry.value;
|
||||
return CupertinoActionSheetAction(
|
||||
onPressed: () {
|
||||
_kamikazePlayerIndex = index;
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
child: Text(name),
|
||||
);
|
||||
}).toList(),
|
||||
cancelButton: CupertinoActionSheetAction(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
isDestructiveAction: true,
|
||||
child: Text(AppLocalizations.of(context).cancel),
|
||||
),
|
||||
);
|
||||
},
|
||||
) ??
|
||||
false;
|
||||
}
|
||||
|
||||
/// Focuses the next text field in the list of text fields.
|
||||
/// [index] is the index of the current text field.
|
||||
void _focusNextTextfield(int index) {
|
||||
@@ -469,10 +439,9 @@ class _RoundViewState extends State<RoundView> {
|
||||
return bonusPlayers;
|
||||
}
|
||||
|
||||
/// Shows a popup dialog with the bonus information.
|
||||
/// Shows a popup dialog with the information which player received the bonus points.
|
||||
Future<void> _showBonusPopup(
|
||||
BuildContext context, List<int> bonusPlayers) async {
|
||||
print('Bonus Popup wird angezeigt');
|
||||
int pointLimit = widget.gameSession.pointLimit;
|
||||
int bonusPoints = (pointLimit / 2).round();
|
||||
|
||||
@@ -519,6 +488,37 @@ class _RoundViewState extends State<RoundView> {
|
||||
return resultText;
|
||||
}
|
||||
|
||||
/// Handles the navigation for the end of the round.
|
||||
/// It checks for bonus players and shows a popup, saves the game session,
|
||||
/// and navigates to the next round or back to the previous screen.
|
||||
/// It takes the BuildContext [context] and a boolean [navigateToNextRound] to determine
|
||||
/// if it should navigate to the next round or not.
|
||||
Future<void> _endOfRoundNavigation(
|
||||
BuildContext context, bool navigateToNextRound) async {
|
||||
List<int> bonusPlayersIndices = _finishRound();
|
||||
if (bonusPlayersIndices.isNotEmpty) {
|
||||
await _showBonusPopup(context, bonusPlayersIndices);
|
||||
}
|
||||
|
||||
LocalStorageService.saveGameSessions();
|
||||
|
||||
if (context.mounted) {
|
||||
// If the game is finished, pop the context and return to the previous screen.
|
||||
if (widget.gameSession.isGameFinished) {
|
||||
Navigator.pop(context);
|
||||
return;
|
||||
}
|
||||
// If navigateToNextRound is false, pop the context and return to the previous screen.
|
||||
if (!navigateToNextRound) {
|
||||
Navigator.pop(context);
|
||||
return;
|
||||
}
|
||||
// If navigateToNextRound is true and the game isn't finished yet,
|
||||
// pop the context and navigate to the next round.
|
||||
Navigator.pop(context, widget.roundNumber + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (final controller in _scoreControllerList) {
|
||||
|
||||
Reference in New Issue
Block a user