Refactored components
This commit is contained in:
@@ -2,6 +2,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
|
||||
/// Button types used for styling the [CustomWidthButton]
|
||||
/// - [ButtonType.primary]: Primary button style.
|
||||
/// - [ButtonType.secondary]: Secondary button style.
|
||||
/// - [ButtonType.tertiary]: Tertiary button style.
|
||||
enum ButtonType { primary, secondary, tertiary }
|
||||
|
||||
/// Result types for import operations in the [SettingsView]
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:skeletonizer/skeletonizer.dart';
|
||||
|
||||
/// A widget that provides a skeleton loading effect to its child widget tree.
|
||||
/// - [child]: The widget tree to apply the skeleton effect to.
|
||||
/// - [enabled]: A boolean to enable or disable the skeleton effect.
|
||||
/// - [fixLayoutBuilder]: A boolean to fix the layout builder for AnimatedSwitcher.
|
||||
class AppSkeleton extends StatefulWidget {
|
||||
final Widget child;
|
||||
final bool enabled;
|
||||
final bool fixLayoutBuilder;
|
||||
|
||||
const AppSkeleton({
|
||||
super.key,
|
||||
required this.child,
|
||||
@@ -13,6 +13,15 @@ class AppSkeleton extends StatefulWidget {
|
||||
this.fixLayoutBuilder = false,
|
||||
});
|
||||
|
||||
/// The widget tree to apply the skeleton effect to.
|
||||
final Widget child;
|
||||
|
||||
/// A boolean to enable or disable the skeleton effect.
|
||||
final bool enabled;
|
||||
|
||||
/// A boolean to fix the layout builder for AnimatedSwitcher.
|
||||
final bool fixLayoutBuilder;
|
||||
|
||||
@override
|
||||
State<AppSkeleton> createState() => _AppSkeletonState();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,12 @@ import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/core/enums.dart';
|
||||
|
||||
/// A custom button widget that is designed to have a width relative to the screen size.
|
||||
/// It supports three types of buttons: primary, secondary, and text buttons.
|
||||
/// - [text]: The text to display on the button.
|
||||
/// - [buttonType]: The type of button to display. Defaults to [ButtonType.primary].
|
||||
/// - [sizeRelativeToWidth]: The size of the button relative to the width of the screen.
|
||||
/// - [onPressed]: The callback to be invoked when the button is pressed.
|
||||
class CustomWidthButton extends StatelessWidget {
|
||||
const CustomWidthButton({
|
||||
super.key,
|
||||
@@ -11,9 +17,16 @@ class CustomWidthButton extends StatelessWidget {
|
||||
this.onPressed,
|
||||
});
|
||||
|
||||
/// The text to display on the button.
|
||||
final String text;
|
||||
|
||||
/// The size of the button relative to the width of the screen.
|
||||
final double sizeRelativeToWidth;
|
||||
|
||||
/// The callback to be invoked when the button is pressed.
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
/// The type of button to display. Depends on the enum [ButtonType].
|
||||
final ButtonType buttonType;
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
|
||||
/// A button widget designed for quick creating matches in the [HomeView]
|
||||
/// - [text]: The text to display on the button.
|
||||
/// - [onPressed]: The callback to be invoked when the button is pressed.
|
||||
class QuickCreateButton extends StatefulWidget {
|
||||
final String text;
|
||||
final VoidCallback? onPressed;
|
||||
const QuickCreateButton({
|
||||
super.key,
|
||||
required this.text,
|
||||
required this.onPressed,
|
||||
});
|
||||
|
||||
/// The text to display on the button.
|
||||
final String text;
|
||||
|
||||
/// The callback to be invoked when the button is pressed.
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
@override
|
||||
State<QuickCreateButton> createState() => _QuickCreateButtonState();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// A navigation bar item widget that represents a single tab in a navigation bar.
|
||||
/// - [index]: The index of the tab.
|
||||
/// - [isSelected]: A boolean indicating whether the tab is currently selected.
|
||||
/// - [icon]: The icon to display for the tab.
|
||||
/// - [label]: The label to display for the tab.
|
||||
/// - [onTabTapped]: The callback to be invoked when the tab is tapped.
|
||||
class NavbarItem extends StatefulWidget {
|
||||
final int index;
|
||||
final bool isSelected;
|
||||
final IconData icon;
|
||||
final String label;
|
||||
final Function(int) onTabTapped;
|
||||
|
||||
const NavbarItem({
|
||||
super.key,
|
||||
required this.index,
|
||||
@@ -16,6 +16,21 @@ class NavbarItem extends StatefulWidget {
|
||||
required this.onTabTapped,
|
||||
});
|
||||
|
||||
/// The index of the tab.
|
||||
final int index;
|
||||
|
||||
/// A boolean indicating whether the tab is currently selected.
|
||||
final bool isSelected;
|
||||
|
||||
/// The icon to display for the tab.
|
||||
final IconData icon;
|
||||
|
||||
/// The label to display for the tab.
|
||||
final String label;
|
||||
|
||||
/// The callback to be invoked when the tab is tapped.
|
||||
final Function(int) onTabTapped;
|
||||
|
||||
@override
|
||||
State<NavbarItem> createState() => _NavbarItemState();
|
||||
}
|
||||
|
||||
@@ -11,31 +11,55 @@ 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';
|
||||
|
||||
/// A widget that allows users to select players from a list,
|
||||
/// with search functionality and the ability to add new players.
|
||||
/// - [availablePlayers]: An optional list of players to choose from. If null, all
|
||||
/// players from the database are used.
|
||||
/// - [initialSelectedPlayers]: An optional list of players that should be pre-selected.
|
||||
/// - [onChanged]: A callback function that is invoked whenever the selection changes,
|
||||
/// providing the updated list of selected players.
|
||||
class PlayerSelection extends StatefulWidget {
|
||||
final Function(List<Player> value) onChanged;
|
||||
final List<Player>? availablePlayers;
|
||||
final List<Player>? initialSelectedPlayers;
|
||||
|
||||
const PlayerSelection({
|
||||
super.key,
|
||||
required this.onChanged,
|
||||
this.availablePlayers,
|
||||
this.initialSelectedPlayers,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
/// An optional list of players to choose from. If null, all players from the database are used.
|
||||
final List<Player>? availablePlayers;
|
||||
|
||||
/// An optional list of players that should be pre-selected.
|
||||
final List<Player>? initialSelectedPlayers;
|
||||
|
||||
/// A callback function that is invoked whenever the selection changes,
|
||||
final Function(List<Player> value) onChanged;
|
||||
|
||||
@override
|
||||
State<PlayerSelection> createState() => _PlayerSelectionState();
|
||||
}
|
||||
|
||||
class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
List<Player> selectedPlayers = [];
|
||||
List<Player> suggestedPlayers = [];
|
||||
List<Player> allPlayers = [];
|
||||
late final AppDatabase db;
|
||||
bool isLoading = true;
|
||||
|
||||
/// Future that loads all players from the database.
|
||||
late Future<List<Player>> _allPlayersFuture;
|
||||
|
||||
/// The complete list of all available players.
|
||||
List<Player> allPlayers = [];
|
||||
|
||||
/// The list of players suggested based on the search input.
|
||||
List<Player> suggestedPlayers = [];
|
||||
|
||||
/// The list of currently selected players.
|
||||
List<Player> selectedPlayers = [];
|
||||
|
||||
/// Controller for the search bar input.
|
||||
late final TextEditingController _searchBarController =
|
||||
TextEditingController();
|
||||
late final AppDatabase db;
|
||||
late Future<List<Player>> _allPlayersFuture;
|
||||
|
||||
/// Skeleton data used while loading players.
|
||||
late final List<Player> skeletonData = List.filled(
|
||||
7,
|
||||
Player(name: 'Player 0'),
|
||||
@@ -49,42 +73,6 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
loadPlayerList();
|
||||
}
|
||||
|
||||
void loadPlayerList() {
|
||||
_allPlayersFuture = Future.wait([
|
||||
db.playerDao.getAllPlayers(),
|
||||
Future.delayed(Constants.minimumSkeletonDuration),
|
||||
]).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();
|
||||
}
|
||||
} else {
|
||||
// Otherwise, use the loaded players from the database.
|
||||
loadedPlayers.sort((a, b) => a.name.compareTo(b.name));
|
||||
allPlayers = [...loadedPlayers];
|
||||
suggestedPlayers = [...loadedPlayers];
|
||||
}
|
||||
isLoading = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
@@ -227,53 +215,97 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
);
|
||||
}
|
||||
|
||||
/// Loads the list of players from the database or uses the provided available players.
|
||||
/// Sets the loading state and updates the player lists accordingly.
|
||||
void loadPlayerList() {
|
||||
_allPlayersFuture = Future.wait([
|
||||
db.playerDao.getAllPlayers(),
|
||||
Future.delayed(Constants.minimumSkeletonDuration),
|
||||
]).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();
|
||||
}
|
||||
} else {
|
||||
// Otherwise, use the loaded players from the database.
|
||||
loadedPlayers.sort((a, b) => a.name.compareTo(b.name));
|
||||
allPlayers = [...loadedPlayers];
|
||||
suggestedPlayers = [...loadedPlayers];
|
||||
}
|
||||
isLoading = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a new player to the database from the search bar input.
|
||||
/// Shows a snackbar indicating success or failure.
|
||||
/// [context] - BuildContext to show the snackbar.
|
||||
void addNewPlayerFromSearch({required BuildContext context}) async {
|
||||
Future<void> addNewPlayerFromSearch({required BuildContext context}) async {
|
||||
final loc = AppLocalizations.of(context);
|
||||
String playerName = _searchBarController.text.trim();
|
||||
Player createdPlayer = Player(name: playerName);
|
||||
bool success = await db.playerDao.addPlayer(player: createdPlayer);
|
||||
final playerName = _searchBarController.text.trim();
|
||||
|
||||
final createdPlayer = Player(name: playerName);
|
||||
final success = await db.playerDao.addPlayer(player: createdPlayer);
|
||||
|
||||
if (!context.mounted) return;
|
||||
|
||||
if (success) {
|
||||
selectedPlayers.insert(0, createdPlayer);
|
||||
widget.onChanged([...selectedPlayers]);
|
||||
allPlayers.add(createdPlayer);
|
||||
setState(() {
|
||||
_searchBarController.clear();
|
||||
suggestedPlayers = allPlayers.where((player) {
|
||||
return !selectedPlayers.contains(player);
|
||||
}).toList();
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: CustomTheme.boxColor,
|
||||
content: Center(
|
||||
child: Text(
|
||||
AppLocalizations.of(
|
||||
context,
|
||||
).successfully_added_player(playerName),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
_handleSuccessfulPlayerCreation(createdPlayer);
|
||||
showSnackBarMessage(loc.successfully_added_player(playerName));
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: CustomTheme.boxColor,
|
||||
content: Center(
|
||||
child: Text(
|
||||
loc.could_not_add_player(playerName),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
showSnackBarMessage(loc.could_not_add_player(playerName));
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the state after successfully adding a new player.
|
||||
void _handleSuccessfulPlayerCreation(Player player) {
|
||||
selectedPlayers.insert(0, player);
|
||||
widget.onChanged([...selectedPlayers]);
|
||||
allPlayers.add(player);
|
||||
|
||||
setState(() {
|
||||
_searchBarController.clear();
|
||||
_updateSuggestedPlayers();
|
||||
});
|
||||
}
|
||||
|
||||
/// Updates the suggested players list based on current selection.
|
||||
void _updateSuggestedPlayers() {
|
||||
suggestedPlayers = allPlayers
|
||||
.where((player) => !selectedPlayers.contains(player))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Displays a snackbar message at the bottom of the screen.
|
||||
/// [message] - The message to display in the snackbar.
|
||||
void showSnackBarMessage(String message) {
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: CustomTheme.boxColor,
|
||||
content: Center(
|
||||
child: Text(message, style: const TextStyle(color: Colors.white)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Determines the appropriate info text to display when no players
|
||||
/// are available in the suggested players list.
|
||||
String _getInfoText(BuildContext context) {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
|
||||
/// A custom search bar widget that encapsulates a [SearchBar] with additional customization options.
|
||||
/// - [controller]: The controller for the search bar's text input.
|
||||
/// - [hintText]: The hint text displayed in the search bar when it is empty.
|
||||
/// - [trailingButtonShown]: Whether to show the trailing button.
|
||||
/// - [trailingButtonicon]: The icon for the trailing button.
|
||||
/// - [trailingButtonEnabled]: Whether the trailing button is in enabled state.
|
||||
/// - [onTrailingButtonPressed]: The callback invoked when the trailing button is pressed.
|
||||
/// - [onChanged]: The callback invoked when the text in the search bar changes.
|
||||
/// - [constraints]: The constraints for the search bar.
|
||||
class CustomSearchBar extends StatelessWidget {
|
||||
final TextEditingController controller;
|
||||
final String hintText;
|
||||
final ValueChanged<String>? onChanged;
|
||||
final BoxConstraints? constraints;
|
||||
final bool trailingButtonShown;
|
||||
final bool trailingButtonEnabled;
|
||||
final VoidCallback? onTrailingButtonPressed;
|
||||
final IconData trailingButtonicon;
|
||||
|
||||
const CustomSearchBar({
|
||||
super.key,
|
||||
required this.controller,
|
||||
@@ -23,6 +23,30 @@ class CustomSearchBar extends StatelessWidget {
|
||||
this.constraints,
|
||||
});
|
||||
|
||||
/// The controller for the search bar's text input.
|
||||
final TextEditingController controller;
|
||||
|
||||
/// The hint text displayed in the search bar when it is empty.
|
||||
final String hintText;
|
||||
|
||||
/// Whether to show the trailing button.
|
||||
final bool trailingButtonShown;
|
||||
|
||||
/// The icon for the trailing button.
|
||||
final IconData trailingButtonicon;
|
||||
|
||||
/// Whether the trailing button is in enabled state.
|
||||
final bool trailingButtonEnabled;
|
||||
|
||||
/// The callback invoked when the trailing button is pressed.
|
||||
final VoidCallback? onTrailingButtonPressed;
|
||||
|
||||
/// The callback invoked when the text in the search bar changes.
|
||||
final ValueChanged<String>? onChanged;
|
||||
|
||||
/// The constraints for the search bar.
|
||||
final BoxConstraints? constraints;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SearchBar(
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
|
||||
/// A custom text input field widget that encapsulates a [TextField] with specific styling.
|
||||
/// - [controller]: The controller for the text input field.
|
||||
/// - [onChanged]: The callback invoked when the text in the field changes.
|
||||
/// - [hintText]: The hint text displayed in the text input field when it is empty
|
||||
class TextInputField extends StatelessWidget {
|
||||
final TextEditingController controller;
|
||||
final ValueChanged<String>? onChanged;
|
||||
final String hintText;
|
||||
|
||||
const TextInputField({
|
||||
super.key,
|
||||
required this.controller,
|
||||
@@ -13,6 +13,15 @@ class TextInputField extends StatelessWidget {
|
||||
this.onChanged,
|
||||
});
|
||||
|
||||
/// The controller for the text input field.
|
||||
final TextEditingController controller;
|
||||
|
||||
/// The callback invoked when the text in the field changes.
|
||||
final ValueChanged<String>? onChanged;
|
||||
|
||||
/// The hint text displayed in the text input field when it is empty.
|
||||
final String hintText;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
|
||||
/// A tile widget that allows users to choose an option by tapping on it.
|
||||
/// - [title]: The title text displayed on the tile.
|
||||
/// - [trailingText]: Optional trailing text displayed on the tile.
|
||||
/// - [onPressed]: The callback invoked when the tile is tapped.
|
||||
class ChooseTile extends StatefulWidget {
|
||||
final String title;
|
||||
final VoidCallback? onPressed;
|
||||
final String? trailingText;
|
||||
const ChooseTile({
|
||||
super.key,
|
||||
required this.title,
|
||||
@@ -12,6 +13,15 @@ class ChooseTile extends StatefulWidget {
|
||||
this.onPressed,
|
||||
});
|
||||
|
||||
/// The title text displayed on the tile.
|
||||
final String title;
|
||||
|
||||
/// The callback invoked when the tile is tapped.
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
/// Optional trailing text displayed on the tile.
|
||||
final String? trailingText;
|
||||
|
||||
@override
|
||||
State<ChooseTile> createState() => _ChooseTileState();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
|
||||
/// A custom radio list tile widget that encapsulates a [Radio] button with additional styling and functionality.
|
||||
/// - [text]: The text to display next to the radio button.
|
||||
/// - [value]: The value associated with the radio button.
|
||||
/// - [onContainerTap]: The callback invoked when the container is tapped.
|
||||
class CustomRadioListTile<T> extends StatelessWidget {
|
||||
final String text;
|
||||
final T value;
|
||||
final ValueChanged<T> onContainerTap;
|
||||
|
||||
const CustomRadioListTile({
|
||||
super.key,
|
||||
required this.text,
|
||||
@@ -13,6 +13,15 @@ class CustomRadioListTile<T> extends StatelessWidget {
|
||||
required this.onContainerTap,
|
||||
});
|
||||
|
||||
/// The text to display next to the radio button.
|
||||
final String text;
|
||||
|
||||
/// The value associated with the radio button.
|
||||
final T value;
|
||||
|
||||
/// The callback invoked when the container is tapped.
|
||||
final ValueChanged<T> onContainerTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
|
||||
@@ -3,10 +3,16 @@ 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';
|
||||
|
||||
/// A tile widget that displays information about a group, including its name and members.
|
||||
/// - [group]: The group data to be displayed.
|
||||
/// - [isHighlighted]: Whether the tile should be highlighted.
|
||||
class GroupTile extends StatelessWidget {
|
||||
const GroupTile({super.key, required this.group, this.isHighlighted = false});
|
||||
|
||||
/// The group data to be displayed.
|
||||
final Group group;
|
||||
|
||||
/// Whether the tile should be highlighted.
|
||||
final bool isHighlighted;
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
|
||||
/// A tile widget that displays a title with an icon and some content below it.
|
||||
/// - [title]: The title text displayed on the tile.
|
||||
/// - [icon]: The icon displayed next to the title.
|
||||
/// - [content]: The content widget displayed below the title.
|
||||
/// - [padding]: Optional padding for the tile content.
|
||||
/// - [height]: Optional height for the tile.
|
||||
/// - [width]: Optional width for the tile.
|
||||
class InfoTile extends StatefulWidget {
|
||||
final String title;
|
||||
final IconData icon;
|
||||
final Widget content;
|
||||
final EdgeInsets? padding;
|
||||
final double? height;
|
||||
final double? width;
|
||||
const InfoTile({
|
||||
super.key,
|
||||
required this.title,
|
||||
@@ -18,6 +19,24 @@ class InfoTile extends StatefulWidget {
|
||||
this.width,
|
||||
});
|
||||
|
||||
/// The title text displayed on the tile.
|
||||
final String title;
|
||||
|
||||
/// The icon displayed next to the title.
|
||||
final IconData icon;
|
||||
|
||||
/// The content widget displayed below the title.
|
||||
final Widget content;
|
||||
|
||||
/// Optional padding for the tile content.
|
||||
final EdgeInsets? padding;
|
||||
|
||||
/// Optional height for the tile.
|
||||
final double? height;
|
||||
|
||||
/// Optional width for the tile.
|
||||
final double? width;
|
||||
|
||||
@override
|
||||
State<InfoTile> createState() => _InfoTileState();
|
||||
}
|
||||
|
||||
@@ -1,26 +1,41 @@
|
||||
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';
|
||||
|
||||
/// A tile widget that displays information about a match, including its name,
|
||||
/// creation date, associated group, winner, and players.
|
||||
/// - [match]: The match data to be displayed.
|
||||
/// - [onTap]: The callback invoked when the tile is tapped.
|
||||
class MatchTile extends StatefulWidget {
|
||||
final Match match;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const MatchTile({super.key, required this.match, required this.onTap});
|
||||
|
||||
/// The match data to be displayed.
|
||||
final Match match;
|
||||
|
||||
/// The callback invoked when the tile is tapped.
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
State<MatchTile> createState() => _MatchTileState();
|
||||
}
|
||||
|
||||
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 allPlayers = _getAllPlayers();
|
||||
final loc = AppLocalizations.of(context);
|
||||
|
||||
return GestureDetector(
|
||||
@@ -114,7 +129,7 @@ class _MatchTileState extends State<MatchTile> {
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
|
||||
if (allPlayers.isNotEmpty) ...[
|
||||
if (_allPlayers.isNotEmpty) ...[
|
||||
Text(
|
||||
loc.players,
|
||||
style: const TextStyle(
|
||||
@@ -127,7 +142,7 @@ class _MatchTileState extends State<MatchTile> {
|
||||
Wrap(
|
||||
spacing: 6,
|
||||
runSpacing: 6,
|
||||
children: allPlayers.map((player) {
|
||||
children: _allPlayers.map((player) {
|
||||
return TextIconTile(text: player.name, iconEnabled: false);
|
||||
}).toList(),
|
||||
),
|
||||
@@ -138,6 +153,8 @@ class _MatchTileState extends State<MatchTile> {
|
||||
);
|
||||
}
|
||||
|
||||
/// Formats the given [dateTime] into a human-readable string based on its
|
||||
/// difference from the current date.
|
||||
String _formatDate(DateTime dateTime, BuildContext context) {
|
||||
final now = DateTime.now();
|
||||
final difference = now.difference(dateTime);
|
||||
@@ -158,8 +175,10 @@ class _MatchTileState extends State<MatchTile> {
|
||||
}
|
||||
}
|
||||
|
||||
List<dynamic> _getAllPlayers() {
|
||||
final allPlayers = <dynamic>[];
|
||||
/// 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
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
|
||||
/// A tile widget that displays a title with an icon and a numeric value below it.
|
||||
/// - [title]: The title text displayed on the tile.
|
||||
/// - [icon]: The icon displayed next to the title.
|
||||
/// - [value]: The numeric value displayed below the title.
|
||||
/// - [height]: Optional height for the tile.
|
||||
/// - [width]: Optional width for the tile.
|
||||
/// - [padding]: Optional padding for the tile content.
|
||||
class QuickInfoTile extends StatefulWidget {
|
||||
final String title;
|
||||
final IconData icon;
|
||||
final int value;
|
||||
final double? height;
|
||||
final double? width;
|
||||
final EdgeInsets? padding;
|
||||
const QuickInfoTile({
|
||||
super.key,
|
||||
required this.title,
|
||||
@@ -18,6 +19,24 @@ class QuickInfoTile extends StatefulWidget {
|
||||
this.padding,
|
||||
});
|
||||
|
||||
/// The title text displayed on the tile.
|
||||
final String title;
|
||||
|
||||
/// The icon displayed next to the title.
|
||||
final IconData icon;
|
||||
|
||||
/// The numeric value displayed below the title.
|
||||
final int value;
|
||||
|
||||
/// Optional height for the tile.
|
||||
final double? height;
|
||||
|
||||
/// Optional width for the tile.
|
||||
final double? width;
|
||||
|
||||
/// Optional padding for the tile content.
|
||||
final EdgeInsets? padding;
|
||||
|
||||
@override
|
||||
State<QuickInfoTile> createState() => _QuickInfoTileState();
|
||||
}
|
||||
|
||||
@@ -1,19 +1,32 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
|
||||
/// A customizable settings list tile widget that displays an icon, title, and an optional suffix widget.
|
||||
/// - [icon]: The icon displayed on the left side of the tile.
|
||||
/// - [title]: The title text displayed next to the icon.
|
||||
/// - [suffixWidget]: An optional widget displayed on the right side of the tile.
|
||||
/// - [onPressed]: The callback invoked when the tile is tapped.
|
||||
class SettingsListTile extends StatelessWidget {
|
||||
final VoidCallback? onPressed;
|
||||
final IconData icon;
|
||||
final String title;
|
||||
final Widget? suffixWidget;
|
||||
const SettingsListTile({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.icon,
|
||||
required this.title,
|
||||
this.suffixWidget,
|
||||
this.onPressed,
|
||||
});
|
||||
|
||||
/// The icon displayed on the left side of the tile.
|
||||
final IconData icon;
|
||||
|
||||
/// The title text displayed next to the icon.
|
||||
final String title;
|
||||
|
||||
/// An optional widget displayed on the right side of the tile.
|
||||
final Widget? suffixWidget;
|
||||
|
||||
/// The callback invoked when the tile is tapped.
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
|
||||
@@ -4,6 +4,13 @@ import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/l10n/generated/app_localizations.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/info_tile.dart';
|
||||
|
||||
/// A tile widget that displays statistical data using horizontal bars.
|
||||
/// - [icon]: The icon displayed next to the title.
|
||||
/// - [title]: The title text displayed on the tile.
|
||||
/// - [width]: The width of the tile.
|
||||
/// - [values]: A list of tuples containing labels and their corresponding numeric values.
|
||||
/// - [itemCount]: The maximum number of items to display.
|
||||
/// - [barColor]: The color of the bars representing the values.
|
||||
class StatisticsTile extends StatelessWidget {
|
||||
const StatisticsTile({
|
||||
super.key,
|
||||
@@ -15,11 +22,22 @@ class StatisticsTile extends StatelessWidget {
|
||||
required this.barColor,
|
||||
});
|
||||
|
||||
/// The icon displayed next to the title.
|
||||
final IconData icon;
|
||||
|
||||
/// The title text displayed on the tile.
|
||||
final String title;
|
||||
|
||||
/// The width of the tile.
|
||||
final double width;
|
||||
|
||||
/// A list of tuples containing labels and their corresponding numeric values.
|
||||
final List<(String, num)> values;
|
||||
|
||||
/// The maximum number of items to display.
|
||||
final int itemCount;
|
||||
|
||||
/// The color of the bars representing the values.
|
||||
final Color barColor;
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
|
||||
/// A list tile widget that displays text with an optional icon button.
|
||||
/// - [text]: The text to display in the tile.
|
||||
/// - [onPressed]: The callback to be invoked when the icon is pressed.
|
||||
/// - [iconEnabled]: A boolean to determine if the icon should be displayed.
|
||||
class TextIconListTile extends StatelessWidget {
|
||||
final String text;
|
||||
final VoidCallback? onPressed;
|
||||
final bool iconEnabled;
|
||||
|
||||
const TextIconListTile({
|
||||
super.key,
|
||||
required this.text,
|
||||
this.onPressed,
|
||||
this.iconEnabled = true,
|
||||
this.onPressed,
|
||||
});
|
||||
|
||||
/// The text to display in the tile.
|
||||
final String text;
|
||||
|
||||
/// A boolean to determine if the icon should be displayed.
|
||||
final bool iconEnabled;
|
||||
|
||||
/// The callback to be invoked when the icon is pressed.
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
|
||||
/// A tile widget that displays text with an optional icon that can be tapped.
|
||||
/// - [text]: The text to display in the tile.
|
||||
/// - [iconEnabled]: A boolean to determine if the icon should be displayed.
|
||||
/// - [onIconTap]: The callback to be invoked when the icon is tapped.
|
||||
class TextIconTile extends StatelessWidget {
|
||||
final String text;
|
||||
final bool iconEnabled;
|
||||
final VoidCallback? onIconTap;
|
||||
|
||||
const TextIconTile({
|
||||
super.key,
|
||||
required this.text,
|
||||
this.onIconTap,
|
||||
this.iconEnabled = true,
|
||||
this.onIconTap,
|
||||
});
|
||||
|
||||
/// The text to display in the tile.
|
||||
final String text;
|
||||
|
||||
/// A boolean to determine if the icon should be displayed.
|
||||
final bool iconEnabled;
|
||||
|
||||
/// The callback to be invoked when the icon is tapped.
|
||||
final VoidCallback? onIconTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
|
||||
/// A list tile widget that displays a title and description, with optional highlighting and badge.
|
||||
/// - [title]: The title text displayed on the tile.
|
||||
/// - [description]: The description text displayed below the title.
|
||||
/// - [onPressed]: The callback invoked when the tile is tapped.
|
||||
/// - [isHighlighted]: A boolean to determine if the tile should be highlighted.
|
||||
/// - [badgeText]: Optional text to display in a badge on the right side of the title.
|
||||
/// - [badgeColor]: Optional color for the badge background.
|
||||
class TitleDescriptionListTile extends StatelessWidget {
|
||||
final String title;
|
||||
final String description;
|
||||
final VoidCallback? onPressed;
|
||||
final bool isHighlighted;
|
||||
final String? badgeText;
|
||||
final Color? badgeColor;
|
||||
|
||||
const TitleDescriptionListTile({
|
||||
super.key,
|
||||
required this.title,
|
||||
@@ -19,6 +19,24 @@ class TitleDescriptionListTile extends StatelessWidget {
|
||||
this.badgeColor,
|
||||
});
|
||||
|
||||
/// The title text displayed on the tile.
|
||||
final String title;
|
||||
|
||||
/// The description text displayed below the title.
|
||||
final String description;
|
||||
|
||||
/// The callback invoked when the tile is tapped.
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
/// A boolean to determine if the tile should be highlighted.
|
||||
final bool isHighlighted;
|
||||
|
||||
/// Optional text to display in a badge on the right side of the title.
|
||||
final String? badgeText;
|
||||
|
||||
/// Optional color for the badge background.
|
||||
final Color? badgeColor;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// A widget that displays a message centered at the top of the screen with an icon, title, and message.
|
||||
/// - [icon]: The icon to display above the title.
|
||||
/// - [title]: The title text to display.
|
||||
/// - [message]: The message text to display below the title.
|
||||
class TopCenteredMessage extends StatelessWidget {
|
||||
const TopCenteredMessage({
|
||||
super.key,
|
||||
@@ -8,10 +12,15 @@ class TopCenteredMessage extends StatelessWidget {
|
||||
required this.message,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final String message;
|
||||
/// The icon to display above the title.
|
||||
final IconData icon;
|
||||
|
||||
/// The title text to display.
|
||||
final String title;
|
||||
|
||||
/// The message text to display below the title.
|
||||
final String message;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
|
||||
Reference in New Issue
Block a user