Merge branch 'development' into feature/132-verschiedene-regelsaetze-implementieren
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 46s
Pull Request Pipeline / lint (pull_request) Successful in 54s

# Conflicts:
#	lib/core/common.dart
#	lib/presentation/views/main_menu/statistics_view.dart
#	lib/presentation/widgets/player_selection.dart
This commit is contained in:
2026-04-24 12:15:57 +02:00
17 changed files with 710 additions and 225 deletions

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tallee/core/common.dart';
import 'package:tallee/core/constants.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/data/db/database.dart';
@@ -140,6 +141,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
padding: const EdgeInsets.only(right: 8.0),
child: TextIconTile(
text: player.name,
suffixText: getNameCountText(player),
onIconTap: () {
setState(() {
// Removes the player from the selection and notifies the parent.
@@ -193,6 +195,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
itemBuilder: (BuildContext context, int index) {
return TextIconListTile(
text: suggestedPlayers[index].name,
suffixText: getNameCountText(suggestedPlayers[index]),
onPressed: () {
setState(() {
// If the player is not already selected
@@ -282,7 +285,8 @@ class _PlayerSelectionState extends State<PlayerSelection> {
final loc = AppLocalizations.of(context);
final playerName = _searchBarController.text.trim();
final createdPlayer = Player(name: playerName);
int nameCount = _calculateNameCount(playerName);
final createdPlayer = Player(name: playerName, nameCount: nameCount);
final success = await db.playerDao.addPlayer(player: createdPlayer);
if (!context.mounted) return;
@@ -295,6 +299,22 @@ class _PlayerSelectionState extends State<PlayerSelection> {
}
}
int _calculateNameCount(String playerName) {
final playersWithSameName =
allPlayers.where((player) => player.name == playerName).toList()
..sort((a, b) => a.nameCount.compareTo(b.nameCount));
if (playersWithSameName.isEmpty) {
return 0;
} else if (playersWithSameName.length == 1) {
// Initialize nameCount
playersWithSameName[0].nameCount = 1;
}
// Return following count
return playersWithSameName.length + 1;
}
/// Updates the state after successfully adding a new player.
void _handleSuccessfulPlayerCreation(Player player) {
selectedPlayers.insert(0, player);

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:tallee/core/common.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/data/models/group.dart';
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
@@ -81,7 +82,11 @@ class _GroupTileState extends State<GroupTile> {
for (var member in [
...widget.group.members,
]..sort((a, b) => a.name.compareTo(b.name)))
TextIconTile(text: member.name, iconEnabled: false),
TextIconTile(
text: member.name,
suffixText: getNameCountText(member),
iconEnabled: false,
),
],
),
const SizedBox(height: 2.5),

View File

@@ -258,7 +258,11 @@ class _MatchTileState extends State<MatchTile> {
spacing: 6,
runSpacing: 6,
children: players.map((player) {
return TextIconTile(text: player.name, iconEnabled: false);
return TextIconTile(
text: player.name,
suffixText: getNameCountText(player),
iconEnabled: false,
);
}).toList(),
),
],

View File

@@ -1,6 +1,9 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:tallee/core/common.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/data/models/player.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/widgets/tiles/info_tile.dart';
@@ -32,7 +35,7 @@ class StatisticsTile extends StatelessWidget {
final double width;
/// A list of tuples containing labels and their corresponding numeric values.
final List<(String, num)> values;
final List<(Player, num)> values;
/// The maximum number of items to display.
final int itemCount;
@@ -89,11 +92,29 @@ class StatisticsTile extends StatelessWidget {
),
Padding(
padding: const EdgeInsets.only(left: 4.0),
child: Text(
values[index].$1,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
child: RichText(
overflow: TextOverflow.ellipsis,
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: [
TextSpan(
text: values[index].$1.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
TextSpan(
text: getNameCountText(values[index].$1),
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: CustomTheme.textColor.withAlpha(
150,
),
),
),
],
),
),
),

View File

@@ -9,6 +9,7 @@ class TextIconListTile extends StatelessWidget {
const TextIconListTile({
super.key,
required this.text,
this.suffixText = '',
this.iconEnabled = true,
this.onPressed,
});
@@ -16,6 +17,9 @@ class TextIconListTile extends StatelessWidget {
/// The text to display in the tile.
final String text;
/// An optional suffix text to display after the main text.
final String suffixText;
/// A boolean to determine if the icon should be displayed.
final bool iconEnabled;
@@ -35,12 +39,27 @@ class TextIconListTile extends StatelessWidget {
Flexible(
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12.5),
child: Text(
text,
child: RichText(
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: [
TextSpan(
text: text,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
TextSpan(
text: suffixText,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: CustomTheme.textColor.withAlpha(100),
),
),
],
),
),
),

View File

@@ -9,6 +9,7 @@ class TextIconTile extends StatelessWidget {
const TextIconTile({
super.key,
required this.text,
this.suffixText = '',
this.iconEnabled = true,
this.onIconTap,
});
@@ -16,6 +17,8 @@ class TextIconTile extends StatelessWidget {
/// The text to display in the tile.
final String text;
final String suffixText;
/// A boolean to determine if the icon should be displayed.
final bool iconEnabled;
@@ -36,10 +39,28 @@ class TextIconTile extends StatelessWidget {
children: [
if (iconEnabled) const SizedBox(width: 3),
Flexible(
child: Text(
text,
child: RichText(
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: [
TextSpan(
text: text,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
TextSpan(
text: suffixText,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: CustomTheme.textColor.withAlpha(120),
),
),
],
),
),
),
if (iconEnabled) ...<Widget>[