Merge pull request #88 from flixcoo/feature/46-implement-native-rating-dialogs

Implement native rating dialogs
This commit is contained in:
2025-07-10 20:46:30 +02:00
committed by GitHub
25 changed files with 393 additions and 94 deletions

View File

@@ -11,6 +11,4 @@ linter:
prefer_const_literals_to_create_immutables: true
unnecessary_const: true
lines_longer_than_80_chars: false
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
constant_identifier_names: false

View File

@@ -1,5 +1,6 @@
arb-dir: lib/l10n
arb-dir: lib/l10n/arb
template-arb-file: app_de.arb
untranslated-messages-file: lib/l10n/untranslated_messages.json
untranslated-messages-file: lib/l10n/arb/untranslated_messages.json
nullable-getter: false
output-localization-file: app_localizations.dart
output-dir: lib/l10n/generated

22
lib/core/constants.dart Normal file
View File

@@ -0,0 +1,22 @@
import 'package:rate_my_app/rate_my_app.dart';
class Constants {
static const String appDevPhase = 'Beta';
static const String INSTAGRAM_LINK = 'https://instagram.felixkirchner.de';
static const String GITHUB_LINK = 'https://github.felixkirchner.de';
static const String GITHUB_ISSUES_LINK =
'https://cabocounter-issues.felixkirchner.de';
static const String GITHUB_WIKI_LINK =
'https://cabocounter-wiki.felixkirchner.de';
static const String EMAIL = 'cabocounter@felixkirchner.de';
static const String PRIVACY_POLICY_LINK =
'https://www.privacypolicies.com/live/1b3759d4-b2f1-4511-8e3b-21bb1626be68';
static RateMyApp rateMyApp = RateMyApp(
appStoreIdentifier: '6747105718',
minDays: 15,
remindDays: 45,
minLaunches: 15,
remindLaunches: 40);
}

View File

@@ -30,6 +30,16 @@
}
}
},
"pre_rating_title": "Gefällt dir die App?",
"pre_rating_message": "Feedback hilft mir, die App zu verbessern. Vielen Dank!",
"yes": "Ja",
"no": "Nein",
"bad_rating_title": "Unzufrieden mit der App?",
"bad_rating_message": "Schreib mir gerne direkt eine E-Mail, damit wir dein Problem lösen können!",
"contact_email": "E-Mail schreiben",
"email_subject": "Feedback: Cabo Counter App",
"email_body": "Ich habe folgendes Feedback...",
"overview": "Übersicht",
"new_game": "Neues Spiel",
"game_title": "Titel des Spiels",
@@ -103,6 +113,7 @@
"create_issue": "Issue erstellen",
"wiki": "Wiki",
"app_version": "App-Version",
"privacy_policy": "Datenschutzerklärung",
"build": "Build-Nr.",
"loading": "Lädt...",

View File

@@ -30,6 +30,16 @@
}
}
},
"pre_rating_title": "Do you like the app?",
"pre_rating_message": "Feedback helps me to continuously improve the app. Thank you!",
"yes": "Yes",
"no": "No",
"bad_rating_title": "Not satisfied?",
"bad_rating_message": "If you are not satisfied with the app, please let me know before leaving a bad rating. I will try to fix the issue as soon as possible.",
"contact_email": "Contact via E-Mail",
"email_subject": "Feedback: Cabo Counter App",
"email_body": "I have the following feedback...",
"overview": "Overview",
"new_game": "New Game",
"game_title": "Game Title",
@@ -103,6 +113,7 @@
"create_issue": "Create Issue",
"wiki": "Wiki",
"app_version": "App Version",
"privacy_policy": "Privacy Policy",
"loading": "Loading...",
"build": "Build No.",

View File

@@ -18,7 +18,7 @@ import 'app_localizations_en.dart';
/// `supportedLocales` list. For example:
///
/// ```dart
/// import 'l10n/app_localizations.dart';
/// import 'generated/app_localizations.dart';
///
/// return MaterialApp(
/// localizationsDelegates: AppLocalizations.localizationsDelegates,
@@ -218,6 +218,60 @@ abstract class AppLocalizations {
/// **'Bist du sicher, dass du das Spiel \"{gameTitle}\" löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.'**
String delete_game_message(String gameTitle);
/// No description provided for @pre_rating_title.
///
/// In de, this message translates to:
/// **'Gefällt dir die App?'**
String get pre_rating_title;
/// No description provided for @pre_rating_message.
///
/// In de, this message translates to:
/// **'Feedback hilft mir, die App zu verbessern. Vielen Dank!'**
String get pre_rating_message;
/// No description provided for @yes.
///
/// In de, this message translates to:
/// **'Ja'**
String get yes;
/// No description provided for @no.
///
/// In de, this message translates to:
/// **'Nein'**
String get no;
/// No description provided for @bad_rating_title.
///
/// In de, this message translates to:
/// **'Unzufrieden mit der App?'**
String get bad_rating_title;
/// No description provided for @bad_rating_message.
///
/// In de, this message translates to:
/// **'Schreib mir gerne direkt eine E-Mail, damit wir dein Problem lösen können!'**
String get bad_rating_message;
/// No description provided for @contact_email.
///
/// In de, this message translates to:
/// **'E-Mail schreiben'**
String get contact_email;
/// No description provided for @email_subject.
///
/// In de, this message translates to:
/// **'Feedback: Cabo Counter App'**
String get email_subject;
/// No description provided for @email_body.
///
/// In de, this message translates to:
/// **'Ich habe folgendes Feedback...'**
String get email_body;
/// No description provided for @overview.
///
/// In de, this message translates to:
@@ -566,6 +620,12 @@ abstract class AppLocalizations {
/// **'App-Version'**
String get app_version;
/// No description provided for @privacy_policy.
///
/// In de, this message translates to:
/// **'Datenschutzerklärung'**
String get privacy_policy;
/// No description provided for @build.
///
/// In de, this message translates to:

View File

@@ -71,6 +71,35 @@ class AppLocalizationsDe extends AppLocalizations {
return 'Bist du sicher, dass du das Spiel \"$gameTitle\" löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.';
}
@override
String get pre_rating_title => 'Gefällt dir die App?';
@override
String get pre_rating_message =>
'Feedback hilft mir, die App zu verbessern. Vielen Dank!';
@override
String get yes => 'Ja';
@override
String get no => 'Nein';
@override
String get bad_rating_title => 'Unzufrieden mit der App?';
@override
String get bad_rating_message =>
'Schreib mir gerne direkt eine E-Mail, damit wir dein Problem lösen können!';
@override
String get contact_email => 'E-Mail schreiben';
@override
String get email_subject => 'Feedback: Cabo Counter App';
@override
String get email_body => 'Ich habe folgendes Feedback...';
@override
String get overview => 'Übersicht';
@@ -256,6 +285,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get app_version => 'App-Version';
@override
String get privacy_policy => 'Datenschutzerklärung';
@override
String get build => 'Build-Nr.';

View File

@@ -71,6 +71,35 @@ class AppLocalizationsEn extends AppLocalizations {
return 'Are you sure you want to delete the game \"$gameTitle\"? This action cannot be undone.';
}
@override
String get pre_rating_title => 'Do you like the app?';
@override
String get pre_rating_message =>
'Feedback helps me to continuously improve the app. Thank you!';
@override
String get yes => 'Yes';
@override
String get no => 'No';
@override
String get bad_rating_title => 'Not satisfied?';
@override
String get bad_rating_message =>
'If you are not satisfied with the app, please let me know before leaving a bad rating. I will try to fix the issue as soon as possible.';
@override
String get contact_email => 'Contac via E-Mail';
@override
String get email_subject => 'Feedback: Cabo Counter App';
@override
String get email_body => 'I have the following feedback...';
@override
String get overview => 'Overview';
@@ -253,6 +282,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get app_version => 'App Version';
@override
String get privacy_policy => 'Privacy Policy';
@override
String get build => 'Build No.';

View File

@@ -1,9 +1,9 @@
import 'package:cabo_counter/l10n/app_localizations.dart';
import 'package:cabo_counter/core/custom_theme.dart';
import 'package:cabo_counter/l10n/generated/app_localizations.dart';
import 'package:cabo_counter/presentation/views/tab_view.dart';
import 'package:cabo_counter/services/config_service.dart';
import 'package:cabo_counter/services/local_storage_service.dart';
import 'package:cabo_counter/services/version_service.dart';
import 'package:cabo_counter/utility/custom_theme.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';

View File

@@ -1,11 +1,13 @@
import 'package:cabo_counter/l10n/app_localizations.dart';
import 'package:cabo_counter/core/constants.dart';
import 'package:cabo_counter/l10n/generated/app_localizations.dart';
import 'package:cabo_counter/services/version_service.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:url_launcher/url_launcher.dart';
class InformationView extends StatelessWidget {
const InformationView({super.key});
class AboutView extends StatelessWidget {
const AboutView({super.key});
@override
Widget build(BuildContext context) {
@@ -28,9 +30,13 @@ class InformationView extends StatelessWidget {
),
),
),
Text(
'${AppLocalizations.of(context).app_version} ${VersionService.getVersionWithBuild()}',
style: TextStyle(fontSize: 15, color: Colors.grey[300]),
),
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
child: SizedBox(
height: 200,
child: Image.asset('assets/cabo_counter-logo_rounded.png'),
@@ -54,15 +60,15 @@ class InformationView extends StatelessWidget {
children: [
IconButton(
onPressed: () =>
launchUrl(Uri.parse('https://www.instagram.com/fx.kr')),
launchUrl(Uri.parse(Constants.INSTAGRAM_LINK)),
icon: const Icon(FontAwesomeIcons.instagram)),
IconButton(
onPressed: () => launchUrl(
Uri.parse('mailto:felix.kirchner.fk@gmail.com')),
onPressed: () =>
launchUrl(Uri.parse('mailto:${Constants.EMAIL}')),
icon: const Icon(CupertinoIcons.envelope)),
IconButton(
onPressed: () =>
launchUrl(Uri.parse('https://www.github.com/flixcoo')),
launchUrl(Uri.parse(Constants.GITHUB_LINK)),
icon: const Icon(FontAwesomeIcons.github)),
],
),

View File

@@ -1,11 +1,11 @@
import 'package:cabo_counter/core/custom_theme.dart';
import 'package:cabo_counter/data/game_manager.dart';
import 'package:cabo_counter/data/game_session.dart';
import 'package:cabo_counter/l10n/app_localizations.dart';
import 'package:cabo_counter/l10n/generated/app_localizations.dart';
import 'package:cabo_counter/presentation/views/create_game_view.dart';
import 'package:cabo_counter/presentation/views/graph_view.dart';
import 'package:cabo_counter/presentation/views/round_view.dart';
import 'package:cabo_counter/services/local_storage_service.dart';
import 'package:cabo_counter/utility/custom_theme.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@@ -235,7 +235,8 @@ class _ActiveGameViewState extends State<ActiveGameView> {
child: Text(
AppLocalizations.of(context).end_game,
style: const TextStyle(
fontWeight: FontWeight.bold, color: Colors.red),
fontWeight: FontWeight.bold,
color: CupertinoColors.destructiveRed),
),
onPressed: () {
setState(() {

View File

@@ -1,10 +1,10 @@
import 'package:cabo_counter/core/custom_theme.dart';
import 'package:cabo_counter/data/game_manager.dart';
import 'package:cabo_counter/data/game_session.dart';
import 'package:cabo_counter/l10n/app_localizations.dart';
import 'package:cabo_counter/l10n/generated/app_localizations.dart';
import 'package:cabo_counter/presentation/views/active_game_view.dart';
import 'package:cabo_counter/presentation/views/mode_selection_view.dart';
import 'package:cabo_counter/services/config_service.dart';
import 'package:cabo_counter/utility/custom_theme.dart';
import 'package:flutter/cupertino.dart';
enum CreateStatus {

View File

@@ -1,5 +1,5 @@
import 'package:cabo_counter/data/game_session.dart';
import 'package:cabo_counter/l10n/app_localizations.dart';
import 'package:cabo_counter/l10n/generated/app_localizations.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_charts/charts.dart';

View File

@@ -1,13 +1,19 @@
import 'package:cabo_counter/core/constants.dart';
import 'package:cabo_counter/core/custom_theme.dart';
import 'package:cabo_counter/data/game_manager.dart';
import 'package:cabo_counter/l10n/app_localizations.dart';
import 'package:cabo_counter/l10n/generated/app_localizations.dart';
import 'package:cabo_counter/presentation/views/active_game_view.dart';
import 'package:cabo_counter/presentation/views/create_game_view.dart';
import 'package:cabo_counter/presentation/views/settings_view.dart';
import 'package:cabo_counter/services/config_service.dart';
import 'package:cabo_counter/services/local_storage_service.dart';
import 'package:cabo_counter/utility/custom_theme.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
enum PreRatingDialogDecision { yes, no, cancel }
enum BadRatingDialogDecision { email, cancel }
class MainMenuView extends StatefulWidget {
const MainMenuView({super.key});
@@ -29,6 +35,17 @@ class _MainMenuViewState extends State<MainMenuView> {
});
});
gameManager.addListener(_updateView);
WidgetsBinding.instance.addPostFrameCallback((_) async {
await Constants.rateMyApp.init();
if (Constants.rateMyApp.shouldOpenDialog &&
Constants.appDevPhase != 'Beta') {
await Future.delayed(const Duration(milliseconds: 600));
if (!mounted) return;
_handleFeedbackDialog(context);
}
});
}
void _updateView() {
@@ -57,14 +74,12 @@ class _MainMenuViewState extends State<MainMenuView> {
icon: const Icon(CupertinoIcons.settings, size: 30)),
middle: const Text('Cabo Counter'),
trailing: IconButton(
onPressed: () => {
Navigator.push(
onPressed: () => Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => const CreateGameView(),
),
)
},
),
icon: const Icon(CupertinoIcons.add)),
),
child: CupertinoPageScaffold(
@@ -128,7 +143,7 @@ class _MainMenuViewState extends State<MainMenuView> {
final String gameTitle = gameManager
.gameList[index].gameTitle;
return await _showDeleteGamePopup(
gameTitle);
context, gameTitle);
},
onDismissed: (direction) {
gameManager
@@ -204,40 +219,144 @@ class _MainMenuViewState extends State<MainMenuView> {
return AppLocalizations.of(context).unlimited;
}
/// Handles the feedback dialog when the conditions for rating are met.
/// It shows a dialog asking the user if they like the app,
/// and based on their response, it either opens the rating dialog or an email client for feedback.
Future<void> _handleFeedbackDialog(BuildContext context) async {
final String emailSubject = AppLocalizations.of(context).email_subject;
final String emailBody = AppLocalizations.of(context).email_body;
final Uri emailUri = Uri(
scheme: 'mailto',
path: Constants.EMAIL,
query: 'subject=$emailSubject'
'&body=$emailBody',
);
PreRatingDialogDecision preRatingDecision =
await _showPreRatingDialog(context);
BadRatingDialogDecision badRatingDecision = BadRatingDialogDecision.cancel;
// so that the bad rating dialog is not shown immediately
await Future.delayed(const Duration(milliseconds: 300));
switch (preRatingDecision) {
case PreRatingDialogDecision.yes:
if (context.mounted) Constants.rateMyApp.showStarRateDialog(context);
break;
case PreRatingDialogDecision.no:
if (context.mounted) {
badRatingDecision = await _showBadRatingDialog(context);
}
if (badRatingDecision == BadRatingDialogDecision.email) {
if (context.mounted) {
launchUrl(emailUri);
}
}
break;
case PreRatingDialogDecision.cancel:
break;
}
}
/// Shows a confirmation dialog to delete all game sessions.
/// Returns true if the user confirms the deletion, false otherwise.
/// [gameTitle] is the title of the game session to be deleted.
Future<bool> _showDeleteGamePopup(String gameTitle) async {
bool? shouldDelete = await showCupertinoDialog<bool>(
Future<bool> _showDeleteGamePopup(
BuildContext context, String gameTitle) async {
return await showCupertinoDialog<bool>(
context: context,
builder: (context) {
builder: (BuildContext context) {
return CupertinoAlertDialog(
title: Text(AppLocalizations.of(context).delete_game_title),
content: Text(
AppLocalizations.of(context).delete_game_message(gameTitle)),
title: Text(
AppLocalizations.of(context).delete_game_title,
),
content: Text(AppLocalizations.of(context)
.delete_game_message(gameTitle)),
actions: [
CupertinoDialogAction(
onPressed: () {
Navigator.pop(context, false);
Navigator.of(context).pop(false);
},
child: Text(AppLocalizations.of(context).cancel),
),
CupertinoDialogAction(
isDestructiveAction: true,
isDefaultAction: true,
onPressed: () {
Navigator.pop(context, true);
Navigator.of(context).pop(true);
},
child: Text(
AppLocalizations.of(context).delete,
style: const TextStyle(
fontWeight: FontWeight.bold, color: Colors.red),
),
),
],
);
)
]);
},
) ??
false;
return shouldDelete;
}
/// Shows a dialog asking the user if they like the app.
/// Returns the user's decision as an integer.
/// - PRE_RATING_DIALOG_YES: User likes the app and wants to rate it.
/// - PRE_RATING_DIALOG_NO: User does not like the app and wants to provide feedback.
/// - PRE_RATING_DIALOG_CANCEL: User cancels the dialog.
Future<PreRatingDialogDecision> _showPreRatingDialog(
BuildContext context) async {
return await showCupertinoDialog<PreRatingDialogDecision>(
context: context,
builder: (BuildContext context) => CupertinoAlertDialog(
title: Text(AppLocalizations.of(context).pre_rating_title),
content:
Text(AppLocalizations.of(context).pre_rating_message),
actions: [
CupertinoDialogAction(
onPressed: () => Navigator.of(context)
.pop(PreRatingDialogDecision.yes),
isDefaultAction: true,
child: Text(AppLocalizations.of(context).yes),
),
CupertinoDialogAction(
onPressed: () =>
Navigator.of(context).pop(PreRatingDialogDecision.no),
child: Text(AppLocalizations.of(context).no),
),
CupertinoDialogAction(
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: Text(AppLocalizations.of(context).cancel),
)
],
)) ??
PreRatingDialogDecision.cancel;
}
/// Shows a dialog asking the user for feedback if they do not like the app.
/// Returns the user's decision as an integer.
/// - BAD_RATING_DIALOG_EMAIL: User wants to send an email with feedback.
/// - BAD_RATING_DIALOG_CANCEL: User cancels the dialog.
Future<BadRatingDialogDecision> _showBadRatingDialog(
BuildContext context) async {
return await showCupertinoDialog<BadRatingDialogDecision>(
context: context,
builder: (BuildContext context) => CupertinoAlertDialog(
title: Text(AppLocalizations.of(context).bad_rating_title),
content:
Text(AppLocalizations.of(context).bad_rating_message),
actions: [
CupertinoDialogAction(
isDefaultAction: true,
onPressed: () => Navigator.of(context)
.pop(BadRatingDialogDecision.email),
child: Text(AppLocalizations.of(context).contact_email),
),
CupertinoDialogAction(
isDestructiveAction: true,
onPressed: () => Navigator.of(context).pop(),
child: Text(AppLocalizations.of(context).cancel))
],
)) ??
BadRatingDialogDecision.cancel;
}
@override

View File

@@ -1,5 +1,5 @@
import 'package:cabo_counter/l10n/app_localizations.dart';
import 'package:cabo_counter/utility/custom_theme.dart';
import 'package:cabo_counter/core/custom_theme.dart';
import 'package:cabo_counter/l10n/generated/app_localizations.dart';
import 'package:flutter/cupertino.dart';
class ModeSelectionMenu extends StatelessWidget {

View File

@@ -1,7 +1,7 @@
import 'package:cabo_counter/core/custom_theme.dart';
import 'package:cabo_counter/data/game_session.dart';
import 'package:cabo_counter/l10n/app_localizations.dart';
import 'package:cabo_counter/l10n/generated/app_localizations.dart';
import 'package:cabo_counter/services/local_storage_service.dart';
import 'package:cabo_counter/utility/custom_theme.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';

View File

@@ -1,10 +1,11 @@
import 'package:cabo_counter/l10n/app_localizations.dart';
import 'package:cabo_counter/core/constants.dart';
import 'package:cabo_counter/core/custom_theme.dart';
import 'package:cabo_counter/l10n/generated/app_localizations.dart';
import 'package:cabo_counter/presentation/widgets/custom_form_row.dart';
import 'package:cabo_counter/presentation/widgets/stepper.dart';
import 'package:cabo_counter/presentation/widgets/custom_stepper.dart';
import 'package:cabo_counter/services/config_service.dart';
import 'package:cabo_counter/services/local_storage_service.dart';
import 'package:cabo_counter/services/version_service.dart';
import 'package:cabo_counter/utility/custom_theme.dart';
import 'package:flutter/cupertino.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -53,7 +54,7 @@ class _SettingsViewState extends State<SettingsView> {
CustomFormRow(
prefixText: 'Cabo-Strafe',
prefixIcon: CupertinoIcons.bolt_fill,
suffixWidget: Stepper(
suffixWidget: CustomStepper(
key: _stepperKey1,
initialValue: ConfigService.caboPenalty,
minValue: 0,
@@ -70,7 +71,7 @@ class _SettingsViewState extends State<SettingsView> {
CustomFormRow(
prefixText: 'Punkte-Limit',
prefixIcon: FontAwesomeIcons.bullseye,
suffixWidget: Stepper(
suffixWidget: CustomStepper(
key: _stepperKey2,
initialValue: ConfigService.pointLimit,
minValue: 30,
@@ -93,7 +94,6 @@ class _SettingsViewState extends State<SettingsView> {
setState(() {
_stepperKey1 = UniqueKey();
_stepperKey2 = UniqueKey();
print('Config reset to default');
});
},
)
@@ -142,17 +142,25 @@ class _SettingsViewState extends State<SettingsView> {
margin: EdgeInsets.zero,
children: [
CustomFormRow(
prefixText: AppLocalizations.of(context).create_issue,
prefixIcon: FontAwesomeIcons.github,
onPressed: () => launchUrl(Uri.parse(
'https://github.com/flixcoo/Cabo-Counter/issues')),
prefixText: AppLocalizations.of(context).wiki,
prefixIcon: CupertinoIcons.book,
onPressed: () =>
launchUrl(Uri.parse(Constants.GITHUB_WIKI_LINK)),
suffixWidget: const CupertinoListTileChevron(),
),
CustomFormRow(
prefixText: AppLocalizations.of(context).wiki,
prefixIcon: CupertinoIcons.book,
onPressed: () => launchUrl(Uri.parse(
'https://github.com/flixcoo/Cabo-Counter/wiki')),
prefixText:
AppLocalizations.of(context).privacy_policy,
prefixIcon: CupertinoIcons.doc_append,
onPressed: () => launchUrl(
Uri.parse(Constants.PRIVACY_POLICY_LINK)),
suffixWidget: const CupertinoListTileChevron(),
),
CustomFormRow(
prefixText: AppLocalizations.of(context).error_found,
prefixIcon: FontAwesomeIcons.github,
onPressed: () => launchUrl(
Uri.parse(Constants.GITHUB_ISSUES_LINK)),
suffixWidget: const CupertinoListTileChevron(),
),
CustomFormRow(

View File

@@ -1,7 +1,7 @@
import 'package:cabo_counter/l10n/app_localizations.dart';
import 'package:cabo_counter/presentation/views/information_view.dart';
import 'package:cabo_counter/core/custom_theme.dart';
import 'package:cabo_counter/l10n/generated/app_localizations.dart';
import 'package:cabo_counter/presentation/views/about_view.dart';
import 'package:cabo_counter/presentation/views/main_menu_view.dart';
import 'package:cabo_counter/utility/custom_theme.dart';
import 'package:flutter/cupertino.dart';
class TabView extends StatefulWidget {
@@ -39,7 +39,7 @@ class _TabViewState extends State<TabView> {
if (index == 0) {
return const MainMenuView();
} else {
return const InformationView();
return const AboutView();
}
});
},

View File

@@ -1,5 +1,5 @@
import 'package:cabo_counter/presentation/widgets/stepper.dart';
import 'package:cabo_counter/utility/custom_theme.dart';
import 'package:cabo_counter/core/custom_theme.dart';
import 'package:cabo_counter/presentation/widgets/custom_stepper.dart';
import 'package:flutter/cupertino.dart';
class CustomFormRow extends StatefulWidget {
@@ -43,7 +43,7 @@ class _CustomFormRowState extends State<CustomFormRow> {
Text(widget.prefixText),
],
),
padding: suffixWidget is Stepper
padding: suffixWidget is CustomStepper
? const EdgeInsets.fromLTRB(15, 0, 0, 0)
: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
child: suffixWidget,

View File

@@ -1,13 +1,13 @@
import 'package:cabo_counter/utility/custom_theme.dart';
import 'package:cabo_counter/core/custom_theme.dart';
import 'package:flutter/cupertino.dart'; // Für iOS-Style
class Stepper extends StatefulWidget {
class CustomStepper extends StatefulWidget {
final int minValue;
final int maxValue;
final int? initialValue;
final int step;
final ValueChanged<int> onChanged;
const Stepper({
const CustomStepper({
super.key,
required this.minValue,
required this.maxValue,
@@ -18,10 +18,10 @@ class Stepper extends StatefulWidget {
@override
// ignore: library_private_types_in_public_api
_StepperState createState() => _StepperState();
_CustomStepperState createState() => _CustomStepperState();
}
class _StepperState extends State<Stepper> {
class _CustomStepperState extends State<CustomStepper> {
late int _value;
@override

View File

@@ -1,4 +1,4 @@
import 'package:cabo_counter/utility/globals.dart';
import 'package:cabo_counter/core/constants.dart';
import 'package:package_info_plus/package_info_plus.dart';
class VersionService {
@@ -19,7 +19,7 @@ class VersionService {
if (_version == '-.-.-') {
return getVersionNumber();
}
return '${Globals.appDevPhase} $_version';
return '${Constants.appDevPhase} $_version';
}
static String getBuildNumber() {

View File

@@ -1,3 +0,0 @@
class Globals {
static String appDevPhase = 'Beta';
}

View File

@@ -2,7 +2,7 @@ name: cabo_counter
description: "Mobile app for the card game Cabo"
publish_to: 'none'
version: 0.4.0+382
version: 0.4.0+467
environment:
sdk: ^3.5.4
@@ -27,6 +27,7 @@ dependencies:
intl: any
syncfusion_flutter_charts: ^30.1.37
uuid: ^4.5.1
rate_my_app: ^2.3.2
dev_dependencies:
flutter_test: