Merge branch 'development' into feature/168-teamspiele-implementieren

# Conflicts:
#	lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart
#	lib/presentation/views/main_menu/match_view/match_detail_view.dart
#	lib/presentation/views/main_menu/match_view/match_result_view.dart
#	lib/presentation/widgets/buttons/main_menu_button.dart
#	pubspec.yaml
This commit is contained in:
2026-05-18 01:06:46 +02:00
39 changed files with 846 additions and 380 deletions

View File

@@ -20,11 +20,11 @@ jobs:
uses: subosito/flutter-action@v2 uses: subosito/flutter-action@v2
with: with:
channel: stable channel: stable
flutter-version: 3.38.6 flutter-version: 3.41.0
- name: Get dependencies - name: Get dependencies
run: | run: |
git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.38.6-x64 git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.41.0-x64
flutter pub get flutter pub get
- name: Analyze Formatting - name: Analyze Formatting
@@ -46,11 +46,11 @@ jobs:
uses: subosito/flutter-action@v2 uses: subosito/flutter-action@v2
with: with:
channel: stable channel: stable
flutter-version: 3.38.6 flutter-version: 3.41.0
- name: Get dependencies - name: Get dependencies
run: | run: |
git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.38.6-x64 git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.41.0-x64
flutter pub get flutter pub get
- name: Run tests - name: Run tests

View File

@@ -32,11 +32,11 @@ jobs:
uses: subosito/flutter-action@v2 uses: subosito/flutter-action@v2
with: with:
channel: stable channel: stable
flutter-version: 3.38.6 flutter-version: 3.41.0
- name: Get dependencies - name: Get dependencies
run: | run: |
git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.38.6-x64 git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.41.0-x64
flutter pub get flutter pub get
- name: Build APK - name: Build APK
@@ -58,11 +58,11 @@ jobs:
uses: subosito/flutter-action@v2 uses: subosito/flutter-action@v2
with: with:
channel: stable channel: stable
flutter-version: 3.38.6 flutter-version: 3.41.0
- name: Get dependencies - name: Get dependencies
run: | run: |
git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.38.6-x64 git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.41.0-x64
flutter pub get flutter pub get
- name: Run tests - name: Run tests
@@ -118,11 +118,11 @@ jobs:
uses: subosito/flutter-action@v2 uses: subosito/flutter-action@v2
with: with:
channel: stable channel: stable
flutter-version: 3.38.6 flutter-version: 3.41.0
- name: Get dependencies - name: Get dependencies
run: | run: |
git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.38.6-x64 git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.41.0-x64
flutter pub get flutter pub get
- name: Generate oss_licenses.dart - name: Generate oss_licenses.dart
@@ -161,11 +161,11 @@ jobs:
uses: subosito/flutter-action@v2 uses: subosito/flutter-action@v2
with: with:
channel: stable channel: stable
flutter-version: 3.38.6 flutter-version: 3.41.0
- name: Get dependencies - name: Get dependencies
run: | run: |
git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.38.6-x64 git config --global --add safe.directory /opt/hostedtoolcache/flutter/stable-3.41.0-x64
flutter pub get flutter pub get
- name: Check code format - name: Check code format

View File

@@ -1,4 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_back_button.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_close_button.dart';
/// Theme class that defines colors, border radius, padding, and decorations /// Theme class that defines colors, border radius, padding, and decorations
class CustomTheme { class CustomTheme {
@@ -83,6 +85,11 @@ class CustomTheme {
iconTheme: IconThemeData(color: textColor), iconTheme: IconThemeData(color: textColor),
); );
static final ActionIconThemeData actionIconTheme = ActionIconThemeData(
backButtonIconBuilder: (context) => const HapticBackButton(),
closeButtonIconBuilder: (context) => const HapticCloseButton(),
);
static const SearchBarThemeData searchBarTheme = SearchBarThemeData( static const SearchBarThemeData searchBarTheme = SearchBarThemeData(
textStyle: WidgetStatePropertyAll(TextStyle(color: textColor)), textStyle: WidgetStatePropertyAll(TextStyle(color: textColor)),
hintStyle: WidgetStatePropertyAll(TextStyle(color: hintColor)), hintStyle: WidgetStatePropertyAll(TextStyle(color: hintColor)),

View File

@@ -34,21 +34,13 @@ enum ExportResult { success, canceled, unknownException }
/// - [Ruleset.multipleWinners]: Multiple players can be winners. /// - [Ruleset.multipleWinners]: Multiple players can be winners.
/// - [Ruleset.placement]: The player with the highest placement wins. /// - [Ruleset.placement]: The player with the highest placement wins.
enum Ruleset { enum Ruleset {
singleWinner,
multipleWinners,
highestScore, highestScore,
lowestScore, lowestScore,
singleWinner,
singleLoser,
multipleWinners,
placement, placement,
singleLoser,
} }
/// Different colors available for games /// Different colors for highlighting games
/// - [GameColor.red]: Red color enum GameColor { red, orange, yellow, green, teal, blue, purple, pink }
/// - [GameColor.blue]: Blue color
/// - [GameColor.green]: Green color
/// - [GameColor.yellow]: Yellow color
/// - [GameColor.purple]: Purple color
/// - [GameColor.orange]: Orange color
/// - [GameColor.pink]: Pink color
/// - [GameColor.teal]: Teal color
enum GameColor { red, blue, green, yellow, purple, orange, pink, teal }

View File

@@ -228,7 +228,7 @@ class ScoreEntryDao extends DatabaseAccessor<AppDatabase>
required String playerId, required String playerId,
}) async { }) async {
// Clear previous winner if exists // Clear previous winner if exists
deleteAllScoresForMatch(matchId: matchId); await deleteAllScoresForMatch(matchId: matchId);
// Set the winner's score to 1 // Set the winner's score to 1
final rowsAffected = await into(scoreEntryTable).insert( final rowsAffected = await into(scoreEntryTable).insert(
@@ -245,7 +245,7 @@ class ScoreEntryDao extends DatabaseAccessor<AppDatabase>
return rowsAffected > 0; return rowsAffected > 0;
} }
// Retrieves the winner of a match by looking for a score entry where score /// Retrieves the winner of a match by looking for a score entry where score
/// is 1. Returns `null` if no player found, else the first with the score. /// is 1. Returns `null` if no player found, else the first with the score.
Future<Player?> getWinner({required String matchId}) async { Future<Player?> getWinner({required String matchId}) async {
final query = final query =
@@ -276,13 +276,42 @@ class ScoreEntryDao extends DatabaseAccessor<AppDatabase>
/// Returns `true` if the winner was removed, `false` if there are multiple /// Returns `true` if the winner was removed, `false` if there are multiple
/// scores or if the winner cannot be removed. /// scores or if the winner cannot be removed.
Future<bool> removeWinner({required String matchId}) async { Future<bool> removeWinner({required String matchId}) async {
final scores = await getAllMatchScores(matchId: matchId);
if (scores.length > 1) {
return false;
} else {
return await deleteAllScoresForMatch(matchId: matchId); return await deleteAllScoresForMatch(matchId: matchId);
} }
/* multiple winners handling */
/// Sets the winners for a match.
///
/// Returns `true` if more than 0 rows were affected
Future<bool> setWinners({
required List<Player> winners,
required String matchId,
}) async {
// Clear previous winners if exists
await deleteAllScoresForMatch(matchId: matchId);
if (winners.isEmpty) return false;
await batch((batch) {
batch.insertAll(
scoreEntryTable,
winners
.map(
(player) => ScoreEntryTableCompanion.insert(
playerId: player.id,
matchId: matchId,
roundNumber: 0,
score: 1,
change: 0,
),
)
.toList(),
mode: InsertMode.insertOrReplace,
);
});
return true;
} }
/* Loser handling */ /* Loser handling */
@@ -354,6 +383,8 @@ class ScoreEntryDao extends DatabaseAccessor<AppDatabase>
} }
} }
/* placement handling */
/// Sets the placement for each player in a match. /// Sets the placement for each player in a match.
/// The highest score is assigned to the first player, the second highest to the second player, and so on. /// The highest score is assigned to the first player, the second highest to the second player, and so on.
Future<void> setPlacements({ Future<void> setPlacements({

View File

@@ -164,7 +164,7 @@ class Match {
return _getPlayersWithLowestScore().take(1).toList(); return _getPlayersWithLowestScore().take(1).toList();
case Ruleset.multipleWinners: case Ruleset.multipleWinners:
return []; return _getPlayersWithHighestScore().toList();
case Ruleset.placement: case Ruleset.placement:
return _getPlayersWithHighestScore().take(1).toList(); return _getPlayersWithHighestScore().take(1).toList();

View File

@@ -120,6 +120,7 @@
"search_for_groups": "Nach Gruppen suchen", "search_for_groups": "Nach Gruppen suchen",
"search_for_players": "Nach Spieler:innen suchen", "search_for_players": "Nach Spieler:innen suchen",
"select_winner": "Gewinner:in wählen", "select_winner": "Gewinner:in wählen",
"select_winners": "Gewinner:innen wählen",
"select_loser": "Verlierer:in wählen", "select_loser": "Verlierer:in wählen",
"selected_players": "Ausgewählte Spieler:innen", "selected_players": "Ausgewählte Spieler:innen",
"settings": "Einstellungen", "settings": "Einstellungen",
@@ -141,6 +142,7 @@
"undo": "Rückgängig", "undo": "Rückgängig",
"unknown_exception": "Unbekannter Fehler (siehe Konsole)", "unknown_exception": "Unbekannter Fehler (siehe Konsole)",
"winner": "Gewinner:in", "winner": "Gewinner:in",
"winners": "Gewinner:innen",
"winrate": "Siegquote", "winrate": "Siegquote",
"wins": "Siege", "wins": "Siege",
"yesterday_at": "Gestern um" "yesterday_at": "Gestern um"

View File

@@ -120,6 +120,7 @@
"search_for_groups": "Search for groups", "search_for_groups": "Search for groups",
"search_for_players": "Search for players", "search_for_players": "Search for players",
"select_winner": "Select Winner", "select_winner": "Select Winner",
"select_winners": "Select Winners",
"select_loser": "Select Loser", "select_loser": "Select Loser",
"selected_players": "Selected players", "selected_players": "Selected players",
"settings": "Settings", "settings": "Settings",
@@ -150,6 +151,7 @@
"undo": "Undo", "undo": "Undo",
"unknown_exception": "Unknown Exception (see console)", "unknown_exception": "Unknown Exception (see console)",
"winner": "Winner", "winner": "Winner",
"winners": "Winners",
"winrate": "Winrate", "winrate": "Winrate",
"wins": "Wins", "wins": "Wins",
"yesterday_at": "Yesterday at" "yesterday_at": "Yesterday at"

View File

@@ -770,6 +770,12 @@ abstract class AppLocalizations {
/// **'Select Winner'** /// **'Select Winner'**
String get select_winner; String get select_winner;
/// No description provided for @select_winners.
///
/// In en, this message translates to:
/// **'Select Winners'**
String get select_winners;
/// No description provided for @select_loser. /// No description provided for @select_loser.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -896,6 +902,12 @@ abstract class AppLocalizations {
/// **'Winner'** /// **'Winner'**
String get winner; String get winner;
/// No description provided for @winners.
///
/// In en, this message translates to:
/// **'Winners'**
String get winners;
/// No description provided for @winrate. /// No description provided for @winrate.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

View File

@@ -366,6 +366,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get select_winner => 'Gewinner:in wählen'; String get select_winner => 'Gewinner:in wählen';
@override
String get select_winners => 'Gewinner:innen wählen';
@override @override
String get select_loser => 'Verlierer:in wählen'; String get select_loser => 'Verlierer:in wählen';
@@ -434,6 +437,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get winner => 'Gewinner:in'; String get winner => 'Gewinner:in';
@override
String get winners => 'Gewinner:innen';
@override @override
String get winrate => 'Siegquote'; String get winrate => 'Siegquote';

View File

@@ -366,6 +366,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get select_winner => 'Select Winner'; String get select_winner => 'Select Winner';
@override
String get select_winners => 'Select Winners';
@override @override
String get select_loser => 'Select Loser'; String get select_loser => 'Select Loser';
@@ -433,6 +436,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get winner => 'Winner'; String get winner => 'Winner';
@override
String get winners => 'Winners';
@override @override
String get winrate => 'Winrate'; String get winrate => 'Winrate';

View File

@@ -42,6 +42,7 @@ class GameTracker extends StatelessWidget {
scaffoldBackgroundColor: CustomTheme.backgroundColor, scaffoldBackgroundColor: CustomTheme.backgroundColor,
// themes // themes
appBarTheme: CustomTheme.appBarTheme, appBarTheme: CustomTheme.appBarTheme,
actionIconTheme: CustomTheme.actionIconTheme,
inputDecorationTheme: CustomTheme.inputDecorationTheme, inputDecorationTheme: CustomTheme.inputDecorationTheme,
searchBarTheme: CustomTheme.searchBarTheme, searchBarTheme: CustomTheme.searchBarTheme,
radioTheme: CustomTheme.radioTheme, radioTheme: CustomTheme.radioTheme,

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tallee/core/adaptive_page_route.dart'; import 'package:tallee/core/adaptive_page_route.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
@@ -6,6 +7,7 @@ import 'package:tallee/presentation/views/main_menu/group_view/group_view.dart';
import 'package:tallee/presentation/views/main_menu/match_view/match_view.dart'; import 'package:tallee/presentation/views/main_menu/match_view/match_view.dart';
import 'package:tallee/presentation/views/main_menu/settings_view/settings_view.dart'; import 'package:tallee/presentation/views/main_menu/settings_view/settings_view.dart';
import 'package:tallee/presentation/views/main_menu/statistics_view.dart'; import 'package:tallee/presentation/views/main_menu/statistics_view.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
import 'package:tallee/presentation/widgets/navbar_item.dart'; import 'package:tallee/presentation/widgets/navbar_item.dart';
class CustomNavigationBar extends StatefulWidget { class CustomNavigationBar extends StatefulWidget {
@@ -53,10 +55,10 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
scrolledUnderElevation: 0, scrolledUnderElevation: 0,
actions: [ actions: [
IconButton( HapticIconButton(
onPressed: () async { onPressed: () async {
await Navigator.push( final navigator = Navigator.of(context);
context, await navigator.push(
adaptivePageRoute(builder: (_) => const SettingsView()), adaptivePageRoute(builder: (_) => const SettingsView()),
); );
setState(() { setState(() {
@@ -125,7 +127,8 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
} }
/// Handles tab tap events. Updates the current [index] state. /// Handles tab tap events. Updates the current [index] state.
void onTabTapped(int index) { void onTabTapped(int index) async {
await HapticFeedback.selectionClick();
setState(() { setState(() {
currentIndex = index; currentIndex = index;
}); });

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tallee/core/constants.dart'; import 'package:tallee/core/constants.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
@@ -133,8 +134,14 @@ class _CreateGroupViewState extends State<CreateGroupView> {
if (!mounted) return; if (!mounted) return;
if (success) { if (success) {
await HapticFeedback.successNotification();
if (mounted) {
Navigator.pop(context, updatedGroup); Navigator.pop(context, updatedGroup);
}
} else { } else {
if (mounted) {
await HapticFeedback.errorNotification();
}
showSnackbar( showSnackbar(
message: widget.groupToEdit == null message: widget.groupToEdit == null
? loc.error_creating_group ? loc.error_creating_group

View File

@@ -12,6 +12,7 @@ import 'package:tallee/data/models/player.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/views/main_menu/group_view/create_group_view.dart'; import 'package:tallee/presentation/views/main_menu/group_view/create_group_view.dart';
import 'package:tallee/presentation/widgets/app_skeleton.dart'; import 'package:tallee/presentation/widgets/app_skeleton.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart'; import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
import 'package:tallee/presentation/widgets/colored_icon_container.dart'; import 'package:tallee/presentation/widgets/colored_icon_container.dart';
import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart'; import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart';
@@ -65,7 +66,7 @@ class _GroupDetailViewState extends State<GroupDetailView> {
appBar: AppBar( appBar: AppBar(
title: Text(loc.group_profile), title: Text(loc.group_profile),
actions: [ actions: [
IconButton( HapticIconButton(
icon: const Icon(Icons.delete), icon: const Icon(Icons.delete),
onPressed: () async { onPressed: () async {
showDialog<bool>( showDialog<bool>(

View File

@@ -7,6 +7,7 @@ import 'package:tallee/data/db/database.dart';
import 'package:tallee/data/models/game.dart'; import 'package:tallee/data/models/game.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/views/main_menu/match_view/create_match/create_game_view.dart'; import 'package:tallee/presentation/views/main_menu/match_view/create_match/create_game_view.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
import 'package:tallee/presentation/widgets/text_input/custom_search_bar.dart'; import 'package:tallee/presentation/widgets/text_input/custom_search_bar.dart';
import 'package:tallee/presentation/widgets/tiles/game_tile.dart'; import 'package:tallee/presentation/widgets/tiles/game_tile.dart';
import 'package:tallee/presentation/widgets/top_centered_message.dart'; import 'package:tallee/presentation/widgets/top_centered_message.dart';
@@ -70,7 +71,7 @@ class _ChooseGameViewState extends State<ChooseGameView> {
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
appBar: AppBar( appBar: AppBar(
leading: IconButton( leading: HapticIconButton(
icon: const Icon(Icons.arrow_back_ios), icon: const Icon(Icons.arrow_back_ios),
onPressed: () { onPressed: () {
Navigator.of(context).pop( Navigator.of(context).pop(
@@ -83,7 +84,7 @@ class _ChooseGameViewState extends State<ChooseGameView> {
}, },
), ),
actions: [ actions: [
IconButton( HapticIconButton(
icon: const Icon(Icons.add), icon: const Icon(Icons.add),
onPressed: () async { onPressed: () async {
final result = await Navigator.push( final result = await Navigator.push(

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/data/models/group.dart'; import 'package:tallee/data/models/group.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
import 'package:tallee/presentation/widgets/text_input/custom_search_bar.dart'; import 'package:tallee/presentation/widgets/text_input/custom_search_bar.dart';
import 'package:tallee/presentation/widgets/tiles/group_tile.dart'; import 'package:tallee/presentation/widgets/tiles/group_tile.dart';
import 'package:tallee/presentation/widgets/top_centered_message.dart'; import 'package:tallee/presentation/widgets/top_centered_message.dart';
@@ -45,7 +46,7 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
appBar: AppBar( appBar: AppBar(
leading: IconButton( leading: HapticIconButton(
icon: const Icon(Icons.arrow_back_ios), icon: const Icon(Icons.arrow_back_ios),
onPressed: () { onPressed: () {
Navigator.of(context).pop( Navigator.of(context).pop(
@@ -111,7 +112,10 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
padding: const EdgeInsets.only(bottom: 85), padding: const EdgeInsets.only(bottom: 85),
itemCount: filteredGroups.length, itemCount: filteredGroups.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
return GestureDetector( return GroupTile(
group: filteredGroups[index],
isHighlighted:
selectedGroupId == filteredGroups[index].id,
onTap: () { onTap: () {
setState(() { setState(() {
if (selectedGroupId != filteredGroups[index].id) { if (selectedGroupId != filteredGroups[index].id) {
@@ -121,11 +125,6 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
} }
}); });
}, },
child: GroupTile(
group: filteredGroups[index],
isHighlighted:
selectedGroupId == filteredGroups[index].id,
),
); );
}, },
), ),

View File

@@ -1,6 +1,7 @@
import 'dart:math'; import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_popup/flutter_popup.dart'; import 'package:flutter_popup/flutter_popup.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tallee/core/common.dart'; import 'package:tallee/core/common.dart';
@@ -12,6 +13,7 @@ import 'package:tallee/data/models/game.dart';
import 'package:tallee/data/models/group.dart'; import 'package:tallee/data/models/group.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/widgets/buttons/custom_width_button.dart'; import 'package:tallee/presentation/widgets/buttons/custom_width_button.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart'; import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart';
import 'package:tallee/presentation/widgets/dialog/custom_dialog_action.dart'; import 'package:tallee/presentation/widgets/dialog/custom_dialog_action.dart';
import 'package:tallee/presentation/widgets/text_input/text_input_field.dart'; import 'package:tallee/presentation/widgets/text_input/text_input_field.dart';
@@ -47,9 +49,9 @@ class _CreateGameViewState extends State<CreateGameView> {
late final AppDatabase db; late final AppDatabase db;
late List<(Ruleset, String)> _rulesets; late List<(Ruleset, String)> _rulesets;
Ruleset? selectedRuleset = Ruleset.singleWinner;
late List<(GameColor, String)> _colors; late List<(GameColor, String)> _colors;
Ruleset? selectedRuleset = Ruleset.singleWinner;
GameColor? selectedColor = GameColor.orange; GameColor? selectedColor = GameColor.orange;
/// Controller for the game name input field. /// Controller for the game name input field.
@@ -77,38 +79,20 @@ class _CreateGameViewState extends State<CreateGameView> {
@override @override
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();
_rulesets = [ _rulesets = List.generate(
( Ruleset.values.length,
Ruleset.singleWinner, (index) => (
translateRulesetToString(Ruleset.singleWinner, context), Ruleset.values[index],
translateRulesetToString(Ruleset.values[index], context),
), ),
( );
Ruleset.singleLoser, _colors = List.generate(
translateRulesetToString(Ruleset.singleLoser, context), GameColor.values.length,
(index) => (
GameColor.values[index],
translateGameColorToString(GameColor.values[index], context),
), ),
( );
Ruleset.highestScore,
translateRulesetToString(Ruleset.highestScore, context),
),
(
Ruleset.lowestScore,
translateRulesetToString(Ruleset.lowestScore, context),
),
(
Ruleset.multipleWinners,
translateRulesetToString(Ruleset.multipleWinners, context),
),
];
_colors = [
(GameColor.green, translateGameColorToString(GameColor.green, context)),
(GameColor.teal, translateGameColorToString(GameColor.teal, context)),
(GameColor.blue, translateGameColorToString(GameColor.blue, context)),
(GameColor.purple, translateGameColorToString(GameColor.purple, context)),
(GameColor.pink, translateGameColorToString(GameColor.pink, context)),
(GameColor.red, translateGameColorToString(GameColor.red, context)),
(GameColor.orange, translateGameColorToString(GameColor.orange, context)),
(GameColor.yellow, translateGameColorToString(GameColor.yellow, context)),
];
if (widget.gameToEdit != null) { if (widget.gameToEdit != null) {
_gameNameController.text = widget.gameToEdit!.name; _gameNameController.text = widget.gameToEdit!.name;
@@ -138,7 +122,7 @@ class _CreateGameViewState extends State<CreateGameView> {
title: Text(isEditing ? loc.edit_game : loc.create_game), title: Text(isEditing ? loc.edit_game : loc.create_game),
actions: [ actions: [
if (isEditMode()) if (isEditMode())
IconButton( HapticIconButton(
icon: const Icon(Icons.delete), icon: const Icon(Icons.delete),
onPressed: () async { onPressed: () async {
if (!context.mounted) return; if (!context.mounted) return;
@@ -214,10 +198,13 @@ class _CreateGameViewState extends State<CreateGameView> {
// Choose ruleset tile // Choose ruleset tile
if (!isEditMode()) if (!isEditMode())
ChooseTile(title: loc.ruleset, trailing: getColorDropdown(loc)), ChooseTile(
title: loc.ruleset,
trailing: getRulesetDropdown(loc),
),
// Choose color tile // Choose color tile
ChooseTile(title: loc.color, trailing: getRulesetDropdown(loc)), ChooseTile(title: loc.color, trailing: getColorDropdown(loc)),
// Description input field // Description input field
Container( Container(
@@ -344,6 +331,12 @@ class _CreateGameViewState extends State<CreateGameView> {
contentPadding: const EdgeInsets.symmetric(horizontal: 0, vertical: 10), contentPadding: const EdgeInsets.symmetric(horizontal: 0, vertical: 10),
barrierColor: Colors.transparent, barrierColor: Colors.transparent,
contentDecoration: CustomTheme.standardBoxDecoration, contentDecoration: CustomTheme.standardBoxDecoration,
onBeforePopup: () async {
await HapticFeedback.selectionClick();
},
onAfterPopup: () async {
await HapticFeedback.selectionClick();
},
content: StatefulBuilder( content: StatefulBuilder(
builder: (context, setPopupState) => SizedBox( builder: (context, setPopupState) => SizedBox(
width: 280, width: 280,
@@ -353,7 +346,8 @@ class _CreateGameViewState extends State<CreateGameView> {
children: List.generate( children: List.generate(
_rulesets.length, _rulesets.length,
(index) => GestureDetector( (index) => GestureDetector(
onTap: () { onTap: () async {
await HapticFeedback.selectionClick();
setState(() { setState(() {
selectedRuleset = _rulesets[index].$1; selectedRuleset = _rulesets[index].$1;
}); });
@@ -427,6 +421,12 @@ class _CreateGameViewState extends State<CreateGameView> {
contentPadding: const EdgeInsets.symmetric(horizontal: 0, vertical: 10), contentPadding: const EdgeInsets.symmetric(horizontal: 0, vertical: 10),
barrierColor: Colors.transparent, barrierColor: Colors.transparent,
contentDecoration: CustomTheme.standardBoxDecoration, contentDecoration: CustomTheme.standardBoxDecoration,
onBeforePopup: () async {
await HapticFeedback.selectionClick();
},
onAfterPopup: () async {
await HapticFeedback.selectionClick();
},
content: StatefulBuilder( content: StatefulBuilder(
builder: (context, setPopupState) => SizedBox( builder: (context, setPopupState) => SizedBox(
width: 150, width: 150,
@@ -436,7 +436,8 @@ class _CreateGameViewState extends State<CreateGameView> {
children: List.generate( children: List.generate(
_colors.length, _colors.length,
(index) => GestureDetector( (index) => GestureDetector(
onTap: () { onTap: () async {
await HapticFeedback.selectionClick();
setState(() { setState(() {
selectedColor = _colors[index].$1; selectedColor = _colors[index].$1;
}); });

View File

@@ -273,11 +273,11 @@ class _CreateMatchViewState extends State<CreateMatchView> {
/// Determines whether the "Create Match" button should be enabled. /// Determines whether the "Create Match" button should be enabled.
/// ///
/// Returns `true` if: /// Returns `true` if:
/// - A ruleset is selected AND /// - A game is selected AND
/// - Either a group is selected OR at least 2 players are selected. /// - Either a group is selected OR at least 2 players are selected.
bool isSubmitButtonEnabled() { bool isSubmitButtonEnabled() {
return (selectedGroup != null || return ((selectedGroup != null || selectedPlayers.length > 1) &&
(selectedPlayers.length > 1) && selectedGame != null); selectedGame != null);
} }
/// Handles navigation when the create or save button is pressed. /// Handles navigation when the create or save button is pressed.

View File

@@ -13,6 +13,7 @@ import 'package:tallee/data/models/team.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/views/main_menu/match_view/create_match/create_match_view.dart'; import 'package:tallee/presentation/views/main_menu/match_view/create_match/create_match_view.dart';
import 'package:tallee/presentation/views/main_menu/match_view/match_result_view.dart'; import 'package:tallee/presentation/views/main_menu/match_view/match_result_view.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart'; import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
import 'package:tallee/presentation/widgets/cards/team_card.dart'; import 'package:tallee/presentation/widgets/cards/team_card.dart';
import 'package:tallee/presentation/widgets/colored_icon_container.dart'; import 'package:tallee/presentation/widgets/colored_icon_container.dart';
@@ -69,7 +70,7 @@ class _MatchDetailViewState extends State<MatchDetailView> {
appBar: AppBar( appBar: AppBar(
title: Text(loc.match_profile), title: Text(loc.match_profile),
actions: [ actions: [
IconButton( HapticIconButton(
icon: const Icon(Icons.delete), icon: const Icon(Icons.delete),
onPressed: () async { onPressed: () async {
showDialog<bool>( showDialog<bool>(
@@ -297,6 +298,7 @@ class _MatchDetailViewState extends State<MatchDetailView> {
Widget getResultWidget(AppLocalizations loc) { Widget getResultWidget(AppLocalizations loc) {
if (isSingleRowResult()) { if (isSingleRowResult()) {
return Row( return Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: getSingleResultRow(loc), children: getSingleResultRow(loc),
); );
@@ -312,9 +314,12 @@ class _MatchDetailViewState extends State<MatchDetailView> {
if (localMatch.mvp.isNotEmpty || localMatch.mvt.isNotEmpty) { if (localMatch.mvp.isNotEmpty || localMatch.mvt.isNotEmpty) {
// Single Winner / Loser // Single Winner / Loser
final mvpName = localMatch.isTeamMatch final mvps = localMatch.isTeamMatch
? localMatch.mvt.first.name ? localMatch.mvt
: localMatch.mvp.first.name; : localMatch.mvp;
final mvpName = ruleset == Ruleset.multipleWinners
? mvps.map((party) => party.name).join(', ')
: mvps.first.name;
return [ return [
Text( Text(
@@ -440,7 +445,8 @@ class _MatchDetailViewState extends State<MatchDetailView> {
// Returns if the result can be displayed in a single row // Returns if the result can be displayed in a single row
bool isSingleRowResult() { bool isSingleRowResult() {
return localMatch.game.ruleset == Ruleset.singleWinner || return localMatch.game.ruleset == Ruleset.singleWinner ||
localMatch.game.ruleset == Ruleset.singleLoser; localMatch.game.ruleset == Ruleset.singleLoser ||
localMatch.game.ruleset == Ruleset.multipleWinners;
} }
String getPlacementText(BuildContext context, int rank) { String getPlacementText(BuildContext context, int rank) {

View File

@@ -10,6 +10,8 @@ import 'package:tallee/data/models/score_entry.dart';
import 'package:tallee/data/models/team.dart'; import 'package:tallee/data/models/team.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/widgets/buttons/custom_width_button.dart'; import 'package:tallee/presentation/widgets/buttons/custom_width_button.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
import 'package:tallee/presentation/widgets/tiles/match_result_view/custom_checkbox_list_tile.dart';
import 'package:tallee/presentation/widgets/tiles/match_result_view/custom_radio_list_tile.dart'; import 'package:tallee/presentation/widgets/tiles/match_result_view/custom_radio_list_tile.dart';
import 'package:tallee/presentation/widgets/tiles/match_result_view/live_edit_list_tile.dart'; import 'package:tallee/presentation/widgets/tiles/match_result_view/live_edit_list_tile.dart';
import 'package:tallee/presentation/widgets/tiles/match_result_view/score_list_tile.dart'; import 'package:tallee/presentation/widgets/tiles/match_result_view/score_list_tile.dart';
@@ -49,9 +51,11 @@ class _MatchResultViewState extends State<MatchResultView> {
late bool isTeamMatch; late bool isTeamMatch;
/// Currently selected winner player /// Currently selected player(s)/team(s) (winner / looser)
Player? _selectedPlayer; Player? _selectedPlayer;
Team? _selectedTeam; Team? _selectedTeam;
final Set<Player> _selectedPlayers = {};
final Set<Team> _selectedTeams = {};
@override @override
void initState() { void initState() {
@@ -84,11 +88,11 @@ class _MatchResultViewState extends State<MatchResultView> {
return Scaffold( return Scaffold(
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
appBar: AppBar( appBar: AppBar(
leading: IconButton( leading: HapticIconButton(
icon: const Icon(Icons.close), icon: const Icon(Icons.close),
onPressed: () { onPressed: () {
widget.onWinnerChanged?.call(); widget.onWinnerChanged?.call();
Navigator.of(context).pop(_selectedPlayer); Navigator.pop(context);
}, },
), ),
title: Text(widget.match.name), title: Text(widget.match.name),
@@ -142,7 +146,36 @@ class _MatchResultViewState extends State<MatchResultView> {
const SizedBox(height: 10), const SizedBox(height: 10),
// Show player selection // Show player selection
if (rulesetSupportsWinnerSelection()) if (rulesetSupportsPlayerSelection())
if (ruleset == Ruleset.multipleWinners)
Expanded(
child: ListView.builder(
physics: const NeverScrollableScrollPhysics(),
itemCount: allPlayers.length,
itemBuilder: (context, index) {
return CustomCheckboxListTile(
text: allPlayers[index].name,
value: _selectedPlayers.contains(
allPlayers[index],
),
onChanged: (bool value) {
setState(() {
if (value) {
_selectedPlayers.add(
allPlayers[index],
);
} else {
_selectedPlayers.remove(
allPlayers[index],
);
}
});
},
);
},
),
)
else
Expanded( Expanded(
child: buildWinnerSelectionWidget(isTeamMatch), child: buildWinnerSelectionWidget(isTeamMatch),
), ),
@@ -152,7 +185,7 @@ class _MatchResultViewState extends State<MatchResultView> {
Expanded(child: buildScoreEntryWidget(isTeamMatch)), Expanded(child: buildScoreEntryWidget(isTeamMatch)),
// Show draggable placement list // Show draggable placement list
if (rulesetSupportsPlacement()) if (rulesetSupportsDragBehaviour())
Expanded(child: buildPlacementWidget(isTeamMatch)), Expanded(child: buildPlacementWidget(isTeamMatch)),
], ],
), ),
@@ -207,16 +240,24 @@ class _MatchResultViewState extends State<MatchResultView> {
// Prefill fields // Prefill fields
if (widget.match.mvt.isNotEmpty) { if (widget.match.mvt.isNotEmpty) {
if (rulesetSupportsWinnerSelection()) { if (rulesetSupportsPlayerSelection()) {
if (ruleset == Ruleset.multipleWinners) {
for (int i = 0; i < allTeams.length; i++) {
if (allTeams[i].score == 1) {
_selectedTeams.add(allTeams[i]);
}
}
} else {
_selectedTeam = allTeams.firstWhere( _selectedTeam = allTeams.firstWhere(
(p) => p.id == widget.match.mvt.first.id, (team) => team.id == widget.match.mvt.first.id,
); );
}
} else if (rulesetSupportsScoreEntry()) { } else if (rulesetSupportsScoreEntry()) {
for (int i = 0; i < allTeams.length; i++) { for (int i = 0; i < allTeams.length; i++) {
final score = allTeams[i].score; final score = allTeams[i].score ?? 0;
controller[i].text = score.toString(); controller[i].text = score.toString();
} }
} else if (rulesetSupportsPlacement()) { } else if (rulesetSupportsDragBehaviour()) {
allTeams.sort((a, b) { allTeams.sort((a, b) {
final scoreA = a.score ?? 0; final scoreA = a.score ?? 0;
final scoreB = b.score ?? 0; final scoreB = b.score ?? 0;
@@ -237,17 +278,25 @@ class _MatchResultViewState extends State<MatchResultView> {
// Prefill fields // Prefill fields
if (widget.match.mvp.isNotEmpty) { if (widget.match.mvp.isNotEmpty) {
if (rulesetSupportsWinnerSelection()) { if (rulesetSupportsPlayerSelection()) {
if (ruleset == Ruleset.multipleWinners) {
for (int i = 0; i < allPlayers.length; i++) {
if (widget.match.scores[allPlayers[i].id]?.score == 1) {
_selectedPlayers.add(allPlayers[i]);
}
}
} else {
_selectedPlayer = allPlayers.firstWhere( _selectedPlayer = allPlayers.firstWhere(
(p) => p.id == widget.match.mvp.first.id, (p) => p.id == widget.match.mvp.first.id,
); );
}
} else if (rulesetSupportsScoreEntry()) { } else if (rulesetSupportsScoreEntry()) {
for (int i = 0; i < allPlayers.length; i++) { for (int i = 0; i < allPlayers.length; i++) {
final scoreList = widget.match.scores[allPlayers[i].id]; final scoreList = widget.match.scores[allPlayers[i].id];
final score = scoreList?.score ?? 0; final score = scoreList?.score ?? 0;
controller[i].text = score.toString(); controller[i].text = score.toString();
} }
} else if (rulesetSupportsPlacement()) { } else if (rulesetSupportsDragBehaviour()) {
allPlayers.sort((a, b) { allPlayers.sort((a, b) {
final scoreA = widget.match.scores[a.id]?.score ?? 0; final scoreA = widget.match.scores[a.id]?.score ?? 0;
final scoreB = widget.match.scores[b.id]?.score ?? 0; final scoreB = widget.match.scores[b.id]?.score ?? 0;
@@ -278,12 +327,14 @@ class _MatchResultViewState extends State<MatchResultView> {
await _handleScores(); await _handleScores();
} else if (ruleset == Ruleset.placement) { } else if (ruleset == Ruleset.placement) {
await _handlePlacement(); await _handlePlacement();
} else if (ruleset == Ruleset.multipleWinners) {
await _handleWinners();
} }
widget.onWinnerChanged?.call(); widget.onWinnerChanged?.call();
} }
/// Handles saving or removing the winner in the database. /// Handles saving or removing the (single) winner in the database.
Future<bool> _handleWinner() async { Future<bool> _handleWinner() async {
if (isTeamMatch) { if (isTeamMatch) {
if (_selectedTeam == null) { if (_selectedTeam == null) {
@@ -309,6 +360,18 @@ class _MatchResultViewState extends State<MatchResultView> {
} }
} }
/// Handles saving the (multiple) winners to the database.
Future<bool> _handleWinners() async {
if (_selectedPlayers.isEmpty) {
return await db.scoreEntryDao.removeWinner(matchId: widget.match.id);
} else {
return await db.scoreEntryDao.setWinners(
matchId: widget.match.id,
winners: allPlayers.where((p) => _selectedPlayers.contains(p)).toList(),
);
}
}
/// Handles saving or removing the loser in the database. /// Handles saving or removing the loser in the database.
Future<bool> _handleLoser() async { Future<bool> _handleLoser() async {
if (isTeamMatch) { if (isTeamMatch) {
@@ -389,20 +452,24 @@ class _MatchResultViewState extends State<MatchResultView> {
return loc.select_loser; return loc.select_loser;
case Ruleset.placement: case Ruleset.placement:
return loc.drag_to_set_placement; return loc.drag_to_set_placement;
case Ruleset.multipleWinners:
return loc.select_winners;
default: default:
return loc.enter_points; return loc.enter_points;
} }
} }
bool rulesetSupportsWinnerSelection() { bool rulesetSupportsPlayerSelection() {
return ruleset == Ruleset.singleWinner || ruleset == Ruleset.singleLoser; return ruleset == Ruleset.singleWinner ||
ruleset == Ruleset.singleLoser ||
ruleset == Ruleset.multipleWinners;
} }
bool rulesetSupportsScoreEntry() { bool rulesetSupportsScoreEntry() {
return ruleset == Ruleset.lowestScore || ruleset == Ruleset.highestScore; return ruleset == Ruleset.lowestScore || ruleset == Ruleset.highestScore;
} }
bool rulesetSupportsPlacement() { bool rulesetSupportsDragBehaviour() {
return ruleset == Ruleset.placement; return ruleset == Ruleset.placement;
} }

View File

@@ -73,7 +73,6 @@ const allDependencies = <Package>[
_io, _io,
_jni, _jni,
_jni_flutter, _jni_flutter,
_js,
_json_annotation, _json_annotation,
_json_schema, _json_schema,
_leak_tracker, _leak_tracker,
@@ -244,13 +243,13 @@ class PackageRef {
Package resolve() => allDependencies.firstWhere((d) => d.name == name); Package resolve() => allDependencies.firstWhere((d) => d.name == name);
} }
/// _fe_analyzer_shared 91.0.0 /// _fe_analyzer_shared 92.0.0
const __fe_analyzer_shared = Package( const __fe_analyzer_shared = Package(
name: '_fe_analyzer_shared', name: '_fe_analyzer_shared',
description: 'Logic that is shared between the front_end and analyzer packages.', description: 'Logic that is shared between the front_end and analyzer packages.',
repository: 'https://github.com/dart-lang/sdk/tree/main/pkg/_fe_analyzer_shared', repository: 'https://github.com/dart-lang/sdk/tree/main/pkg/_fe_analyzer_shared',
authors: [], authors: [],
version: '91.0.0', version: '92.0.0',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -285,13 +284,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// analyzer 8.4.1 /// analyzer 9.0.0
const _analyzer = Package( const _analyzer = Package(
name: 'analyzer', name: 'analyzer',
description: 'This package provides a library that performs static analysis of Dart code.', description: 'This package provides a library that performs static analysis of Dart code.',
repository: 'https://github.com/dart-lang/sdk/tree/main/pkg/analyzer', repository: 'https://github.com/dart-lang/sdk/tree/main/pkg/analyzer',
authors: [], authors: [],
version: '8.4.1', version: '9.0.0',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -699,13 +698,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// characters 1.4.0 /// characters 1.4.1
const _characters = Package( const _characters = Package(
name: 'characters', name: 'characters',
description: 'String replacement with operations that are Unicode/grapheme cluster aware.', description: 'String replacement with operations that are Unicode/grapheme cluster aware.',
repository: 'https://github.com/dart-lang/core/tree/main/pkgs/characters', repository: 'https://github.com/dart-lang/core/tree/main/pkgs/characters',
authors: [], authors: [],
version: '1.4.0', version: '1.4.1',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -2503,13 +2502,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// flutter 3.38.6 /// flutter 3.41.0
const _flutter = Package( const _flutter = Package(
name: 'flutter', name: 'flutter',
description: 'A framework for writing Flutter applications', description: 'A framework for writing Flutter applications',
homepage: 'https://flutter.dev', homepage: 'https://flutter.dev',
authors: [], authors: [],
version: '3.38.6', version: '3.41.0',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: true, isSdk: true,
@@ -3292,47 +3291,6 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// js 0.7.2
const _js = Package(
name: 'js',
description: 'Annotations to create static Dart interfaces for JavaScript APIs.',
repository: 'https://github.com/dart-lang/sdk/tree/main/pkg/js',
authors: [],
version: '0.7.2',
spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false,
isSdk: false,
dependencies: [],
devDependencies: [PackageRef('lints')],
license: '''Copyright 2012, the Dart project authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
);
/// json_annotation 4.11.0 /// json_annotation 4.11.0
const _json_annotation = Package( const _json_annotation = Package(
name: 'json_annotation', name: 'json_annotation',
@@ -3671,18 +3629,18 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// matcher 0.12.17 /// matcher 0.12.18
const _matcher = Package( const _matcher = Package(
name: 'matcher', name: 'matcher',
description: 'Support for specifying test expectations via an extensible Matcher class. Also includes a number of built-in Matcher implementations for common cases.', description: 'Support for specifying test expectations via an extensible Matcher class. Also includes a number of built-in Matcher implementations for common cases.',
repository: 'https://github.com/dart-lang/test/tree/master/pkgs/matcher', repository: 'https://github.com/dart-lang/test/tree/master/pkgs/matcher',
authors: [], authors: [],
version: '0.12.17', version: '0.12.18',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
dependencies: [PackageRef('async'), PackageRef('meta'), PackageRef('stack_trace'), PackageRef('term_glyph'), PackageRef('test_api')], dependencies: [PackageRef('async'), PackageRef('meta'), PackageRef('stack_trace'), PackageRef('term_glyph'), PackageRef('test_api')],
devDependencies: [PackageRef('fake_async'), PackageRef('lints'), PackageRef('test')], devDependencies: [PackageRef('fake_async'), PackageRef('test')],
license: '''Copyright 2014, the Dart project authors. license: '''Copyright 2014, the Dart project authors.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
@@ -3712,18 +3670,18 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// material_color_utilities 0.11.1 /// material_color_utilities 0.13.0
const _material_color_utilities = Package( const _material_color_utilities = Package(
name: 'material_color_utilities', name: 'material_color_utilities',
description: 'Algorithms and utilities that power the Material Design 3 color system, including choosing theme colors from images and creating tones of colors; all in a new color space.', description: 'Algorithms and utilities that power the Material Design 3 color system, including choosing theme colors from images and creating tones of colors; all in a new color space.',
repository: 'https://github.com/material-foundation/material-color-utilities/tree/main/dart', repository: 'https://github.com/material-foundation/material-color-utilities/tree/main/dart',
authors: [], authors: [],
version: '0.11.1', version: '0.13.0',
spdxIdentifiers: ['Apache-2.0'], spdxIdentifiers: ['Apache-2.0'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
dependencies: [PackageRef('collection')], dependencies: [PackageRef('collection')],
devDependencies: [PackageRef('matcher'), PackageRef('lints'), PackageRef('test')], devDependencies: [PackageRef('matcher'), PackageRef('test')],
license: '''Apache License license: '''Apache License
Version 2.0, January 2004 Version 2.0, January 2004
http://www.apache.org/licenses/ http://www.apache.org/licenses/
@@ -6288,6 +6246,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
icu
json json
@@ -6913,6 +6872,7 @@ skia
limitations under the License. limitations under the License.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
angle angle
benchmark
boringssl boringssl
cpu_features cpu_features
flatbuffers flatbuffers
@@ -10342,6 +10302,7 @@ prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined the License, but only in their entirety and only with respect to the Combined
Software. Software.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
icu
include include
json json
@@ -13912,34 +13873,6 @@ License & terms of use: http://www.unicode.org/copyright.html
All Rights Reserved. All Rights Reserved.
--------------------------------------------------------------------------------
icu
Copyright (C) 2016 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html
Copyright (C) 2002-2010, International Business Machines
Corporation and others. All Rights Reserved.
--------------------------------------------------------------------------------
icu
Copyright (C) 2016 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html
Copyright (c) 2001-2003 International Business Machines
Corporation and others. All Rights Reserved.
--------------------------------------------------------------------------------
icu
Copyright (C) 2016 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html
Copyright (c) 2001-2010 International Business Machines
Corporation and others. All Rights Reserved.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
icu icu
@@ -13954,14 +13887,6 @@ icu
Copyright (C) 2016 and later: Unicode, Inc. and others. Copyright (C) 2016 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html License & terms of use: http://www.unicode.org/copyright.html
Copyright (c) 2002-2010, International Business Machines Corporation and others. All Rights Reserved.
--------------------------------------------------------------------------------
icu
Copyright (C) 2016 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html
Copyright (c) 2002-2015, International Business Machines Corporation and Copyright (c) 2002-2015, International Business Machines Corporation and
others. All Rights Reserved. others. All Rights Reserved.
@@ -13976,22 +13901,6 @@ Copyright (c) 2002-2016 International Business Machines Corporation and
others. All Rights Reserved. others. All Rights Reserved.
--------------------------------------------------------------------------------
icu
Copyright (C) 2016 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html
Copyright (c) 2003-2005, International Business Machines Corporation and others. All Rights Reserved.
--------------------------------------------------------------------------------
icu
Copyright (C) 2016 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html
Copyright (c) 2003-2010, International Business Machines Corporation and others. All Rights Reserved.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
icu icu
@@ -14166,17 +14075,6 @@ License & terms of use: http://www.unicode.org/copyright.html
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
icu icu
Copyright (C) 2016 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html
***************************************************************************
*
* Copyright (C) 2009 International Business Machines
* Corporation and others. All Rights Reserved.
*
***************************************************************************
--------------------------------------------------------------------------------
icu
Copyright (C) 2016 and later: Unicode, Inc. and others. Copyright (C) 2016 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html License & terms of use: http://www.unicode.org/copyright.html
***************************************************************************** *****************************************************************************
@@ -14187,19 +14085,6 @@ License & terms of use: http://www.unicode.org/copyright.html
***************************************************************************** *****************************************************************************
--------------------------------------------------------------------------------
icu
Copyright (C) 2016 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html
*******************************************************************************
*
* Copyright (C) 1995-2001, International Business Machines
* Corporation and others. All Rights Reserved.
*
*******************************************************************************
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
icu icu
@@ -14278,6 +14163,15 @@ License & terms of use: http://www.unicode.org/copyright.html
******************************************************************************* *******************************************************************************
--------------------------------------------------------------------------------
icu
Copyright (C) 2016 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html
---------------------------------------------------------
Copyright (C) 2013, International Business Machines
Corporation and others. All Rights Reserved.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
icu icu
@@ -15561,6 +15455,22 @@ angle
Copyright (c) 2008-2021 The Khronos Group Inc. Copyright (c) 2008-2021 The Khronos Group Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--------------------------------------------------------------------------------
angle
Copyright (c) 2008-2023 The Khronos Group Inc.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
@@ -18045,6 +17955,28 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
volk
Copyright (c) 2018-2024 Arseny Kapoulkine
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
--------------------------------------------------------------------------------
vulkan-validation-layers vulkan-validation-layers
Copyright (c) 2018-2024 The Khronos Group Inc. Copyright (c) 2018-2024 The Khronos Group Inc.
@@ -18569,7 +18501,6 @@ Copyright (c) 2020 The ANGLE Project Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. found in the LICENSE file.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
angle
spirv-tools spirv-tools
Copyright (c) 2020 The Khronos Group Inc. Copyright (c) 2020 The Khronos Group Inc.
@@ -19380,6 +19311,22 @@ spirv-tools
Copyright (c) 2023 LunarG Inc. Copyright (c) 2023 LunarG Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--------------------------------------------------------------------------------
angle
Copyright (c) 2023 The Khronos Group Inc.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
@@ -19417,6 +19364,7 @@ for details. All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
dart dart
perfetto
Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
for details. All rights reserved. Use of this source code is governed by a for details. All rights reserved. Use of this source code is governed by a
@@ -19946,6 +19894,12 @@ limitations under the License.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
dart dart
Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
for details. All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
--------------------------------------------------------------------------------
perfetto
Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
for details. All rights reserved. Use of this source code is governed by a for details. All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file. BSD-style license that can be found in the LICENSE file.
@@ -20809,11 +20763,6 @@ Copyright 2014-2022 The Khronos Group Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
swiftshader swiftshader
Copyright 2014-2023 The Khronos Group Inc.
SPDX-License-Identifier: Apache-2.0
--------------------------------------------------------------------------------
vulkan vulkan
vulkan-headers vulkan-headers
@@ -20851,6 +20800,7 @@ tree. An additional intellectual property rights grant can be found
in the file PATENTS. All contributing project authors may in the file PATENTS. All contributing project authors may
be found in the AUTHORS file in the root of the source tree. be found in the AUTHORS file in the root of the source tree.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
benchmark
flatbuffers flatbuffers
Copyright 2015 Google Inc. All rights reserved. Copyright 2015 Google Inc. All rights reserved.
@@ -20936,12 +20886,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
swiftshader swiftshader
Copyright 2015-2023 The Khronos Group Inc.
SPDX-License-Identifier: Apache-2.0
--------------------------------------------------------------------------------
swiftshader
vulkan vulkan
vulkan-headers vulkan-headers
@@ -20951,6 +20895,7 @@ Copyright 2015-2023 LunarG, Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
swiftshader
vulkan vulkan
vulkan-headers vulkan-headers
@@ -20978,18 +20923,6 @@ skia
Copyright 2016 Google Inc. Copyright 2016 Google Inc.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
--------------------------------------------------------------------------------
skia
Copyright 2016 Google Inc.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
Copyright 2014 Google Inc.
Use of this source code is governed by a BSD-style license that can be Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. found in the LICENSE file.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@@ -21007,6 +20940,39 @@ flatbuffers
Copyright 2016 Google Inc. All rights reserved. Copyright 2016 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--------------------------------------------------------------------------------
benchmark
Copyright 2016 Ismael Jimenez Martinez. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--------------------------------------------------------------------------------
benchmark
Copyright 2016 Ismael Jimenez Martinez. All rights reserved.
Copyright 2017 Roman Lebedev. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
@@ -21437,6 +21403,7 @@ tree. An additional intellectual property rights grant can be found
in the file PATENTS. All contributing project authors may in the file PATENTS. All contributing project authors may
be found in the AUTHORS file in the root of the source tree. be found in the AUTHORS file in the root of the source tree.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
benchmark
flatbuffers flatbuffers
Copyright 2018 Google Inc. All rights reserved. Copyright 2018 Google Inc. All rights reserved.
@@ -22213,7 +22180,6 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
angle
swiftshader swiftshader
Copyright 2020 The SwiftShader Authors. All Rights Reserved. Copyright 2020 The SwiftShader Authors. All Rights Reserved.
@@ -22263,6 +22229,7 @@ tree. An additional intellectual property rights grant can be found
in the file PATENTS. All contributing project authors may in the file PATENTS. All contributing project authors may
be found in the AUTHORS file in the root of the source tree. be found in the AUTHORS file in the root of the source tree.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
benchmark
flatbuffers flatbuffers
Copyright 2021 Google Inc. All rights reserved. Copyright 2021 Google Inc. All rights reserved.
@@ -22695,6 +22662,22 @@ Copyright 2023 The ANGLE Project Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. found in the LICENSE file.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
angle
Copyright 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--------------------------------------------------------------------------------
skia skia
Copyright 2023 The Android Open Source Project Copyright 2023 The Android Open Source Project
@@ -22772,6 +22755,7 @@ Copyright 2023-2025 The Khronos Group Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
swiftshader
vulkan-utility-libraries vulkan-utility-libraries
Copyright 2023-2025 The Khronos Group Inc. Copyright 2023-2025 The Khronos Group Inc.
@@ -22789,6 +22773,12 @@ skia
Copyright 2024 Google Inc. Copyright 2024 Google Inc.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
--------------------------------------------------------------------------------
angle
Copyright 2024 Google Inc. All rights reserved.
Use of this source code is governed by a BSD-style license that can be Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. found in the LICENSE file.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@@ -22824,6 +22814,12 @@ found in the LICENSE file.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
skia skia
Copyright 2024 Google LLC.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
--------------------------------------------------------------------------------
skia
Copyright 2024 Google LLC. Copyright 2024 Google LLC.
Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@@ -22834,6 +22830,17 @@ Copyright 2024 Google, LLC
Use of this source code is governed by a BSD-style license that can be Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. found in the LICENSE file.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
angle
Copyright 2024 The ANGLE Project Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
--------------------------------------------------------------------------------
angle
Copyright 2024 The ANGLE Project Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
--------------------------------------------------------------------------------
skia skia
Copyright 2024 The Android Open Source Project Copyright 2024 The Android Open Source Project
@@ -22915,6 +22922,19 @@ skia
Copyright 2025 Google LLC. Copyright 2025 Google LLC.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
--------------------------------------------------------------------------------
skia
Copyright 2025 Google, LLC
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
--------------------------------------------------------------------------------
angle
Copyright 2025 The ANGLE Project Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. found in the LICENSE file.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@@ -22979,6 +22999,12 @@ Copyright The ANGLE Project Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. found in the LICENSE file.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
angle
Copyright {copyright_year} The ANGLE Project Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
--------------------------------------------------------------------------------
harfbuzz harfbuzz
Copyright © 1998-2004 David Turner and Werner Lemberg Copyright © 1998-2004 David Turner and Werner Lemberg
@@ -26723,6 +26749,48 @@ FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
flutter
License for the Ahem font embedded below is from:
https://www.w3.org/Style/CSS/Test/Fonts/Ahem/COPYING
The Ahem font in this directory belongs to the public domain. In
jurisdictions that do not recognize public domain ownership of these
files, the following Creative Commons Zero declaration applies:
<http://labs.creativecommons.org/licenses/zero-waive/1.0/us/legalcode>
which is quoted below:
The person who has associated a work with this document (the "Work")
affirms that he or she (the "Affirmer") is the/an author or owner of
the Work. The Work may be any work of authorship, including a
database.
The Affirmer hereby fully, permanently and irrevocably waives and
relinquishes all of her or his copyright and related or neighboring
legal rights in the Work available under any federal or state law,
treaty or contract, including but not limited to moral rights,
publicity and privacy rights, rights protecting against unfair
competition and any rights protecting the extraction, dissemination
and reuse of data, whether such rights are present or future, vested
or contingent (the "Waiver"). The Affirmer makes the Waiver for the
benefit of the public at large and to the detriment of the Affirmer's
heirs or successors.
The Affirmer understands and intends that the Waiver has the effect
of eliminating and entirely removing from the Affirmer's control all
the copyright and related or neighboring legal rights previously held
by the Affirmer in the Work, to that extent making the Work freely
available to the public for any and all uses and purposes without
restriction of any kind, including commercial use and uses in media
and formats or by methods that have not yet been invented or
conceived. Should the Waiver for any reason be judged legally
ineffective in any jurisdiction, the Affirmer hereby grants a free,
full, permanent, irrevocable, nonexclusive and worldwide license for
all her or his copyright and related or neighboring legal rights in
the Work.
--------------------------------------------------------------------------------
fallback_root_certificates fallback_root_certificates
Mozilla Public License Version 2.0 Mozilla Public License Version 2.0
@@ -27105,8 +27173,8 @@ libpng
PNG Reference Library License version 2 PNG Reference Library License version 2
--------------------------------------- ---------------------------------------
* Copyright (c) 1995-2024 The PNG Reference Library Authors. * Copyright (c) 1995-2025 The PNG Reference Library Authors.
* Copyright (c) 2018-2024 Cosmin Truta. * Copyright (c) 2018-2025 Cosmin Truta.
* Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson. * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
* Copyright (c) 1996-1997 Andreas Dilger. * Copyright (c) 1996-1997 Andreas Dilger.
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
@@ -27240,8 +27308,8 @@ libpng
PNG Reference Library License version 2 PNG Reference Library License version 2
--------------------------------------- ---------------------------------------
* Copyright (c) 1995-2024 The PNG Reference Library Authors. * Copyright (c) 1995-2025 The PNG Reference Library Authors.
* Copyright (c) 2018-2024 Cosmin Truta. * Copyright (c) 2018-2025 Cosmin Truta.
* Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson. * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
* Copyright (c) 1996-1997 Andreas Dilger. * Copyright (c) 1996-1997 Andreas Dilger.
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
@@ -28708,7 +28776,7 @@ UNICODE LICENSE V3
COPYRIGHT AND PERMISSION NOTICE COPYRIGHT AND PERMISSION NOTICE
Copyright © 2016-2023 Unicode, Inc. Copyright © 2016-2025 Unicode, Inc.
NOTICE TO USER: Carefully read the following legal agreement. BY NOTICE TO USER: Carefully read the following legal agreement. BY
DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
@@ -28744,6 +28812,8 @@ not be used in advertising or otherwise to promote the sale, use or other
dealings in these Data Files or Software without prior written dealings in these Data Files or Software without prior written
authorization of the copyright holder. authorization of the copyright holder.
SPDX-License-Identifier: Unicode-3.0
---------------------------------------------------------------------- ----------------------------------------------------------------------
Third-Party Software Licenses Third-Party Software Licenses
@@ -29137,6 +29207,34 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------- ----------------------------------------------------------------------
JSON parsing library (nlohmann/json)
File: vendor/json/upstream/single_include/nlohmann/json.hpp (only for ICU4C)
MIT License
Copyright (c) 2013-2022 Niels Lohmann
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
----------------------------------------------------------------------
File: install-sh (only for ICU4C) File: install-sh (only for ICU4C)
@@ -32422,17 +32520,6 @@ Copyright (C) 2003-2016, International Business Machines
--------------------------------------------------------------------------------
icu
© 2016 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html
Copyright (C) 2008-2013, International Business Machines Corporation and
others. All Rights Reserved.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
icu icu
@@ -32472,16 +32559,6 @@ icu
License & terms of use: http://www.unicode.org/copyright.html License & terms of use: http://www.unicode.org/copyright.html
Copyright (c) 1999-2007, International Business Machines Corporation and
others. All Rights Reserved.
--------------------------------------------------------------------------------
icu
© 2016 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html
Copyright (c) 1999-2010, International Business Machines Corporation and Copyright (c) 1999-2010, International Business Machines Corporation and
others. All Rights Reserved. others. All Rights Reserved.
@@ -35972,15 +36049,6 @@ others. All Rights Reserved.
--------------------------------------------------------------------------------
icu
© 2016 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html
Copyright (C) 2008-2014, International Business Machines Corporation and
others. All Rights Reserved.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
icu icu
@@ -36220,14 +36288,6 @@ others. All Rights Reserved.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
icu icu
© 2016 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html
Copyright (c) 2008-2014, International Business Machines Corporation and
others. All Rights Reserved.
--------------------------------------------------------------------------------
icu
© 2016 and later: Unicode, Inc. and others. © 2016 and later: Unicode, Inc. and others.
License & terms of use: http://www.unicode.org/copyright.html License & terms of use: http://www.unicode.org/copyright.html
Copyright (c) IBM Corporation, 2000-2010. All rights reserved. Copyright (c) IBM Corporation, 2000-2010. All rights reserved.
@@ -36755,17 +36815,17 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// test 1.26.3 /// test 1.28.0
const _test = Package( const _test = Package(
name: 'test', name: 'test',
description: 'A full featured library for writing and running Dart tests across platforms.', description: 'A full featured library for writing and running Dart tests across platforms.',
repository: 'https://github.com/dart-lang/test/tree/master/pkgs/test', repository: 'https://github.com/dart-lang/test/tree/master/pkgs/test',
authors: [], authors: [],
version: '1.26.3', version: '1.28.0',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
dependencies: [PackageRef('analyzer'), PackageRef('async'), PackageRef('boolean_selector'), PackageRef('collection'), PackageRef('coverage'), PackageRef('http_multi_server'), PackageRef('io'), PackageRef('js'), PackageRef('matcher'), PackageRef('node_preamble'), PackageRef('package_config'), PackageRef('path'), PackageRef('pool'), PackageRef('shelf'), PackageRef('shelf_packages_handler'), PackageRef('shelf_static'), PackageRef('shelf_web_socket'), PackageRef('source_span'), PackageRef('stack_trace'), PackageRef('stream_channel'), PackageRef('test_api'), PackageRef('test_core'), PackageRef('typed_data'), PackageRef('web_socket_channel'), PackageRef('webkit_inspection_protocol'), PackageRef('yaml')], dependencies: [PackageRef('analyzer'), PackageRef('async'), PackageRef('boolean_selector'), PackageRef('collection'), PackageRef('coverage'), PackageRef('http_multi_server'), PackageRef('io'), PackageRef('matcher'), PackageRef('node_preamble'), PackageRef('package_config'), PackageRef('path'), PackageRef('pool'), PackageRef('shelf'), PackageRef('shelf_packages_handler'), PackageRef('shelf_static'), PackageRef('shelf_web_socket'), PackageRef('source_span'), PackageRef('stack_trace'), PackageRef('stream_channel'), PackageRef('test_api'), PackageRef('test_core'), PackageRef('typed_data'), PackageRef('web_socket_channel'), PackageRef('webkit_inspection_protocol'), PackageRef('yaml')],
devDependencies: [PackageRef('fake_async'), PackageRef('glob')], devDependencies: [PackageRef('fake_async'), PackageRef('glob')],
license: '''Copyright 2014, the Dart project authors. license: '''Copyright 2014, the Dart project authors.
@@ -36796,13 +36856,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// test_api 0.7.7 /// test_api 0.7.8
const _test_api = Package( const _test_api = Package(
name: 'test_api', name: 'test_api',
description: 'The user facing API for structuring Dart tests and checking expectations.', description: 'The user facing API for structuring Dart tests and checking expectations.',
repository: 'https://github.com/dart-lang/test/tree/master/pkgs/test_api', repository: 'https://github.com/dart-lang/test/tree/master/pkgs/test_api',
authors: [], authors: [],
version: '0.7.7', version: '0.7.8',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -36837,13 +36897,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// test_core 0.6.12 /// test_core 0.6.14
const _test_core = Package( const _test_core = Package(
name: 'test_core', name: 'test_core',
description: 'A basic library for writing tests and running them on the VM.', description: 'A basic library for writing tests and running them on the VM.',
repository: 'https://github.com/dart-lang/test/tree/master/pkgs/test_core', repository: 'https://github.com/dart-lang/test/tree/master/pkgs/test_core',
authors: [], authors: [],
version: '0.6.12', version: '0.6.14',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -37751,12 +37811,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.''', SOFTWARE.''',
); );
/// tallee 0.0.30+264 /// tallee 0.0.33+267
const _tallee = Package( const _tallee = Package(
name: 'tallee', name: 'tallee',
description: 'Tracking App for Card Games', description: 'Tracking App for Card Games',
authors: [], authors: [],
version: '0.0.30+264', version: '0.0.33+267',
spdxIdentifiers: ['LGPL-3.0'], spdxIdentifiers: ['LGPL-3.0'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,

View File

@@ -2,6 +2,7 @@ import 'dart:io';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
@@ -9,6 +10,7 @@ import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/core/enums.dart'; import 'package:tallee/core/enums.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/views/main_menu/settings_view/licenses_view.dart'; import 'package:tallee/presentation/views/main_menu/settings_view/licenses_view.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart'; import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart';
import 'package:tallee/presentation/widgets/dialog/custom_dialog_action.dart'; import 'package:tallee/presentation/widgets/dialog/custom_dialog_action.dart';
import 'package:tallee/presentation/widgets/tiles/settings_list_tile.dart'; import 'package:tallee/presentation/widgets/tiles/settings_list_tile.dart';
@@ -197,19 +199,23 @@ class _SettingsViewState extends State<SettingsView> {
padding: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.only(bottom: 12),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
spacing: 40, spacing: 10,
children: [ children: [
GestureDetector( HapticIconButton(
child: const Icon(Icons.language), color: CustomTheme.textColor,
onTap: () => { icon: const Icon(Icons.language),
onPressed: () async => {
await HapticFeedback.lightImpact(),
launchUrl( launchUrl(
Uri.parse('https://liquid-dev.de'), Uri.parse('https://liquid-dev.de'),
), ),
}, },
), ),
GestureDetector( HapticIconButton(
child: const FaIcon(FontAwesomeIcons.github), color: CustomTheme.textColor,
onTap: () => { icon: const FaIcon(FontAwesomeIcons.github),
onPressed: () async => {
await HapticFeedback.lightImpact(),
launchUrl( launchUrl(
Uri.parse( Uri.parse(
'https://github.com/liquiddevelopmentde', 'https://github.com/liquiddevelopmentde',
@@ -217,15 +223,19 @@ class _SettingsViewState extends State<SettingsView> {
), ),
}, },
), ),
GestureDetector( HapticIconButton(
child: Icon( color: CustomTheme.textColor,
icon: Icon(
Platform.isIOS Platform.isIOS
? CupertinoIcons.mail_solid ? CupertinoIcons.mail_solid
: Icons.email, : Icons.email,
), ),
onTap: () => launchUrl( onPressed: () async => {
await HapticFeedback.lightImpact(),
launchUrl(
Uri.parse('mailto:hi@liquid-dev.de'), Uri.parse('mailto:hi@liquid-dev.de'),
), ),
},
), ),
], ],
), ),
@@ -266,23 +276,44 @@ class _SettingsViewState extends State<SettingsView> {
void showImportSnackBar({ void showImportSnackBar({
required BuildContext context, required BuildContext context,
required ImportResult result, required ImportResult result,
}) { }) async {
final loc = AppLocalizations.of(context); final loc = AppLocalizations.of(context);
switch (result) { switch (result) {
case ImportResult.success: case ImportResult.success:
showSnackbar(context: context, message: loc.data_successfully_imported); await HapticFeedback.successNotification();
if (context.mounted) {
showSnackbar(
context: context,
message: loc.data_successfully_imported,
);
}
case ImportResult.invalidSchema: case ImportResult.invalidSchema:
await HapticFeedback.errorNotification();
if (context.mounted) {
showSnackbar(context: context, message: loc.invalid_schema); showSnackbar(context: context, message: loc.invalid_schema);
}
case ImportResult.fileReadError: case ImportResult.fileReadError:
await HapticFeedback.errorNotification();
if (context.mounted) {
showSnackbar(context: context, message: loc.error_reading_file); showSnackbar(context: context, message: loc.error_reading_file);
}
case ImportResult.canceled: case ImportResult.canceled:
await HapticFeedback.errorNotification();
if (context.mounted) {
showSnackbar(context: context, message: loc.import_canceled); showSnackbar(context: context, message: loc.import_canceled);
}
case ImportResult.formatException: case ImportResult.formatException:
await HapticFeedback.errorNotification();
if (context.mounted) {
showSnackbar(context: context, message: loc.format_exception); showSnackbar(context: context, message: loc.format_exception);
}
case ImportResult.unknownException: case ImportResult.unknownException:
await HapticFeedback.errorNotification();
if (context.mounted) {
showSnackbar(context: context, message: loc.unknown_exception); showSnackbar(context: context, message: loc.unknown_exception);
} }
} }
}
/// Displays a snackbar based on the export result. /// Displays a snackbar based on the export result.
/// ///
@@ -291,17 +322,29 @@ class _SettingsViewState extends State<SettingsView> {
void showExportSnackBar({ void showExportSnackBar({
required BuildContext context, required BuildContext context,
required ExportResult result, required ExportResult result,
}) { }) async {
final loc = AppLocalizations.of(context); final loc = AppLocalizations.of(context);
switch (result) { switch (result) {
case ExportResult.success: case ExportResult.success:
showSnackbar(context: context, message: loc.data_successfully_exported); await HapticFeedback.successNotification();
if (context.mounted) {
showSnackbar(
context: context,
message: loc.data_successfully_exported,
);
}
case ExportResult.canceled: case ExportResult.canceled:
await HapticFeedback.errorNotification();
if (context.mounted) {
showSnackbar(context: context, message: loc.export_canceled); showSnackbar(context: context, message: loc.export_canceled);
}
case ExportResult.unknownException: case ExportResult.unknownException:
await HapticFeedback.errorNotification();
if (context.mounted) {
showSnackbar(context: context, message: loc.unknown_exception); showSnackbar(context: context, message: loc.unknown_exception);
} }
} }
}
/// Displays a snackbar with the given message and optional action. /// Displays a snackbar with the given message and optional action.
/// ///

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/core/enums.dart'; import 'package:tallee/core/enums.dart';
@@ -48,7 +49,12 @@ class CustomWidthButton extends StatelessWidget {
)!; )!;
return ElevatedButton( return ElevatedButton(
onPressed: onPressed, onPressed: onPressed == null
? null
: () async {
await HapticFeedback.selectionClick();
onPressed!.call();
},
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
foregroundColor: textcolor, foregroundColor: textcolor,
disabledForegroundColor: disabledTextColor, disabledForegroundColor: disabledTextColor,
@@ -78,7 +84,12 @@ class CustomWidthButton extends StatelessWidget {
: Color.lerp(CustomTheme.primaryColor, Colors.black, 0.5)!; : Color.lerp(CustomTheme.primaryColor, Colors.black, 0.5)!;
return OutlinedButton( return OutlinedButton(
onPressed: onPressed, onPressed: onPressed == null
? null
: () async {
await HapticFeedback.selectionClick();
onPressed!.call();
},
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
foregroundColor: textcolor, foregroundColor: textcolor,
disabledForegroundColor: disabledTextColor, disabledForegroundColor: disabledTextColor,
@@ -110,7 +121,12 @@ class CustomWidthButton extends StatelessWidget {
disabledBackgroundColor = Colors.transparent; disabledBackgroundColor = Colors.transparent;
return TextButton( return TextButton(
onPressed: onPressed, onPressed: onPressed == null
? null
: () async {
await HapticFeedback.selectionClick();
onPressed!.call();
},
style: TextButton.styleFrom( style: TextButton.styleFrom(
foregroundColor: textcolor, foregroundColor: textcolor,
disabledForegroundColor: disabledTextColor, disabledForegroundColor: disabledTextColor,

View File

@@ -0,0 +1,23 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
class HapticBackButton extends StatelessWidget {
const HapticBackButton({super.key});
@override
Widget build(BuildContext context) {
final iconData = switch (defaultTargetPlatform) {
TargetPlatform.iOS ||
TargetPlatform.macOS => Icons.arrow_back_ios_new_rounded,
_ => Icons.arrow_back_rounded,
};
return HapticIconButton(
icon: Icon(iconData),
onPressed: () async {
Navigator.of(context).maybePop();
},
);
}
}

View File

@@ -0,0 +1,23 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
class HapticCloseButton extends StatelessWidget {
const HapticCloseButton({super.key});
@override
Widget build(BuildContext context) {
final iconData = switch (defaultTargetPlatform) {
TargetPlatform.iOS || TargetPlatform.macOS => CupertinoIcons.xmark,
_ => Icons.close_rounded,
};
return HapticIconButton(
icon: Icon(iconData),
onPressed: () async {
Navigator.of(context).maybePop();
},
);
}
}

View File

@@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class HapticIconButton extends StatelessWidget {
const HapticIconButton({
super.key,
required this.icon,
required this.onPressed,
this.iconSize,
this.color,
this.padding,
this.alignment,
this.constraints,
this.style,
this.isSelected,
this.selectedIcon,
});
final Widget icon;
final VoidCallback? onPressed;
final double? iconSize;
final Color? color;
final EdgeInsetsGeometry? padding;
final AlignmentGeometry? alignment;
final BoxConstraints? constraints;
final ButtonStyle? style;
final bool? isSelected;
final Widget? selectedIcon;
@override
Widget build(BuildContext context) {
return IconButton(
iconSize: iconSize,
highlightColor: Colors.transparent, //disable splash animation
color: color,
padding: padding,
alignment: alignment ?? Alignment.center,
constraints: constraints,
style: style,
isSelected: isSelected,
selectedIcon: selectedIcon,
icon: icon,
onPressed: onPressed == null
? null
: () async {
await HapticFeedback.selectionClick();
onPressed!.call();
},
);
}
}

View File

@@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class MainMenuButton extends StatefulWidget { class MainMenuButton extends StatefulWidget {
/// A button for the main menu with an optional icon and a press animation. /// A button for the main menu with an optional icon and a press animation.
@@ -84,14 +85,21 @@ class _MainMenuButtonState extends State<MainMenuButton>
} else { } else {
_animationController.forward(); _animationController.forward();
if (widget.onLongPressed != null) { if (widget.onLongPressed != null) {
_longPressTimer = Timer(const Duration(milliseconds: 400), () { _longPressTimer = Timer(
const Duration(milliseconds: 400),
() async {
_isLongPressing = true; _isLongPressing = true;
widget.onLongPressed?.call(); widget.onLongPressed?.call();
await HapticFeedback.heavyImpact();
_repeatTimer = Timer.periodic( _repeatTimer = Timer.periodic(
const Duration(milliseconds: 250), const Duration(milliseconds: 250),
(_) => widget.onLongPressed?.call(), (_) async {
widget.onLongPressed?.call();
await HapticFeedback.heavyImpact();
},
);
},
); );
});
} }
} }
}, },
@@ -101,6 +109,7 @@ class _MainMenuButtonState extends State<MainMenuButton>
} else { } else {
_cancelTimers(); _cancelTimers();
if (mounted && !_isLongPressing) { if (mounted && !_isLongPressing) {
await HapticFeedback.selectionClick();
widget.onPressed?.call(); widget.onPressed?.call();
} }
_isLongPressing = false; _isLongPressing = false;

View File

@@ -1,4 +1,5 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:tallee/core/enums.dart'; import 'package:tallee/core/enums.dart';
import 'package:tallee/presentation/widgets/buttons/animated_dialog_button.dart'; import 'package:tallee/presentation/widgets/buttons/animated_dialog_button.dart';
@@ -26,7 +27,10 @@ class CustomDialogAction extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AnimatedDialogButton( return AnimatedDialogButton(
onPressed: onPressed, onPressed: () async {
await HapticFeedback.selectionClick();
onPressed.call();
},
buttonText: text, buttonText: text,
buttonType: buttonType, buttonType: buttonType,
isDescructive: isDestructive, isDescructive: isDestructive,

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tallee/core/common.dart'; import 'package:tallee/core/common.dart';
import 'package:tallee/core/constants.dart'; import 'package:tallee/core/constants.dart';
@@ -143,7 +144,8 @@ class _PlayerSelectionState extends State<PlayerSelection> {
text: player.name, text: player.name,
suffixText: getNameCountText(player), suffixText: getNameCountText(player),
onIconTap: () { onIconTap: () {
setState(() { setState(() async {
await HapticFeedback.selectionClick();
// Removes the player from the selection and notifies the parent. // Removes the player from the selection and notifies the parent.
selectedPlayers.remove(player); selectedPlayers.remove(player);
widget.onChanged([...selectedPlayers]); widget.onChanged([...selectedPlayers]);
@@ -197,7 +199,8 @@ class _PlayerSelectionState extends State<PlayerSelection> {
text: suggestedPlayers[index].name, text: suggestedPlayers[index].name,
suffixText: getNameCountText(suggestedPlayers[index]), suffixText: getNameCountText(suggestedPlayers[index]),
icon: Icons.add, icon: Icons.add,
onPressed: () { onPressed: () async {
await HapticFeedback.selectionClick();
setState(() { setState(() {
// If the player is not already selected // If the player is not already selected
if (!selectedPlayers.contains( if (!selectedPlayers.contains(
@@ -294,8 +297,10 @@ class _PlayerSelectionState extends State<PlayerSelection> {
if (success) { if (success) {
_handleSuccessfulPlayerCreation(createdPlayer); _handleSuccessfulPlayerCreation(createdPlayer);
await HapticFeedback.successNotification();
showSnackBarMessage(loc.successfully_added_player(playerName)); showSnackBarMessage(loc.successfully_added_player(playerName));
} else { } else {
await HapticFeedback.errorNotification();
showSnackBarMessage(loc.could_not_add_player(playerName)); showSnackBarMessage(loc.could_not_add_player(playerName));
} }
} }

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
class ChooseTile extends StatefulWidget { class ChooseTile extends StatefulWidget {
@@ -30,7 +31,14 @@ class _ChooseTileState extends State<ChooseTile> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: widget.onPressed, onTap: widget.onPressed != null
? () async {
await HapticFeedback.selectionClick();
if (widget.onPressed != null) {
widget.onPressed!.call();
}
}
: null,
child: Container( child: Container(
margin: CustomTheme.tileMargin, margin: CustomTheme.tileMargin,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tallee/core/common.dart'; import 'package:tallee/core/common.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/core/enums.dart'; import 'package:tallee/core/enums.dart';
@@ -53,8 +54,18 @@ class GameTile extends StatelessWidget {
final gameColor = badgeColor ?? getColorFromGameColor(GameColor.orange); final gameColor = badgeColor ?? getColorFromGameColor(GameColor.orange);
return GestureDetector( return GestureDetector(
onTap: onTap, onTap: () async {
onLongPress: onLongPress, await HapticFeedback.selectionClick();
if (onTap != null) {
onTap!.call();
}
},
onLongPress: () async {
await HapticFeedback.heavyImpact();
if (onLongPress != null) {
onLongPress!.call();
}
},
child: AnimatedContainer( child: AnimatedContainer(
margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 10), margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
decoration: !isHighlighted decoration: !isHighlighted

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tallee/core/common.dart'; import 'package:tallee/core/common.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/data/models/group.dart'; import 'package:tallee/data/models/group.dart';
@@ -33,7 +34,12 @@ class _GroupTileState extends State<GroupTile> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: widget.onTap, onTap: () async {
await HapticFeedback.selectionClick();
if (widget.onTap != null) {
widget.onTap!.call();
}
},
child: AnimatedContainer( child: AnimatedContainer(
margin: CustomTheme.standardMargin, margin: CustomTheme.standardMargin,
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tallee/core/custom_theme.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/license_detail_view.dart';
import 'package:tallee/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart'; import 'package:tallee/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart';
@@ -15,8 +16,10 @@ class LicenseTile extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () async {
Navigator.of(context).push( final navigator = Navigator.of(context);
await HapticFeedback.selectionClick();
navigator.push(
MaterialPageRoute( MaterialPageRoute(
builder: (context) => LicenseDetailView(package: package), builder: (context) => LicenseDetailView(package: package),
), ),

View File

@@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tallee/core/custom_theme.dart';
class CustomCheckboxListTile extends StatelessWidget {
const CustomCheckboxListTile({
super.key,
required this.text,
required this.value,
required this.onChanged,
});
final String text;
final bool value;
final ValueChanged<bool> onChanged;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () async {
await HapticFeedback.selectionClick();
onChanged(!value);
},
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
padding: const EdgeInsets.symmetric(horizontal: 2),
decoration: BoxDecoration(
color: CustomTheme.boxColor,
border: Border.all(color: CustomTheme.boxBorderColor),
borderRadius: CustomTheme.standardBorderRadiusAll,
),
child: Row(
children: [
Checkbox(
value: value,
onChanged: (bool? v) async {
await HapticFeedback.selectionClick();
if (v == null) return;
onChanged(v);
},
),
Expanded(
child: Text(
text,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
],
),
),
);
}
}

View File

@@ -1,6 +1,7 @@
import 'dart:core' hide Match; import 'dart:core' hide Match;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:tallee/core/common.dart'; import 'package:tallee/core/common.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
@@ -52,7 +53,10 @@ class _MatchTileState extends State<MatchTile> {
final loc = AppLocalizations.of(context); final loc = AppLocalizations.of(context);
return GestureDetector( return GestureDetector(
onTap: widget.onTap, onTap: () async {
await HapticFeedback.selectionClick();
widget.onTap.call();
},
child: Container( child: Container(
margin: EdgeInsets.zero, margin: EdgeInsets.zero,
width: widget.width, width: widget.width,
@@ -332,6 +336,9 @@ class _MatchTileState extends State<MatchTile> {
return '${loc.winner}: $mvpNames (${getPointLabel(loc, mvpScore)})'; return '${loc.winner}: $mvpNames (${getPointLabel(loc, mvpScore)})';
} else if (ruleset == Ruleset.placement) { } else if (ruleset == Ruleset.placement) {
return '${loc.winner}: ${widget.match.mvp.first.name}'; return '${loc.winner}: ${widget.match.mvp.first.name}';
} else if (ruleset == Ruleset.multipleWinners) {
final mvpNames = widget.match.mvp.map((player) => player.name).join(', ');
return '${loc.winners}: $mvpNames';
} }
return '${loc.winner}: n.A.'; return '${loc.winner}: n.A.';
} }

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/presentation/widgets/colored_icon_container.dart'; import 'package:tallee/presentation/widgets/colored_icon_container.dart';
@@ -36,7 +37,10 @@ class SettingsListTile extends StatelessWidget {
child: SizedBox( child: SizedBox(
width: MediaQuery.of(context).size.width * 0.95, width: MediaQuery.of(context).size.width * 0.95,
child: GestureDetector( child: GestureDetector(
onTap: onPressed ?? () {}, onTap: () async {
await HapticFeedback.selectionClick();
onPressed?.call();
},
child: Container( child: Container(
margin: EdgeInsets.zero, margin: EdgeInsets.zero,
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),

View File

@@ -1,7 +1,7 @@
name: tallee name: tallee
description: "Tracking App for Card Games" description: "Tracking App for Card Games"
publish_to: 'none' publish_to: 'none'
version: 0.0.30+304 version: 0.0.30+316
environment: environment:
sdk: ^3.8.1 sdk: ^3.8.1

View File

@@ -880,14 +880,6 @@ void main() {
'createdAt': testGroup.createdAt.toIso8601String(), 'createdAt': testGroup.createdAt.toIso8601String(),
}, },
], ],
'teams': [
{
'id': testTeam.id,
'name': testTeam.name,
'memberIds': [testPlayer1.id, testPlayer2.id],
'createdAt': testTeam.createdAt.toIso8601String(),
},
],
'matches': [ 'matches': [
{ {
'id': testMatch.id, 'id': testMatch.id,