Merge remote-tracking branch 'origin/development' into feature/119-implementierung-der-games

# Conflicts:
#	lib/data/dto/game.dart
#	lib/l10n/arb/app_de.arb
#	lib/l10n/arb/app_en.arb
#	lib/l10n/generated/app_localizations.dart
#	lib/l10n/generated/app_localizations_de.dart
#	lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart
#	lib/presentation/widgets/text_input/text_input_field.dart
#	pubspec.yaml
This commit is contained in:
2026-03-08 09:53:12 +01:00
148 changed files with 41342 additions and 3310 deletions

View File

@@ -47,10 +47,7 @@ class _AppSkeletonState extends State<AppSkeleton> {
: (Widget? currentChild, List<Widget> previousChildren) {
return Stack(
alignment: Alignment.topCenter,
children: [
...previousChildren,
if (currentChild != null) currentChild,
],
children: [...previousChildren, ?currentChild],
);
},
),

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:tallee/core/custom_theme.dart';
class AnimatedDialogButton extends StatefulWidget {
/// A custom animated button widget that provides a scaling and opacity effect

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/core/enums.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/core/enums.dart';
class CustomWidthButton extends StatelessWidget {
/// A custom button widget that is designed to have a width relative to the screen size.

View File

@@ -2,25 +2,25 @@ import 'package:flutter/material.dart';
class MainMenuButton extends StatefulWidget {
/// A button for the main menu with an optional icon and a press animation.
/// - [text]: The text of the button.
/// - [icon]: The icon of the button.
/// - [onPressed]: The callback to be invoked when the button is pressed.
/// - [icon]: The icon of the button.
/// - [text]: The text of the button.
const MainMenuButton({
super.key,
required this.text,
this.icon,
required this.onPressed,
required this.icon,
this.text,
});
/// The text of the button.
final String text;
/// The icon of the button.
final IconData? icon;
/// The callback to be invoked when the button is pressed.
final void Function() onPressed;
/// The icon of the button.
final IconData icon;
/// The text of the button.
final String? text;
@override
State<MainMenuButton> createState() => _MainMenuButtonState();
}
@@ -71,18 +71,18 @@ class _MainMenuButtonState extends State<MainMenuButton>
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (widget.icon != null) ...[
Icon(widget.icon, size: 26, color: Colors.black),
Icon(widget.icon, size: 26, color: Colors.black),
if (widget.text != null) ...[
const SizedBox(width: 7),
],
Text(
widget.text,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black,
Text(
widget.text!,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
),
],
],
),
),

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:tallee/core/custom_theme.dart';
class QuickCreateButton extends StatefulWidget {
/// A button widget designed for quick creating matches in the [HomeView]
@@ -28,14 +28,18 @@ class _QuickCreateButtonState extends State<QuickCreateButton> {
onPressed: widget.onPressed,
style: ElevatedButton.styleFrom(
minimumSize: const Size(140, 45),
backgroundColor: CustomTheme.primaryColor,
backgroundColor: CustomTheme.primaryColor.withAlpha(200).withBlue(40),
shape: RoundedRectangleBorder(
borderRadius: CustomTheme.standardBorderRadiusAll,
),
),
child: Text(
widget.text,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
style: const TextStyle(
color: CustomTheme.textColor,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
);
}

View File

@@ -1,5 +1,5 @@
import 'package:flutter/cupertino.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:tallee/core/custom_theme.dart';
class ColoredIconContainer extends StatelessWidget {
/// A customizable container widget that displays an icon with a colored background.
@@ -48,7 +48,7 @@ class ColoredIconContainer extends StatelessWidget {
child: Icon(
icon,
size: iconSize,
color: CustomTheme.primaryColor.withGreen(40),
color: CustomTheme.primaryColor.withBlue(40),
),
),
],

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:tallee/core/custom_theme.dart';
class CustomAlertDialog extends StatelessWidget {
/// A custom alert dialog widget that provides a os unspecific AlertDialog,
@@ -32,7 +32,7 @@ class CustomAlertDialog extends StatelessWidget {
actionsAlignment: MainAxisAlignment.spaceAround,
shape: RoundedRectangleBorder(
borderRadius: CustomTheme.standardBorderRadiusAll,
side: BorderSide(color: CustomTheme.boxBorder),
side: const BorderSide(color: CustomTheme.boxBorderColor),
),
);
}

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:tallee/core/custom_theme.dart';
class NavbarItem extends StatefulWidget {
/// A navigation bar item widget that represents a single tab in a navigation bar.
@@ -87,19 +87,29 @@ class _NavbarItemState extends State<NavbarItem>
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
ScaleTransition(
scale: widget.isSelected
? _scaleAnimation
: const AlwaysStoppedAnimation(1.0),
child: Icon(
widget.icon,
AnimatedContainer(
width: 50,
height: 50,
decoration: BoxDecoration(
color: widget.isSelected
? CustomTheme.navBarItemSelectedColor
: CustomTheme.navBarItemUnselectedColor,
size: 32,
? CustomTheme.primaryColor.withAlpha(50)
: Colors.transparent,
borderRadius: const BorderRadius.all(Radius.circular(15)),
),
duration: const Duration(milliseconds: 200),
child: ScaleTransition(
scale: widget.isSelected
? _scaleAnimation
: const AlwaysStoppedAnimation(1.0),
child: Icon(
widget.icon,
color: widget.isSelected
? CustomTheme.navBarItemSelectedColor
: CustomTheme.navBarItemUnselectedColor,
size: 32,
),
),
),
const SizedBox(height: 4),
Text(
widget.label,
style: TextStyle(

View File

@@ -1,15 +1,15 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/constants.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/data/db/database.dart';
import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/l10n/generated/app_localizations.dart';
import 'package:game_tracker/presentation/widgets/app_skeleton.dart';
import 'package:game_tracker/presentation/widgets/text_input/custom_search_bar.dart';
import 'package:game_tracker/presentation/widgets/tiles/text_icon_list_tile.dart';
import 'package:game_tracker/presentation/widgets/tiles/text_icon_tile.dart';
import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
import 'package:provider/provider.dart';
import 'package:tallee/core/constants.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/data/db/database.dart';
import 'package:tallee/data/dto/player.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/widgets/app_skeleton.dart';
import 'package:tallee/presentation/widgets/text_input/custom_search_bar.dart';
import 'package:tallee/presentation/widgets/tiles/text_icon_list_tile.dart';
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
import 'package:tallee/presentation/widgets/top_centered_message.dart';
class PlayerSelection extends StatefulWidget {
/// A widget that allows users to select players from a list,
@@ -62,7 +62,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
/// Skeleton data used while loading players.
late final List<Player> skeletonData = List.filled(
7,
Player(name: 'Player 0'),
Player(name: 'Player 0', description: ''),
);
@override
@@ -70,6 +70,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
super.initState();
db = Provider.of<AppDatabase>(context, listen: false);
suggestedPlayers = skeletonData;
selectedPlayers = widget.initialSelectedPlayers ?? [];
loadPlayerList();
}
@@ -100,7 +101,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
if (value.isEmpty) {
// If the search is empty, it shows all unselected players.
suggestedPlayers = allPlayers.where((player) {
return !selectedPlayers.contains(player);
return !selectedPlayers.any((p) => p.id == player.id);
}).toList();
} else {
// If there is input, it filters by name match (case-insensitive) and ensures
@@ -109,8 +110,8 @@ class _PlayerSelectionState extends State<PlayerSelection> {
final bool nameMatches = player.name.toLowerCase().contains(
value.toLowerCase(),
);
final bool isNotSelected = !selectedPlayers.contains(
player,
final bool isNotSelected = !selectedPlayers.any(
(p) => p.id == player.id,
);
return nameMatches && isNotSelected;
}).toList();
@@ -126,46 +127,49 @@ class _PlayerSelectionState extends State<PlayerSelection> {
const SizedBox(height: 10),
SizedBox(
height: 50,
child: selectedPlayers.isEmpty
? Center(child: Text(loc.no_players_selected))
: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
for (var player in selectedPlayers)
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: TextIconTile(
text: player.name,
onIconTap: () {
setState(() {
// Removes the player from the selection and notifies the parent.
selectedPlayers.remove(player);
widget.onChanged([...selectedPlayers]);
child: AppSkeleton(
enabled: isLoading,
child: selectedPlayers.isEmpty
? Center(child: Text(loc.no_players_selected))
: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
for (var player in selectedPlayers)
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: TextIconTile(
text: player.name,
onIconTap: () {
setState(() {
// Removes the player from the selection and notifies the parent.
selectedPlayers.remove(player);
widget.onChanged([...selectedPlayers]);
// Get the current search query
final currentSearch = _searchBarController
.text
.toLowerCase();
// Get the current search query
final currentSearch = _searchBarController
.text
.toLowerCase();
// If the player matches the current search query (or search is empty),
// they are added back to the `suggestedPlayers` and the list is re-sorted.
if (currentSearch.isEmpty ||
player.name.toLowerCase().contains(
currentSearch,
)) {
suggestedPlayers.add(player);
suggestedPlayers.sort(
(a, b) => a.name.compareTo(b.name),
);
}
});
},
// If the player matches the current search query (or search is empty),
// they are added back to the `suggestedPlayers` and the list is re-sorted.
if (currentSearch.isEmpty ||
player.name.toLowerCase().contains(
currentSearch,
)) {
suggestedPlayers.add(player);
suggestedPlayers.sort(
(a, b) => a.name.compareTo(b.name),
);
}
});
},
),
),
),
],
],
),
),
),
),
),
const SizedBox(height: 10),
Text(
@@ -222,35 +226,53 @@ class _PlayerSelectionState extends State<PlayerSelection> {
db.playerDao.getAllPlayers(),
Future.delayed(Constants.MINIMUM_SKELETON_DURATION),
]).then((results) => results[0] as List<Player>);
if (mounted) {
_allPlayersFuture.then((loadedPlayers) {
setState(() {
// If a list of available players is provided (even if empty), use that list.
if (widget.availablePlayers != null) {
widget.availablePlayers!.sort((a, b) => a.name.compareTo(b.name));
allPlayers = [...widget.availablePlayers!];
suggestedPlayers = [...allPlayers];
if (widget.initialSelectedPlayers != null) {
// Ensures that only players available for selection are pre-selected.
selectedPlayers = widget.initialSelectedPlayers!
.where(
(p) => widget.availablePlayers!.any(
(available) => available.id == p.id,
),
)
.toList();
}
_allPlayersFuture.then((loadedPlayers) {
if (!mounted) return;
setState(() {
// If a list of available players is provided (even if empty), use that list.
if (widget.availablePlayers != null) {
widget.availablePlayers!.sort((a, b) => a.name.compareTo(b.name));
allPlayers = [...widget.availablePlayers!];
suggestedPlayers = [...allPlayers];
if (widget.initialSelectedPlayers != null) {
// Ensures that only players available for selection are pre-selected.
selectedPlayers = widget.initialSelectedPlayers!
.where(
(p) => widget.availablePlayers!.any(
(available) => available.id == p.id,
),
)
.toList();
}
} else {
// Otherwise, use the loaded players from the database.
loadedPlayers.sort((a, b) => a.name.compareTo(b.name));
allPlayers = [...loadedPlayers];
if (widget.initialSelectedPlayers != null) {
// Excludes already selected players from the suggested players list.
suggestedPlayers = loadedPlayers
.where(
(p) => !widget.initialSelectedPlayers!.any(
(ip) => ip.id == p.id,
),
)
.toList();
// Ensures that only players available for selection are pre-selected.
selectedPlayers = widget.initialSelectedPlayers!
.where(
(p) => allPlayers.any((available) => available.id == p.id),
)
.toList();
} else {
// Otherwise, use the loaded players from the database.
loadedPlayers.sort((a, b) => a.name.compareTo(b.name));
allPlayers = [...loadedPlayers];
// If no initial selection, all loaded players are suggested.
suggestedPlayers = [...loadedPlayers];
}
isLoading = false;
});
}
isLoading = false;
});
}
});
}
/// Adds a new player to the database from the search bar input.
@@ -260,7 +282,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
final loc = AppLocalizations.of(context);
final playerName = _searchBarController.text.trim();
final createdPlayer = Player(name: playerName);
final createdPlayer = Player(name: playerName, description: '');
final success = await db.playerDao.addPlayer(player: createdPlayer);
if (!context.mounted) return;

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:tallee/core/custom_theme.dart';
class CustomSearchBar extends StatelessWidget {
/// A custom search bar widget that encapsulates a [SearchBar] with additional customization options.
@@ -69,7 +69,6 @@ class CustomSearchBar extends StatelessWidget {
constraints ?? const BoxConstraints(maxHeight: 45, minHeight: 45),
hintText: hintText,
onChanged: onChanged,
hintStyle: WidgetStateProperty.all(const TextStyle(fontSize: 16)),
leading: const Icon(Icons.search),
trailing: [
Visibility(
@@ -87,7 +86,9 @@ class CustomSearchBar extends StatelessWidget {
const SizedBox(width: 5),
],
backgroundColor: WidgetStateProperty.all(CustomTheme.boxColor),
side: WidgetStateProperty.all(BorderSide(color: CustomTheme.boxBorder)),
side: WidgetStateProperty.all(
const BorderSide(color: CustomTheme.boxBorderColor),
),
shape: WidgetStateProperty.all(
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:tallee/core/custom_theme.dart';
class TextInputField extends StatelessWidget {
/// A custom text input field widget that encapsulates a [TextField] with specific styling.
@@ -62,9 +62,9 @@ class TextInputField extends StatelessWidget {
borderRadius: const BorderRadius.all(Radius.circular(12)),
borderSide: BorderSide(color: CustomTheme.boxBorder),
),
focusedBorder: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(12)),
borderSide: BorderSide(color: CustomTheme.boxBorder),
focusedBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
borderSide: BorderSide(color: CustomTheme.boxBorderColor),
),
floatingLabelBehavior: FloatingLabelBehavior.never,
),

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:tallee/core/custom_theme.dart';
class ChooseTile extends StatefulWidget {
/// A tile widget that allows users to choose an option by tapping on it.

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:tallee/core/custom_theme.dart';
class CustomRadioListTile<T> extends StatelessWidget {
/// A custom radio list tile widget that encapsulates a [Radio] button with additional styling and functionality.
@@ -31,16 +31,12 @@ class CustomRadioListTile<T> extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 2),
decoration: BoxDecoration(
color: CustomTheme.boxColor,
border: Border.all(color: CustomTheme.boxBorder),
border: Border.all(color: CustomTheme.boxBorderColor),
borderRadius: CustomTheme.standardBorderRadiusAll,
),
child: Row(
children: [
Radio<T>(
value: value,
activeColor: CustomTheme.primaryColor,
toggleable: true,
),
Radio<T>(value: value, toggleable: true),
Expanded(
child: Text(
text,

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/data/dto/group.dart';
import 'package:game_tracker/presentation/widgets/tiles/text_icon_tile.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/data/dto/group.dart';
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
class GroupTile extends StatefulWidget {
/// A tile widget that displays information about a group, including its name and members.

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:tallee/core/custom_theme.dart';
class InfoTile extends StatefulWidget {
/// A tile widget that displays a title with an icon and some content below it.

View File

@@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/presentation/views/main_menu/settings_view/licenses/license_detail_view.dart';
import 'package:game_tracker/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart';
import 'package:game_tracker/presentation/widgets/colored_icon_container.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/presentation/views/main_menu/settings_view/licenses/license_detail_view.dart';
import 'package:tallee/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart';
import 'package:tallee/presentation/widgets/colored_icon_container.dart';
class LicenseTile extends StatelessWidget {
/// A tile widget that displays information about a software package license.

View File

@@ -1,10 +1,12 @@
import 'dart:core' hide Match;
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/data/dto/match.dart';
import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/l10n/generated/app_localizations.dart';
import 'package:game_tracker/presentation/widgets/tiles/text_icon_tile.dart';
import 'package:intl/intl.dart';
import 'package:tallee/core/common.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/data/dto/match.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
class MatchTile extends StatefulWidget {
/// A tile widget that displays information about a match, including its name,
@@ -38,18 +40,13 @@ class MatchTile extends StatefulWidget {
}
class _MatchTileState extends State<MatchTile> {
late final List<Player> _allPlayers;
@override
void initState() {
super.initState();
_allPlayers = _getCombinedPlayers();
}
@override
Widget build(BuildContext context) {
final group = widget.match.group;
final winner = widget.match.winner;
final match = widget.match;
final group = match.group;
final winner = match.winner;
final players = [...match.players]
..sort((a, b) => a.name.compareTo(b.name));
final loc = AppLocalizations.of(context);
return GestureDetector(
@@ -67,7 +64,7 @@ class _MatchTileState extends State<MatchTile> {
children: [
Expanded(
child: Text(
widget.match.name,
match.name,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
@@ -76,7 +73,7 @@ class _MatchTileState extends State<MatchTile> {
),
),
Text(
_formatDate(widget.match.createdAt, context),
_formatDate(match.createdAt, context),
style: const TextStyle(fontSize: 12, color: Colors.grey),
),
],
@@ -91,7 +88,7 @@ class _MatchTileState extends State<MatchTile> {
const SizedBox(width: 6),
Expanded(
child: Text(
'${group.name}${widget.match.players != null ? ' + ${widget.match.players?.length}' : ''}',
'${match.group!.name}${getExtraPlayerCount(match)}',
style: const TextStyle(fontSize: 14, color: Colors.grey),
overflow: TextOverflow.ellipsis,
),
@@ -106,7 +103,7 @@ class _MatchTileState extends State<MatchTile> {
const SizedBox(width: 6),
Expanded(
child: Text(
'${widget.match.players!.length} ${loc.players}',
'${match.players.length} ${loc.players}',
style: const TextStyle(fontSize: 14, color: Colors.grey),
overflow: TextOverflow.ellipsis,
),
@@ -192,7 +189,7 @@ class _MatchTileState extends State<MatchTile> {
const SizedBox(height: 12),
],
if (_allPlayers.isNotEmpty && widget.compact == false) ...[
if (players.isNotEmpty && widget.compact == false) ...[
Text(
loc.players,
style: const TextStyle(
@@ -205,7 +202,7 @@ class _MatchTileState extends State<MatchTile> {
Wrap(
spacing: 6,
runSpacing: 6,
children: _allPlayers.map((player) {
children: players.map((player) {
return TextIconTile(text: player.name, iconEnabled: false);
}).toList(),
),
@@ -233,34 +230,4 @@ class _MatchTileState extends State<MatchTile> {
return '${loc.created_on} ${DateFormat.yMMMd(Localizations.localeOf(context).toString()).format(dateTime)}';
}
}
/// Retrieves all unique players associated with the match,
/// combining players from both the match and its group.
List<Player> _getCombinedPlayers() {
final allPlayers = <Player>[];
final playerIds = <String>{};
// Add players from game.players
if (widget.match.players != null) {
for (var player in widget.match.players!) {
if (!playerIds.contains(player.id)) {
allPlayers.add(player);
playerIds.add(player.id);
}
}
}
// Add players from game.group.players
if (widget.match.group?.members != null) {
for (var player in widget.match.group!.members) {
if (!playerIds.contains(player.id)) {
allPlayers.add(player);
playerIds.add(player.id);
}
}
}
allPlayers.sort((a, b) => a.name.compareTo(b.name));
return allPlayers;
}
}

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:tallee/core/custom_theme.dart';
class QuickInfoTile extends StatefulWidget {
/// A tile widget that displays a title with an icon and a numeric value below it.

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/presentation/widgets/colored_icon_container.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/presentation/widgets/colored_icon_container.dart';
class SettingsListTile extends StatelessWidget {
/// A customizable settings list tile widget that displays an icon, title, and an optional suffix widget.

View File

@@ -1,8 +1,8 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:game_tracker/l10n/generated/app_localizations.dart';
import 'package:game_tracker/presentation/widgets/tiles/info_tile.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/widgets/tiles/info_tile.dart';
class StatisticsTile extends StatelessWidget {
/// A tile widget that displays statistical data using horizontal bars.

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:tallee/core/custom_theme.dart';
class TextIconListTile extends StatelessWidget {
/// A list tile widget that displays text with an optional icon button.

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:tallee/core/custom_theme.dart';
class TextIconTile extends StatelessWidget {
/// A tile widget that displays text with an optional icon that can be tapped.

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
import 'package:tallee/core/custom_theme.dart';
class TitleDescriptionListTile extends StatelessWidget {
/// A list tile widget that displays a title and description, with optional highlighting and badge.