40 Commits

Author SHA1 Message Date
8c41f6a255 Merge remote-tracking branch 'origin/development' into enhancement/138-neues-navbar-design
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m4s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
# Conflicts:
#	pubspec.yaml
2026-01-12 20:21:35 +01:00
70f570489a Merge pull request 'Einstellungen ausgestalten' (#153) from feature/151-einstellungen-ausgestalten into development
Reviewed-on: #153
Reviewed-by: Mathis Kirchner <mathis.kirchner.mk@gmail.com>
2026-01-12 19:00:26 +00:00
fa7740101b Small changes on navbar
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m5s
Pull Request Pipeline / lint (pull_request) Successful in 2m8s
2026-01-12 19:34:35 +01:00
5aa2a335e3 Merge branch 'development' into enhancement/138-neues-navbar-design
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m3s
Pull Request Pipeline / lint (pull_request) Successful in 2m10s
2026-01-12 18:23:26 +00:00
80e601c10e Changed formatting of link displayment
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m4s
Pull Request Pipeline / lint (pull_request) Successful in 2m11s
2026-01-12 19:21:04 +01:00
2124c523bc Fixed link issue on android 2026-01-12 19:20:52 +01:00
7d0da81cf5 Updated license tile sizes 2026-01-12 19:09:31 +01:00
cd3a5c2a49 Merge branch 'development' into feature/151-einstellungen-ausgestalten
All checks were successful
Pull Request Pipeline / lint (pull_request) Successful in 2m18s
Pull Request Pipeline / test (pull_request) Successful in 2m18s
2026-01-12 17:34:56 +00:00
4628e96456 Merge pull request 'Hotfix: Textarea in Bug Template umgestaltet' (#160) from hotifx/issue-template-fix into development
Reviewed-on: #160
Reviewed-by: Mathis Kirchner <mathis.kirchner.mk@gmail.com>
2026-01-12 17:34:48 +00:00
da61f45e8e Merge remote-tracking branch 'origin/development' into enhancement/138-neues-navbar-design
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m13s
Pull Request Pipeline / lint (pull_request) Successful in 2m21s
2026-01-12 17:56:20 +01:00
9344f8212c Updated version number
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m14s
Pull Request Pipeline / lint (pull_request) Successful in 2m22s
2026-01-12 17:55:52 +01:00
6d42d59bad Removed area
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m8s
Pull Request Pipeline / lint (pull_request) Successful in 2m21s
2026-01-12 17:49:35 +01:00
46118c274c Updated textarea in template
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m14s
Pull Request Pipeline / lint (pull_request) Successful in 2m31s
2026-01-12 17:46:58 +01:00
679f4c94d9 Updated button size
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m1s
Pull Request Pipeline / lint (pull_request) Successful in 2m8s
2026-01-12 17:33:20 +01:00
8bf2b9e3dd Updated navbar item color & size 2026-01-12 17:31:30 +01:00
cde40ef293 Updated buttons in main menu
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 1m57s
Pull Request Pipeline / lint (pull_request) Successful in 2m5s
2026-01-12 17:23:43 +01:00
0fb6208345 Created new buttons for the main menu 2026-01-12 17:23:34 +01:00
ec5a686f90 Merge pull request 'Issue-Templates aktualisiert' (#154) from setup/update-issue-and-pr-templates into development
Reviewed-on: #154
Reviewed-by: Mathis Kirchner <mathis.kirchner.mk@gmail.com>
2026-01-12 16:22:04 +00:00
f0c6dd8401 Adjusted container size and padding
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m9s
Pull Request Pipeline / lint (pull_request) Successful in 2m17s
2026-01-12 16:20:36 +01:00
7bdad57cc8 Removed old code 2026-01-12 16:18:13 +01:00
5da1b6eecb Removed expanded widget 2026-01-12 16:17:18 +01:00
cdafd4bb6f Made links clickable 2026-01-12 16:16:59 +01:00
6aee055df2 Removed whitespace 2026-01-12 16:04:37 +01:00
6d9871a5f0 Removed Web
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m8s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
2026-01-12 16:03:09 +01:00
4bbbcdd93f Updated label
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m8s
Pull Request Pipeline / lint (pull_request) Successful in 2m11s
2026-01-12 00:47:43 +01:00
fed5c55dd4 Updated placeholder
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m7s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
2026-01-12 00:18:51 +01:00
c157644b44 Folder fix 2/2
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m8s
Pull Request Pipeline / lint (pull_request) Successful in 2m8s
2026-01-11 20:00:51 +01:00
5a5898787f Folder fix 1/2 2026-01-11 20:00:38 +01:00
9d3a45c01d Updated PR Template
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m8s
Pull Request Pipeline / lint (pull_request) Successful in 2m10s
2026-01-11 19:57:02 +01:00
485ac87fdb Added new yaml issue templates
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m14s
2026-01-11 19:41:45 +01:00
758f1e6c3a .gitea/PULL_REQUEST_TEMPLATE.md aktualisiert 2026-01-11 16:14:43 +00:00
7fc4bbfb13 Added placeholder setting tiles for legal
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m13s
Pull Request Pipeline / lint (pull_request) Successful in 2m14s
2026-01-11 17:09:05 +01:00
857e05127d Updated arb files 2026-01-11 16:55:28 +01:00
86982ada0f Updated license file
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m6s
Pull Request Pipeline / lint (pull_request) Successful in 2m9s
2026-01-11 16:48:08 +01:00
e51bf3eabb Updated spacing 2026-01-11 16:48:02 +01:00
d7f08c5f50 Updated license tile and adjusted settings tile accordingly
All checks were successful
Pull Request Pipeline / lint (pull_request) Successful in 2m37s
Pull Request Pipeline / test (pull_request) Successful in 2m36s
2026-01-11 16:33:56 +01:00
ab20bd764b implement draft blur navbar
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 2m0s
Pull Request Pipeline / lint (pull_request) Failing after 2m6s
2026-01-11 11:30:00 +01:00
8ca4e3210e remove button in match view for testing 2026-01-11 11:28:21 +01:00
a480530919 Merge branch 'development' into enhancement/138-neues-navbar-design
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m31s
Pull Request Pipeline / lint (pull_request) Successful in 2m34s
# Conflicts:
#	pubspec.yaml
2026-01-10 22:18:54 +01:00
799b7d8403 Implemented new nav bar with selected animation
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 2m3s
Pull Request Pipeline / lint (pull_request) Successful in 2m7s
2026-01-09 21:12:09 +01:00
27 changed files with 937 additions and 456 deletions

View File

@@ -1,35 +0,0 @@
---
name: Bug report
about: Erstelle eine Meldung für etwas, das nicht Funktioniert, wie es soll.
title: ''
labels: 'Task/Bug'
assignees: ''
---
# Bug Report
## Beschreibung
[Eine klare und prägnante Beschreibung des Bugs]
## Schritte zur Reproduktion
1. Schritt 1
2. Schritt 2
3. ...
## Erwartetes Verhalten
[Was hätte passieren sollen]
## Tatsächliches Verhalten
[Was tatsächlich passiert ist]
## Screenshots/Protokolle
[Falls zutreffend, füge Screenshots, Error Logs oder Stack Traces hinzu]
## Umgebung
- Plattform: Android, iOS, Web
- OS: [z. B. iOS 18.5, Android 14]
- Flutter Version: [z.B. 3.35.6]
## Verwandte Issues
[Verweisen Sie auf ähnliche Issues oder PRs]

View File

@@ -1,22 +0,0 @@
---
name: Enhancement
about: Enhancements for current features
title: ''
labels: 'Task\Enhancement'
assignees: ''
---
# Enhancement
## Aktuelles Verhalten
[Beschreibe die bestehende Funktionalität]
## Einschränkungen/Probleme
[Was sind die aktuellen Mängel?]
## Vorgeschlagene Verbesserung
[Wie kann das Problem bzw. die Einschränkung verbessert werden?]
## Zugehörige Issues
[Links zu verwandten oder blockierenden Issues]

View File

@@ -1,19 +0,0 @@
---
name: Feature
about: Neues Feature für die App
title: ''
labels: 'Task\Feature'
assignees: ''
---
# Feature
## Beschreibung
[Ausführliche Erläuterung der vorgeschlagenen Funktion]
## Vorgeschlagene Lösung
[Beschreibe, wie die Feature funktionieren soll]
## Zugehörige Issues
[Links zu verwandten oder blockierenden Issues]

View File

@@ -1,16 +0,0 @@
# [PR Titel]
**Zugehörige Issue(s):**
Closes `<issue-no>`
## Beschreibung
*Eine klare und prägnante Übersicht über die vorgenommenen Änderungen. Erläutere nicht nur das was gemacht wurde, sondern auch warum.*
## Änderungen
- [ ] Neue Funktion X hinzugefügt
- [ ] Bug in Komponente Y behoben
- [ ] Modul Z für bessere Leistung refactored
- [ ] Dependencies aktualisiert
## Zusätzliche Anmerkungen
*Gibt es zusätzlichen Kontext, Einschränkungen oder Informationen, die Reviewer wissen sollten?*

View File

@@ -0,0 +1,53 @@
name: Bug Report
about: Erstelle eine Bug Report
labels: 'Task/Bug'
title: ''
body:
- type: textarea
id: description
attributes:
label: Beschreibung
description: Beschreibe klar und pregnant das Fehlerverhalten
placeholder: |
- Was genau ist das unerwünschte Verhalten?
- Welche Auswirkungen hat der Fehler?
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: Schritte zur Reproduktion
description: Beschreibe, wie der Fehler reproduziert werden kann
placeholder: |
- 1. Schritt 1
- 2. Schritt 2
- 3. ...
- type: dropdown
id: enviroment
attributes:
label: Umgebung
description: Gebe an, auf welchen Platformen dieser Fehler auftritt
list: false
multiple: true
options: ['Android', 'iOS']
- type: textarea
id: solution
attributes:
label: Lösungsidee
description: Beschreibe, wie das Problem bzw. gelöst werden kann
placeholder: |
- Button X ändern, sodass ...
- Funktion X so erweitern, dass ...
- Design anpassen, sodass ...
- type: textarea
attributes:
label: Verwandte Issues
description: Verweise auf ähnliche Issues oder PRs
placeholder: |
- Knüpft an Issue #35 an
- Ersetzt Issue #12
- Brauch Implementierung von #43

View File

@@ -0,0 +1,36 @@
name: Enhancement
about: Erstelle ein Enhancement-Ticket
labels: 'Task/Enhancement'
title: ''
body:
- type: textarea
id: description
attributes:
label: Aktuelles Verhalten
description: Beschreibe, wie die Funktionalität aktuell gestaltet ist
placeholder: |
- Aktuell macht Button X folgendes ...
- Das Problem ist, dass ...
validations:
required: true
- type: textarea
id: solution
attributes:
label: Vorgeschlagene Verbesserung
description: Beschreibe, wie das Problem bzw. die Einschränkung verbessert werden kann
placeholder: |
- Button X ändern, sodass ...
- Funktion X so erweitern, dass ...
- Design anpassen, sodass ...
validations:
required: true
- type: textarea
attributes:
label: Zugehörige Issues
description: Links zu verwandten oder blockierenden Issues
placeholder: |
- Knüpft an Issue #35 an
- Ersetzt Issue #12
- Brauch Implementierung von #43

View File

@@ -0,0 +1,36 @@
name: Feature
about: Erstelle ein Feature-Ticket
labels: 'Task/Feature'
title: ''
body:
- type: textarea
id: description
attributes:
label: Beschreibung
description: Ausführliche Erläuterung der vorgeschlagenen Funktion
placeholder: |
- Welchen Zweck erfüllt das Feature?
- Welches Problem löst das Feature?
- Wer profitiert davon?
- Warum ist es wichtig?
validations:
required: true
- type: textarea
id: solution
attributes:
label: Vorgeschlagene Lösung
description: Beschreibe, wie das Feature funktionieren soll
placeholder: |
- Neues Widget, das folgendermaßen aussieht ...
- Neue Ansicht, die folgende Inhalte hat
- Neue Funktionsweise von Komponente XY
- type: textarea
attributes:
label: Zugehörige Issues
description: Links zu verwandten oder blockierenden Issues
placeholder: |
- Knüpft an Issue #35 an
- Ersetzt Issue #12
- Brauch Implementierung von #43

View File

@@ -0,0 +1,47 @@
name: Pull Request
about: Vorlage für Pull Requests
title: "WIP: [Name des Issues]"
body:
- type: input
id: related_issue
attributes:
label: Zugehörige Issue(s)
description: Issues welche mit diesem Pull Request geschlossen werden sollen
placeholder: "Closes #123"
validations:
required: true
- type: textarea
id: description
attributes:
label: Beschreibung
description: |
Eine klare und prägnante Zusammenfassung aller vorgenommenen Änderungen.
placeholder: |
Was wurde geändert?
validations:
required: true
- type: textarea
id: changes
attributes:
label: Änderungen
description: Liste alle Änderungen detailiert auf, die in diesem Pull Request vorgenommen wurden.
placeholder: |
- Neue Funktion X hinzugefügt
- Bug in Komponente X behoben
- Modul X für bessere Leistung refactored
- Dependencies aktualisiert
- type: textarea
id: additional_notes
attributes:
label: Zusätzliche Anmerkungen
description: |
Gibt es zusätzlichen Kontext, Einschränkungen oder Informationen,
die Reviewer wissen sollten?
placeholder: |
- Bekannte Einschränkungen
- Offene TODOs
- Hinweise für Reviewer

View File

@@ -1,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application <application
android:label="game_tracker" android:label="game_tracker"
android:name="${applicationName}" android:name="${applicationName}"
@@ -42,5 +43,14 @@
<action android:name="android.intent.action.PROCESS_TEXT"/> <action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/> <data android:mimeType="text/plain"/>
</intent> </intent>
<!-- Required for url_launcher to open URLs in external browser -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
</queries> </queries>
</manifest> </manifest>

View File

@@ -11,6 +11,8 @@ class CustomTheme {
static Color onBoxColor = const Color(0xFF181818); static Color onBoxColor = const Color(0xFF181818);
static Color boxBorder = const Color(0xFF272727); static Color boxBorder = const Color(0xFF272727);
static const Color textColor = Colors.white; static const Color textColor = Colors.white;
static Color navBarItemSelectedColor = primaryColor.withGreen(100);
static Color navBarItemUnselectedColor = Colors.grey.shade400;
// ==================== Border Radius ==================== // ==================== Border Radius ====================
static const double standardBorderRadius = 12.0; static const double standardBorderRadius = 12.0;

View File

@@ -13,6 +13,7 @@
"create_match": "Spiel erstellen", "create_match": "Spiel erstellen",
"create_new_group": "Neue Gruppe erstellen", "create_new_group": "Neue Gruppe erstellen",
"create_new_match": "Neues Spiel erstellen", "create_new_match": "Neues Spiel erstellen",
"data": "Daten",
"data_successfully_deleted": "Daten erfolgreich gelöscht", "data_successfully_deleted": "Daten erfolgreich gelöscht",
"data_successfully_exported": "Daten erfolgreich exportiert", "data_successfully_exported": "Daten erfolgreich exportiert",
"data_successfully_imported": "Daten erfolgreich importiert", "data_successfully_imported": "Daten erfolgreich importiert",
@@ -34,18 +35,18 @@
"import_data": "Daten importieren", "import_data": "Daten importieren",
"info": "Info", "info": "Info",
"invalid_schema": "Ungültiges Schema", "invalid_schema": "Ungültiges Schema",
"licenses": "Lizenzen",
"no_licenses_found": "Keine Lizenzen gefunden",
"no_license_text_available": "Kein Lizenztext verfügbar",
"error": "Fehler",
"least_points": "Niedrigste Punkte", "least_points": "Niedrigste Punkte",
"legal": "Rechtliches",
"legal_notice": "Impressum",
"licenses": "Lizenzen",
"match_in_progress": "Spiel läuft...", "match_in_progress": "Spiel läuft...",
"match_name": "Spieltitel", "match_name": "Spieltitel",
"matches": "Spiele", "matches": "Spiele",
"menu": "Menü",
"most_points": "Höchste Punkte", "most_points": "Höchste Punkte",
"no_data_available": "Keine Daten verfügbar", "no_data_available": "Keine Daten verfügbar",
"no_groups_created_yet": "Noch keine Gruppen erstellt", "no_groups_created_yet": "Noch keine Gruppen erstellt",
"no_licenses_found": "Keine Lizenzen gefunden",
"no_license_text_available": "Kein Lizenztext verfügbar",
"no_matches_created_yet": "Noch keine Spiele erstellt", "no_matches_created_yet": "Noch keine Spiele erstellt",
"no_players_created_yet": "Noch keine Spieler:in erstellt", "no_players_created_yet": "Noch keine Spieler:in erstellt",
"no_players_found_with_that_name": "Keine Spieler:in mit diesem Namen gefunden", "no_players_found_with_that_name": "Keine Spieler:in mit diesem Namen gefunden",
@@ -59,6 +60,7 @@
"player_name": "Spieler:innenname", "player_name": "Spieler:innenname",
"players": "Spieler:innen", "players": "Spieler:innen",
"players_count": "{count} Spieler", "players_count": "{count} Spieler",
"privacy_policy": "Datenschutzerklärung",
"quick_create": "Schnellzugriff", "quick_create": "Schnellzugriff",
"recent_matches": "Letzte Spiele", "recent_matches": "Letzte Spiele",
"ruleset": "Regelwerk", "ruleset": "Regelwerk",

View File

@@ -39,6 +39,9 @@
"@create_new_match": { "@create_new_match": {
"description": "Button text to create a new match" "description": "Button text to create a new match"
}, },
"@data": {
"description": "Data label"
},
"@data_successfully_deleted": { "@data_successfully_deleted": {
"description": "Success message after deleting data" "description": "Success message after deleting data"
}, },
@@ -107,21 +110,18 @@
"@invalid_schema": { "@invalid_schema": {
"description": "Error message for invalid schema" "description": "Error message for invalid schema"
}, },
"@licenses": {
"description": "Licenses menu item"
},
"@no_licenses_found": {
"description": "Message when no licenses are found"
},
"@no_license_text_available": {
"description": "Message when no license text is available"
},
"@error": {
"description": "Error label"
},
"@least_points": { "@least_points": {
"description": "Title for least points ruleset" "description": "Title for least points ruleset"
}, },
"@legal": {
"description": "Legal section header"
},
"@legal_notice": {
"description": "Legal notice menu item"
},
"@licenses": {
"description": "Licenses menu item"
},
"@match_in_progress": { "@match_in_progress": {
"description": "Message when match is in progress" "description": "Message when match is in progress"
}, },
@@ -131,9 +131,6 @@
"@matches": { "@matches": {
"description": "Label for matches" "description": "Label for matches"
}, },
"@menu": {
"description": "Menu label"
},
"@most_points": { "@most_points": {
"description": "Title for most points ruleset" "description": "Title for most points ruleset"
}, },
@@ -143,6 +140,12 @@
"@no_groups_created_yet": { "@no_groups_created_yet": {
"description": "Message when no groups exist" "description": "Message when no groups exist"
}, },
"@no_licenses_found": {
"description": "Message when no licenses are found"
},
"@no_license_text_available": {
"description": "Message when no license text is available"
},
"@no_matches_created_yet": { "@no_matches_created_yet": {
"description": "Message when no matches exist" "description": "Message when no matches exist"
}, },
@@ -187,6 +190,9 @@
} }
} }
}, },
"@privacy_policy": {
"description": "Privacy policy menu item"
},
"@quick_create": { "@quick_create": {
"description": "Title for quick create section" "description": "Title for quick create section"
}, },
@@ -221,7 +227,7 @@
"description": "Shows the number of selected players" "description": "Shows the number of selected players"
}, },
"@settings": { "@settings": {
"description": "Settings label" "description": "Label for the App Settings"
}, },
"@single_loser": { "@single_loser": {
"description": "Title for single loser ruleset" "description": "Title for single loser ruleset"
@@ -284,6 +290,7 @@
"create_match": "Create match", "create_match": "Create match",
"create_new_group": "Create new group", "create_new_group": "Create new group",
"create_new_match": "Create new match", "create_new_match": "Create new match",
"data": "Data",
"data_successfully_deleted": "Data successfully deleted", "data_successfully_deleted": "Data successfully deleted",
"data_successfully_exported": "Data successfully exported", "data_successfully_exported": "Data successfully exported",
"data_successfully_imported": "Data successfully imported", "data_successfully_imported": "Data successfully imported",
@@ -305,18 +312,18 @@
"import_data": "Import data", "import_data": "Import data",
"info": "Info", "info": "Info",
"invalid_schema": "Invalid Schema", "invalid_schema": "Invalid Schema",
"licenses": "Licenses",
"no_licenses_found": "No licenses found",
"no_license_text_available": "No license text available",
"error": "Error",
"least_points": "Least Points", "least_points": "Least Points",
"legal": "Legal",
"legal_notice": "Legal Notice",
"licenses": "Licenses",
"match_in_progress": "Match in progress...", "match_in_progress": "Match in progress...",
"match_name": "Match name", "match_name": "Match name",
"matches": "Matches", "matches": "Matches",
"menu": "Menu",
"most_points": "Most Points", "most_points": "Most Points",
"no_data_available": "No data available", "no_data_available": "No data available",
"no_groups_created_yet": "No groups created yet", "no_groups_created_yet": "No groups created yet",
"no_licenses_found": "No licenses found",
"no_license_text_available": "No license text available",
"no_matches_created_yet": "No matches created yet", "no_matches_created_yet": "No matches created yet",
"no_players_created_yet": "No players created yet", "no_players_created_yet": "No players created yet",
"no_players_found_with_that_name": "No players found with that name", "no_players_found_with_that_name": "No players found with that name",
@@ -330,6 +337,7 @@
"player_name": "Player name", "player_name": "Player name",
"players": "Players", "players": "Players",
"players_count": "{count} Players", "players_count": "{count} Players",
"privacy_policy": "Privacy Policy",
"quick_create": "Quick Create", "quick_create": "Quick Create",
"recent_matches": "Recent Matches", "recent_matches": "Recent Matches",
"ruleset": "Ruleset", "ruleset": "Ruleset",

View File

@@ -176,6 +176,12 @@ abstract class AppLocalizations {
/// **'Create new match'** /// **'Create new match'**
String get create_new_match; String get create_new_match;
/// Data label
///
/// In en, this message translates to:
/// **'Data'**
String get data;
/// Success message after deleting data /// Success message after deleting data
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -302,36 +308,30 @@ abstract class AppLocalizations {
/// **'Invalid Schema'** /// **'Invalid Schema'**
String get invalid_schema; String get invalid_schema;
/// Licenses menu item
///
/// In en, this message translates to:
/// **'Licenses'**
String get licenses;
/// Message when no licenses are found
///
/// In en, this message translates to:
/// **'No licenses found'**
String get no_licenses_found;
/// Message when no license text is available
///
/// In en, this message translates to:
/// **'No license text available'**
String get no_license_text_available;
/// Error label
///
/// In en, this message translates to:
/// **'Error'**
String get error;
/// Title for least points ruleset /// Title for least points ruleset
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Least Points'** /// **'Least Points'**
String get least_points; String get least_points;
/// Legal section header
///
/// In en, this message translates to:
/// **'Legal'**
String get legal;
/// Legal notice menu item
///
/// In en, this message translates to:
/// **'Legal Notice'**
String get legal_notice;
/// Licenses menu item
///
/// In en, this message translates to:
/// **'Licenses'**
String get licenses;
/// Message when match is in progress /// Message when match is in progress
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -350,12 +350,6 @@ abstract class AppLocalizations {
/// **'Matches'** /// **'Matches'**
String get matches; String get matches;
/// Menu label
///
/// In en, this message translates to:
/// **'Menu'**
String get menu;
/// Title for most points ruleset /// Title for most points ruleset
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -374,6 +368,18 @@ abstract class AppLocalizations {
/// **'No groups created yet'** /// **'No groups created yet'**
String get no_groups_created_yet; String get no_groups_created_yet;
/// Message when no licenses are found
///
/// In en, this message translates to:
/// **'No licenses found'**
String get no_licenses_found;
/// Message when no license text is available
///
/// In en, this message translates to:
/// **'No license text available'**
String get no_license_text_available;
/// Message when no matches exist /// Message when no matches exist
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -452,6 +458,12 @@ abstract class AppLocalizations {
/// **'{count} Players'** /// **'{count} Players'**
String players_count(int count); String players_count(int count);
/// Privacy policy menu item
///
/// In en, this message translates to:
/// **'Privacy Policy'**
String get privacy_policy;
/// Title for quick create section /// Title for quick create section
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -518,7 +530,7 @@ abstract class AppLocalizations {
/// **'Selected players'** /// **'Selected players'**
String get selected_players; String get selected_players;
/// Settings label /// Label for the App Settings
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Settings'** /// **'Settings'**

View File

@@ -49,6 +49,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get create_new_match => 'Neues Spiel erstellen'; String get create_new_match => 'Neues Spiel erstellen';
@override
String get data => 'Daten';
@override @override
String get data_successfully_deleted => 'Daten erfolgreich gelöscht'; String get data_successfully_deleted => 'Daten erfolgreich gelöscht';
@@ -115,21 +118,18 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get invalid_schema => 'Ungültiges Schema'; String get invalid_schema => 'Ungültiges Schema';
@override
String get licenses => 'Lizenzen';
@override
String get no_licenses_found => 'Keine Lizenzen gefunden';
@override
String get no_license_text_available => 'Kein Lizenztext verfügbar';
@override
String get error => 'Fehler';
@override @override
String get least_points => 'Niedrigste Punkte'; String get least_points => 'Niedrigste Punkte';
@override
String get legal => 'Rechtliches';
@override
String get legal_notice => 'Impressum';
@override
String get licenses => 'Lizenzen';
@override @override
String get match_in_progress => 'Spiel läuft...'; String get match_in_progress => 'Spiel läuft...';
@@ -139,9 +139,6 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get matches => 'Spiele'; String get matches => 'Spiele';
@override
String get menu => 'Menü';
@override @override
String get most_points => 'Höchste Punkte'; String get most_points => 'Höchste Punkte';
@@ -151,6 +148,12 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get no_groups_created_yet => 'Noch keine Gruppen erstellt'; String get no_groups_created_yet => 'Noch keine Gruppen erstellt';
@override
String get no_licenses_found => 'Keine Lizenzen gefunden';
@override
String get no_license_text_available => 'Kein Lizenztext verfügbar';
@override @override
String get no_matches_created_yet => 'Noch keine Spiele erstellt'; String get no_matches_created_yet => 'Noch keine Spiele erstellt';
@@ -193,6 +196,9 @@ class AppLocalizationsDe extends AppLocalizations {
return '$count Spieler'; return '$count Spieler';
} }
@override
String get privacy_policy => 'Datenschutzerklärung';
@override @override
String get quick_create => 'Schnellzugriff'; String get quick_create => 'Schnellzugriff';

View File

@@ -49,6 +49,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get create_new_match => 'Create new match'; String get create_new_match => 'Create new match';
@override
String get data => 'Data';
@override @override
String get data_successfully_deleted => 'Data successfully deleted'; String get data_successfully_deleted => 'Data successfully deleted';
@@ -115,21 +118,18 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get invalid_schema => 'Invalid Schema'; String get invalid_schema => 'Invalid Schema';
@override
String get licenses => 'Licenses';
@override
String get no_licenses_found => 'No licenses found';
@override
String get no_license_text_available => 'No license text available';
@override
String get error => 'Error';
@override @override
String get least_points => 'Least Points'; String get least_points => 'Least Points';
@override
String get legal => 'Legal';
@override
String get legal_notice => 'Legal Notice';
@override
String get licenses => 'Licenses';
@override @override
String get match_in_progress => 'Match in progress...'; String get match_in_progress => 'Match in progress...';
@@ -139,9 +139,6 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get matches => 'Matches'; String get matches => 'Matches';
@override
String get menu => 'Menu';
@override @override
String get most_points => 'Most Points'; String get most_points => 'Most Points';
@@ -151,6 +148,12 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get no_groups_created_yet => 'No groups created yet'; String get no_groups_created_yet => 'No groups created yet';
@override
String get no_licenses_found => 'No licenses found';
@override
String get no_license_text_available => 'No license text available';
@override @override
String get no_matches_created_yet => 'No matches created yet'; String get no_matches_created_yet => 'No matches created yet';
@@ -193,6 +196,9 @@ class AppLocalizationsEn extends AppLocalizations {
return '$count Players'; return '$count Players';
} }
@override
String get privacy_policy => 'Privacy Policy';
@override @override
String get quick_create => 'Quick Create'; String get quick_create => 'Quick Create';

View File

@@ -1,3 +1,5 @@
import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/adaptive_page_route.dart'; import 'package:game_tracker/core/adaptive_page_route.dart';
import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/core/custom_theme.dart';
@@ -71,53 +73,102 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
body: tabs[currentIndex], body: tabs[currentIndex],
extendBody: true, extendBody: true,
bottomNavigationBar: SafeArea( bottomNavigationBar: SizedBox(
minimum: const EdgeInsets.only(bottom: 30), height: 70 + MediaQuery.of(context).padding.bottom,
child: Container( child: Stack(
margin: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 10), children: [
decoration: BoxDecoration( // Dynamically generated blur layers for ultra-smooth transition
borderRadius: BorderRadius.circular(24), ...List.generate(34, (index) {
color: CustomTheme.primaryColor, // Use cubic curve for an even more natural, smoother transition
), final progress = index / 34.0; // 0.0 to 1.0
child: ClipRRect( final cubic = progress * progress * progress; // cubic curve
borderRadius: BorderRadius.circular(24), final blurStrength =
child: SizedBox( 0.5 + (cubic * 50.0); // Very smooth from 0.5 to 50.5
height: 60,
child: Row( // Height goes completely from 100% to 0% (all the way down)
mainAxisAlignment: MainAxisAlignment.spaceAround, // With extra density at the bottom for softer transition
children: <Widget>[ final heightFactor = index < 25
NavbarItem( // First 25 layers: 100% to 30%
index: 0, ? 1.0 - (progress * 0.7)
isSelected: currentIndex == 0, // Last 10 layers: 30% to 0% (denser)
icon: Icons.home_rounded, : 0.3 - ((index - 25) / 34.0);
label: loc.home,
onTabTapped: onTabTapped, return Positioned(
left: 0,
right: 0,
bottom: 0,
height:
(70 + MediaQuery.of(context).padding.bottom) *
heightFactor.clamp(0.05, 1.0),
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: blurStrength,
sigmaY: blurStrength,
),
child: Container(color: Colors.transparent),
), ),
NavbarItem( ),
index: 1, );
isSelected: currentIndex == 1, }),
icon: Icons.gamepad_rounded, // Gradient overlay
label: loc.matches, Positioned.fill(
onTabTapped: onTabTapped, child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
CustomTheme.boxColor.withValues(alpha: 1),
CustomTheme.boxColor.withValues(alpha: 0.5),
CustomTheme.boxColor.withValues(alpha: 0.2),
CustomTheme.boxColor.withValues(alpha: 0.0),
],
stops: const [0.0, 0.4, 0.8, 1],
), ),
NavbarItem( ),
index: 2,
isSelected: currentIndex == 2,
icon: Icons.group_rounded,
label: loc.groups,
onTabTapped: onTabTapped,
),
NavbarItem(
index: 3,
isSelected: currentIndex == 3,
icon: Icons.bar_chart_rounded,
label: loc.statistics,
onTabTapped: onTabTapped,
),
],
), ),
), ),
), // Navbar content
SafeArea(
child: SizedBox(
height: 70,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
NavbarItem(
index: 0,
isSelected: currentIndex == 0,
icon: Icons.home_rounded,
label: loc.home,
onTabTapped: onTabTapped,
),
NavbarItem(
index: 1,
isSelected: currentIndex == 1,
icon: Icons.gamepad_rounded,
label: loc.matches,
onTabTapped: onTabTapped,
),
NavbarItem(
index: 2,
isSelected: currentIndex == 2,
icon: Icons.group_rounded,
label: loc.groups,
onTabTapped: onTabTapped,
),
NavbarItem(
index: 3,
isSelected: currentIndex == 3,
icon: Icons.bar_chart_rounded,
label: loc.statistics,
onTabTapped: onTabTapped,
),
],
),
),
),
],
), ),
), ),
); );

View File

@@ -8,7 +8,7 @@ import 'package:game_tracker/data/dto/player.dart';
import 'package:game_tracker/l10n/generated/app_localizations.dart'; import 'package:game_tracker/l10n/generated/app_localizations.dart';
import 'package:game_tracker/presentation/views/main_menu/group_view/create_group_view.dart'; import 'package:game_tracker/presentation/views/main_menu/group_view/create_group_view.dart';
import 'package:game_tracker/presentation/widgets/app_skeleton.dart'; import 'package:game_tracker/presentation/widgets/app_skeleton.dart';
import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart'; import 'package:game_tracker/presentation/widgets/buttons/main_menu_button.dart';
import 'package:game_tracker/presentation/widgets/tiles/group_tile.dart'; import 'package:game_tracker/presentation/widgets/tiles/group_tile.dart';
import 'package:game_tracker/presentation/widgets/top_centered_message.dart'; import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -79,10 +79,10 @@ class _GroupsViewState extends State<GroupsView> {
), ),
), ),
Positioned( Positioned(
bottom: MediaQuery.paddingOf(context).bottom, bottom: MediaQuery.paddingOf(context).bottom + 20,
child: CustomWidthButton( child: MainMenuButton(
text: loc.create_group, text: loc.create_group,
sizeRelativeToWidth: 0.90, icon: Icons.group_add,
onPressed: () async { onPressed: () async {
await Navigator.push( await Navigator.push(
context, context,

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:fluttericon/rpg_awesome_icons.dart';
import 'package:game_tracker/core/adaptive_page_route.dart'; import 'package:game_tracker/core/adaptive_page_route.dart';
import 'package:game_tracker/core/constants.dart'; import 'package:game_tracker/core/constants.dart';
import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/core/custom_theme.dart';
@@ -12,7 +13,7 @@ import 'package:game_tracker/l10n/generated/app_localizations.dart';
import 'package:game_tracker/presentation/views/main_menu/match_view/create_match/create_match_view.dart'; import 'package:game_tracker/presentation/views/main_menu/match_view/create_match/create_match_view.dart';
import 'package:game_tracker/presentation/views/main_menu/match_view/match_result_view.dart'; import 'package:game_tracker/presentation/views/main_menu/match_view/match_result_view.dart';
import 'package:game_tracker/presentation/widgets/app_skeleton.dart'; import 'package:game_tracker/presentation/widgets/app_skeleton.dart';
import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart'; import 'package:game_tracker/presentation/widgets/buttons/main_menu_button.dart';
import 'package:game_tracker/presentation/widgets/tiles/match_tile.dart'; import 'package:game_tracker/presentation/widgets/tiles/match_tile.dart';
import 'package:game_tracker/presentation/widgets/top_centered_message.dart'; import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -104,10 +105,10 @@ class _MatchViewState extends State<MatchView> {
), ),
), ),
Positioned( Positioned(
bottom: MediaQuery.paddingOf(context).bottom, bottom: MediaQuery.paddingOf(context).bottom + 20,
child: CustomWidthButton( child: MainMenuButton(
text: loc.create_match, text: 'Spiel erstellen',
sizeRelativeToWidth: 0.90, icon: RpgAwesome.clovers_card,
onPressed: () async { onPressed: () async {
Navigator.push( Navigator.push(
context, context,

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart'; import 'package:game_tracker/core/custom_theme.dart';
import 'package:game_tracker/l10n/generated/app_localizations.dart'; import 'package:game_tracker/l10n/generated/app_localizations.dart';
import 'package:game_tracker/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart'; import 'package:game_tracker/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart';
import 'package:url_launcher/url_launcher.dart';
class LicenseDetailView extends StatelessWidget { class LicenseDetailView extends StatelessWidget {
final Package package; final Package package;
@@ -22,30 +23,53 @@ class LicenseDetailView extends StatelessWidget {
Center( Center(
child: Column( child: Column(
children: [ children: [
Text( Row(
package.name, mainAxisAlignment: MainAxisAlignment.center,
textAlign: TextAlign.center, children: [
style: const TextStyle( Container(
fontSize: 24, margin: const EdgeInsetsGeometry.only(right: 15),
fontWeight: FontWeight.bold, width: 60,
), height: 60,
decoration: BoxDecoration(
color: CustomTheme.primaryColor.withAlpha(40),
borderRadius: BorderRadius.circular(10),
),
child: Icon(
Icons.description,
color: CustomTheme.primaryColor,
size: 30,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
package.name,
textAlign: TextAlign.left,
style: const TextStyle(
height: 0,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
if (package.version != null) ...[
Text(
'Version ${package.version}',
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade300,
),
),
],
],
),
],
), ),
if (package.version != null) ...[
const SizedBox(height: 8),
Text(
'Version ${package.version}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade300,
fontWeight: FontWeight.bold,
),
),
],
if (package.authors.isNotEmpty) ...[ if (package.authors.isNotEmpty) ...[
const SizedBox(height: 8), const SizedBox(height: 8),
Text( SelectableText(
package.authors.join(', '), package.authors.join(', '),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
@@ -56,20 +80,33 @@ class LicenseDetailView extends StatelessWidget {
], ],
if (package.homepage != null && if (package.homepage != null &&
package.homepage!.isNotEmpty) ...[ package.homepage!.isNotEmpty) ...[
const SizedBox(height: 4), const SizedBox(height: 8),
Text( GestureDetector(
package.homepage!, onTap: () async {
textAlign: TextAlign.center, final uri = Uri.parse(package.homepage!);
style: TextStyle( await launchUrl(uri, mode: LaunchMode.platformDefault);
fontSize: 12, },
color: Colors.grey.shade500, child: SizedBox(
width: 300,
child: Text(
package.homepage!,
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
color: CustomTheme.secondaryColor,
decoration: TextDecoration.underline,
decorationColor: CustomTheme.secondaryColor,
),
),
), ),
), ),
], ],
], ],
), ),
), ),
const SizedBox(height: 24), const SizedBox(height: 20),
Container( Container(
width: double.infinity, width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16), padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),

View File

@@ -38,6 +38,7 @@ const allDependencies = <Package>[
_cross_file, _cross_file,
_crypto, _crypto,
_csslib, _csslib,
_cupertino_icons,
_dart_pubspec_licenses, _dart_pubspec_licenses,
_dart_style, _dart_style,
_dbus, _dbus,
@@ -153,28 +154,29 @@ const allDependencies = <Package>[
/// Direct `dependencies`. /// Direct `dependencies`.
const dependencies = <Package>[ const dependencies = <Package>[
_flutter, _flutter,
_clock,
_cupertino_icons,
_drift, _drift,
_drift_flutter, _drift_flutter,
_file_picker,
_file_saver,
_font_awesome_flutter,
_intl,
_json_schema,
_package_info_plus,
_path_provider, _path_provider,
_provider, _provider,
_skeletonizer, _skeletonizer,
_uuid, _url_launcher,
_file_picker, _uuid
_json_schema,
_file_saver,
_clock,
_intl,
_package_info_plus,
_font_awesome_flutter,
_url_launcher
]; ];
/// Direct `dev_dependencies`. /// Direct `dev_dependencies`.
const devDependencies = <Package>[ const devDependencies = <Package>[
_build_runner,
_dart_pubspec_licenses, _dart_pubspec_licenses,
_flutter_lints,
_drift_dev, _drift_dev,
_build_runner _flutter_lints
]; ];
/// Package license definition. /// Package license definition.
@@ -1468,6 +1470,40 @@ 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.''',
); );
/// cupertino_icons 1.0.8
const _cupertino_icons = Package(
name: 'cupertino_icons',
description: 'Default icons asset for Cupertino widgets based on Apple styled icons',
repository: 'https://github.com/flutter/packages/tree/main/third_party/packages/cupertino_icons',
authors: [],
version: '1.0.8',
spdxIdentifiers: ['MIT'],
isMarkdown: false,
isSdk: false,
dependencies: [],
devDependencies: [PackageRef('flutter')],
license: '''The MIT License (MIT)
Copyright (c) 2016 Vladimir Kharlampidi
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.''',
);
/// dart_pubspec_licenses 3.0.15 /// dart_pubspec_licenses 3.0.15
const _dart_pubspec_licenses = Package( const _dart_pubspec_licenses = Package(
name: 'dart_pubspec_licenses', name: 'dart_pubspec_licenses',
@@ -7255,16 +7291,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.''', SOFTWARE.''',
); );
/// game_tracker 0.0.4+111 /// game_tracker 0.0.5+127
const _game_tracker = Package( const _game_tracker = Package(
name: 'game_tracker', name: 'game_tracker',
description: 'Game Tracking App for Card Games', description: 'Game Tracking App for Card Games',
authors: [], authors: [],
version: '0.0.4+111', version: '0.0.5+127',
spdxIdentifiers: [], spdxIdentifiers: [],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
dependencies: [PackageRef('flutter'), PackageRef('drift'), PackageRef('drift_flutter'), PackageRef('path_provider'), PackageRef('provider'), PackageRef('skeletonizer'), PackageRef('uuid'), PackageRef('file_picker'), PackageRef('json_schema'), PackageRef('file_saver'), PackageRef('clock'), PackageRef('intl'), PackageRef('package_info_plus'), PackageRef('font_awesome_flutter'), PackageRef('url_launcher')], dependencies: [PackageRef('flutter'), PackageRef('clock'), PackageRef('cupertino_icons'), PackageRef('drift'), PackageRef('drift_flutter'), PackageRef('file_picker'), PackageRef('file_saver'), PackageRef('font_awesome_flutter'), PackageRef('intl'), PackageRef('json_schema'), PackageRef('package_info_plus'), PackageRef('path_provider'), PackageRef('provider'), PackageRef('skeletonizer'), PackageRef('url_launcher'), PackageRef('uuid')],
devDependencies: [PackageRef('dart_pubspec_licenses'), PackageRef('flutter_lints'), PackageRef('drift_dev'), PackageRef('build_runner')], devDependencies: [PackageRef('build_runner'), PackageRef('dart_pubspec_licenses'), PackageRef('drift_dev'), PackageRef('flutter_lints')],
); );

View File

@@ -22,7 +22,7 @@ class LicensesView extends StatelessWidget {
itemBuilder: (context, index) { itemBuilder: (context, index) {
final package = allDependencies[index]; final package = allDependencies[index];
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12), padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
child: LicenseTile(package: package), child: LicenseTile(package: package),
); );
}, },

View File

@@ -40,172 +40,190 @@ class _SettingsViewState extends State<SettingsView> {
child: Scaffold( child: Scaffold(
appBar: AppBar(backgroundColor: CustomTheme.backgroundColor), appBar: AppBar(backgroundColor: CustomTheme.backgroundColor),
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
body: Column( body: SingleChildScrollView(
mainAxisAlignment: MainAxisAlignment.start, child: Column(
crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ crossAxisAlignment: CrossAxisAlignment.start,
Padding( children: [
padding: const EdgeInsets.fromLTRB(24, 0, 24, 10), Padding(
child: Text( padding: const EdgeInsets.only(left: 16, bottom: 10),
textAlign: TextAlign.start, child: Text(
loc.menu, textAlign: TextAlign.start,
style: const TextStyle( loc.settings,
fontSize: 28, style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 28,
fontWeight: FontWeight.bold,
),
), ),
), ),
), Padding(
Padding( padding: const EdgeInsets.only(left: 16, top: 10, bottom: 10),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10), child: Text(
child: Text( textAlign: TextAlign.start,
textAlign: TextAlign.start, loc.data,
loc.settings, style: const TextStyle(
style: const TextStyle( fontSize: 22,
fontSize: 22, fontWeight: FontWeight.bold,
fontWeight: FontWeight.bold, ),
), ),
), ),
), SettingsListTile(
SettingsListTile( title: loc.export_data,
title: loc.export_data, icon: Icons.upload,
icon: Icons.upload_rounded, suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16), onPressed: () async {
onPressed: () async { final String json =
final String json = await DataTransferService.getAppDataAsJson( await DataTransferService.getAppDataAsJson(context);
context, final result = await DataTransferService.exportData(
); json,
final result = await DataTransferService.exportData( 'game_tracker-data',
json, );
'game_tracker-data', if (!context.mounted) return;
); showExportSnackBar(context: context, result: result);
if (!context.mounted) return; },
showExportSnackBar(context: context, result: result); ),
}, SettingsListTile(
), title: loc.import_data,
SettingsListTile( icon: Icons.download,
title: loc.import_data, suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
icon: Icons.download_rounded, onPressed: () async {
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16), final result = await DataTransferService.importData(context);
onPressed: () async { if (!context.mounted) return;
final result = await DataTransferService.importData(context); showImportSnackBar(context: context, result: result);
if (!context.mounted) return; },
showImportSnackBar(context: context, result: result); ),
}, SettingsListTile(
), title: loc.delete_all_data,
SettingsListTile( icon: Icons.delete,
title: loc.delete_all_data, suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
icon: Icons.delete_rounded, onPressed: () {
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16), showDialog<bool>(
onPressed: () { context: context,
showDialog<bool>( builder: (context) => AlertDialog(
context: context, title: Text('${loc.delete_all_data}?'),
builder: (context) => AlertDialog( content: Text(loc.this_cannot_be_undone),
title: Text('${loc.delete_all_data}?'), actions: [
content: Text(loc.this_cannot_be_undone), TextButton(
actions: [ onPressed: () => Navigator.of(context).pop(false),
TextButton( child: Text(loc.cancel),
onPressed: () => Navigator.of(context).pop(false), ),
child: Text(loc.cancel), TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text(loc.delete),
),
],
),
).then((confirmed) {
if (confirmed == true && context.mounted) {
DataTransferService.deleteAllData(context);
showSnackbar(
context: context,
message: AppLocalizations.of(
context,
).data_successfully_deleted,
);
}
});
},
),
Padding(
padding: const EdgeInsets.only(left: 16, top: 10, bottom: 10),
child: Text(
textAlign: TextAlign.start,
loc.legal,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
),
),
),
SettingsListTile(
title: loc.licenses,
icon: Icons.insert_drive_file,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const LicensesView(),
),
);
},
),
SettingsListTile(
title: loc.legal_notice,
icon: Icons.account_balance_sharp,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: null,
),
SettingsListTile(
title: loc.privacy_policy,
icon: Icons.gpp_good_rounded,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: null,
),
Padding(
padding: const EdgeInsets.only(top: 30, bottom: 20),
child: Center(
child: Column(
spacing: 4,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 40,
children: [
GestureDetector(
child: const Icon(Icons.language),
onTap: () => {
launchUrl(Uri.parse('https://liquid-dev.de')),
},
),
GestureDetector(
child: const FaIcon(FontAwesomeIcons.github),
onTap: () => {
launchUrl(
Uri.parse(
'https://github.com/liquiddevelopmentde',
),
),
},
),
GestureDetector(
child: Icon(
Platform.isIOS
? CupertinoIcons.mail_solid
: Icons.email,
),
onTap: () => launchUrl(
Uri.parse('mailto:hi@liquid-dev.de'),
),
),
],
),
), ),
TextButton( Text(
onPressed: () => Navigator.of(context).pop(true), '© ${DateFormat('yyyy').format(DateTime.now())} Liquid Development',
child: Text(loc.delete), style: TextStyle(
color: Colors.grey.shade600,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
Text(
'Version ${_packageInfo.version} (${_packageInfo.buildNumber})',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 14,
fontWeight: FontWeight.w600,
),
), ),
], ],
), ),
).then((confirmed) {
if (confirmed == true && context.mounted) {
DataTransferService.deleteAllData(context);
showSnackbar(
context: context,
message: AppLocalizations.of(
context,
).data_successfully_deleted,
);
}
});
},
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 10),
child: Text(
textAlign: TextAlign.start,
'App',
style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
),
),
SettingsListTile(
title: loc.licenses,
icon: Icons.insert_drive_file,
suffixWidget: const Icon(Icons.arrow_forward_ios, size: 16),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const LicensesView()),
);
},
),
const Spacer(),
Padding(
padding: const EdgeInsets.all(20),
child: Center(
child: Column(
spacing: 4,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 40,
children: [
GestureDetector(
child: const Icon(Icons.language),
onTap: () => {
launchUrl(Uri.parse('https://liquid-dev.de')),
},
),
GestureDetector(
child: const FaIcon(FontAwesomeIcons.github),
onTap: () => {
launchUrl(
Uri.parse(
'https://github.com/liquiddevelopmentde',
),
),
},
),
GestureDetector(
child: Icon(
Platform.isIOS
? CupertinoIcons.mail_solid
: Icons.email,
),
onTap: () =>
launchUrl(Uri.parse('mailto:hi@liquid-dev.de')),
),
],
),
),
Text(
'© ${DateFormat('yyyy').format(DateTime.now())} Liquid Development',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
Text(
'Version ${_packageInfo.version} (${_packageInfo.buildNumber})',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
],
), ),
), ),
), ],
], ),
), ),
), ),
); );
@@ -282,6 +300,7 @@ class _SettingsViewState extends State<SettingsView> {
); );
} }
/// Initializes the package information.
Future<void> _initPackageInfo() async { Future<void> _initPackageInfo() async {
final info = await PackageInfo.fromPlatform(); final info = await PackageInfo.fromPlatform();
setState(() { setState(() {

View File

@@ -0,0 +1,98 @@
import 'package:flutter/material.dart';
/// A button for the main menu with an optional icon and a press animation.
/// - [text]: The text of the button.
/// - [icon]: The icon of the button.
/// - [onPressed]: The callback to be invoked when the button is pressed.
class MainMenuButton extends StatefulWidget {
const MainMenuButton({
super.key,
required this.text,
this.icon,
required this.onPressed,
});
/// The text of the button.
final String text;
/// The icon of the button.
final IconData? icon;
/// The callback to be invoked when the button is pressed.
final void Function() onPressed;
@override
State<MainMenuButton> createState() => _MainMenuButtonState();
}
class _MainMenuButtonState extends State<MainMenuButton>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _scaleAnimation;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 100),
vsync: this,
);
_scaleAnimation = Tween<double>(begin: 1.0, end: 0.95).animate(
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
);
}
@override
Widget build(BuildContext context) {
return ScaleTransition(
scale: _scaleAnimation,
child: GestureDetector(
onTapDown: (_) {
_animationController.forward();
},
onTapUp: (_) async {
await _animationController.reverse();
if (mounted) {
widget.onPressed();
}
},
onTapCancel: () {
_animationController.reverse();
},
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30),
),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (widget.icon != null) ...[
Icon(widget.icon, size: 26, color: Colors.black),
const SizedBox(width: 7),
],
Text(
widget.text,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
],
),
),
),
);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:game_tracker/core/custom_theme.dart';
/// A navigation bar item widget that represents a single tab in a navigation bar. /// A navigation bar item widget that represents a single tab in a navigation bar.
/// - [index]: The index of the tab. /// - [index]: The index of the tab.
@@ -35,7 +36,45 @@ class NavbarItem extends StatefulWidget {
State<NavbarItem> createState() => _NavbarItemState(); State<NavbarItem> createState() => _NavbarItemState();
} }
class _NavbarItemState extends State<NavbarItem> { class _NavbarItemState extends State<NavbarItem>
with SingleTickerProviderStateMixin {
/// Animation controller for the scale animation
late AnimationController _animationController;
/// Scale animation for the icon when selected
late Animation<double> _scaleAnimation;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: this,
);
_scaleAnimation = Tween<double>(begin: 1.0, end: 1.2).animate(
CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOutBack,
),
);
if (widget.isSelected) {
_animationController.forward();
}
}
// Retrigger animation on selection change
@override
void didUpdateWidget(NavbarItem oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.isSelected && !oldWidget.isSelected) {
_animationController.forward();
} else if (!widget.isSelected && oldWidget.isSelected) {
_animationController.reverse();
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Expanded( return Expanded(
@@ -48,19 +87,29 @@ class _NavbarItemState extends State<NavbarItem> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon( ScaleTransition(
widget.icon, scale: widget.isSelected
color: widget.isSelected ? Colors.white : Colors.black, ? _scaleAnimation
: const AlwaysStoppedAnimation(1.0),
child: Icon(
widget.icon,
color: widget.isSelected
? CustomTheme.navBarItemSelectedColor
: CustomTheme.navBarItemUnselectedColor,
size: 32,
),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
widget.label, widget.label,
style: TextStyle( style: TextStyle(
color: widget.isSelected ? Colors.white : Colors.black, color: widget.isSelected
fontSize: 12, ? CustomTheme.navBarItemSelectedColor
: CustomTheme.navBarItemUnselectedColor,
fontSize: widget.isSelected ? 12 : 11,
fontWeight: widget.isSelected fontWeight: widget.isSelected
? FontWeight.bold ? FontWeight.bold
: FontWeight.normal, : FontWeight.w500,
), ),
), ),
], ],
@@ -69,4 +118,10 @@ class _NavbarItemState extends State<NavbarItem> {
), ),
); );
} }
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
} }

View File

@@ -19,32 +19,85 @@ class LicenseTile extends StatelessWidget {
); );
}, },
child: Container( child: Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), margin: const EdgeInsets.only(bottom: 8),
decoration: CustomTheme.standardBoxDecoration, padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 12),
decoration: CustomTheme.standardBoxDecoration.copyWith(
borderRadius: BorderRadius.circular(12),
),
child: Row( child: Row(
children: [ children: [
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: CustomTheme.primaryColor.withAlpha(40),
borderRadius: BorderRadius.circular(10),
),
child: Icon(
Icons.description,
color: CustomTheme.primaryColor,
size: 32,
),
),
const SizedBox(width: 16),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Row(
package.name, children: [
style: const TextStyle( Flexible(
fontSize: 16, child: Text(
fontWeight: FontWeight.w600, package.name,
), overflow: TextOverflow.ellipsis,
maxLines: 1,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
),
if (package.version != null &&
package.version!.isNotEmpty) ...[
const SizedBox(width: 12),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 2,
),
decoration: BoxDecoration(
color: CustomTheme.onBoxColor,
borderRadius: BorderRadius.circular(6),
),
child: Text(
'v${package.version}',
style: TextStyle(
fontSize: 11,
color: Colors.grey.shade500,
fontWeight: FontWeight.w500,
),
),
),
],
],
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
package.description, package.description,
maxLines: 1, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 12, color: Colors.grey.shade400), style: TextStyle(
fontSize: 13,
color: Colors.grey.shade400,
height: 1.3,
),
), ),
], ],
), ),
), ),
const Icon(Icons.arrow_forward_ios, size: 16), const SizedBox(width: 12),
// Arrow Icon
Icon(Icons.chevron_right, color: Colors.grey.shade600, size: 24),
], ],
), ),
), ),

View File

@@ -38,7 +38,7 @@ class SettingsListTile extends StatelessWidget {
onTap: onPressed ?? () {}, onTap: onPressed ?? () {},
child: Container( child: Container(
margin: EdgeInsets.zero, margin: EdgeInsets.zero,
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 14), padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
decoration: CustomTheme.standardBoxDecoration, decoration: CustomTheme.standardBoxDecoration,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -47,12 +47,17 @@ class SettingsListTile extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Container( Container(
padding: const EdgeInsets.all(8), width: 44,
height: 44,
decoration: BoxDecoration( decoration: BoxDecoration(
color: CustomTheme.primaryColor, color: CustomTheme.primaryColor.withAlpha(40),
shape: BoxShape.circle, borderRadius: BorderRadius.circular(10),
),
child: Icon(
icon,
size: 28,
color: CustomTheme.primaryColor.withGreen(40),
), ),
child: Icon(icon, size: 24),
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
Text(title, style: const TextStyle(fontSize: 18)), Text(title, style: const TextStyle(fontSize: 18)),

View File

@@ -1,7 +1,7 @@
name: game_tracker name: game_tracker
description: "Game Tracking App for Card Games" description: "Game Tracking App for Card Games"
publish_to: 'none' publish_to: 'none'
version: 0.0.4+122 version: 0.0.6+208
environment: environment:
sdk: ^3.8.1 sdk: ^3.8.1
@@ -17,6 +17,7 @@ dependencies:
drift_flutter: ^0.2.4 drift_flutter: ^0.2.4
file_picker: ^10.3.6 file_picker: ^10.3.6
file_saver: ^0.3.1 file_saver: ^0.3.1
fluttericon: ^2.0.0
font_awesome_flutter: ^10.12.0 font_awesome_flutter: ^10.12.0
intl: any intl: any
json_schema: ^5.2.2 json_schema: ^5.2.2
@@ -27,7 +28,6 @@ dependencies:
url_launcher: ^6.3.2 url_launcher: ^6.3.2
uuid: ^4.5.2 uuid: ^4.5.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter