Merge pull request #19 from flixcoo/feature/5-implementing-tabs-in-home-view

feature/5-implementing-tabs-in-home-view
This commit is contained in:
2025-05-02 23:44:18 +02:00
committed by GitHub
11 changed files with 248 additions and 169 deletions

View File

@@ -1,8 +1,8 @@
import 'package:cabo_counter/data/game_session.dart';
import 'package:cabo_counter/utility/apptheme.dart';
import 'package:cabo_counter/utility/custom_theme.dart';
import 'package:cabo_counter/utility/globals.dart';
import 'package:cabo_counter/utility/local_storage_service.dart';
import 'package:cabo_counter/views/main_menu_view.dart';
import 'package:cabo_counter/views/tab_view.dart';
import 'package:flutter/cupertino.dart';
void main() {
@@ -81,15 +81,15 @@ class _AppState extends State<App> with WidgetsBindingObserver {
return CupertinoApp(
theme: CupertinoThemeData(
brightness: Brightness.dark,
primaryColor: AppTheme.primaryColor,
scaffoldBackgroundColor: AppTheme.backgroundColor,
primaryColor: CustomTheme.primaryColor,
scaffoldBackgroundColor: CustomTheme.backgroundColor,
textTheme: CupertinoTextThemeData(
primaryColor: AppTheme.primaryColor,
primaryColor: CustomTheme.primaryColor,
),
),
debugShowCheckedModeBanner: false,
title: 'Cabo Counter',
home: const MainMenuView(),
home: const TabView(),
);
}
}

View File

@@ -1,6 +1,6 @@
import 'package:flutter/cupertino.dart';
class AppTheme {
class CustomTheme {
static Color white = CupertinoColors.white;
static Color primaryColor = CupertinoColors.systemGreen;
static Color backgroundColor = const Color(0xFF101010);

View File

@@ -1,5 +1,5 @@
import 'package:cabo_counter/data/game_session.dart';
import 'package:cabo_counter/utility/apptheme.dart';
import 'package:cabo_counter/utility/custom_theme.dart';
import 'package:cabo_counter/views/round_view.dart';
import 'package:flutter/cupertino.dart';
@@ -28,7 +28,7 @@ class _ActiveGameViewState extends State<ActiveGameView> {
padding: const EdgeInsets.fromLTRB(10, 10, 0, 0),
child: Text(
'Spieler:innen',
style: AppTheme.createGameTitle,
style: CustomTheme.createGameTitle,
),
),
ListView.builder(
@@ -61,7 +61,7 @@ class _ActiveGameViewState extends State<ActiveGameView> {
padding: const EdgeInsets.fromLTRB(10, 10, 0, 0),
child: Text(
'Runden',
style: AppTheme.createGameTitle,
style: CustomTheme.createGameTitle,
),
),
ListView.builder(
@@ -82,8 +82,9 @@ class _ActiveGameViewState extends State<ActiveGameView> {
style: TextStyle(fontSize: 22)),
onTap: () async {
// ignore: unused_local_variable
final val = await Navigator.push(
context,
final val =
await Navigator.of(context, rootNavigator: true)
.push(
CupertinoPageRoute(
fullscreenDialog: true,
builder: (context) => RoundView(

View File

@@ -1,5 +1,5 @@
import 'package:cabo_counter/data/game_session.dart';
import 'package:cabo_counter/utility/apptheme.dart';
import 'package:cabo_counter/utility/custom_theme.dart';
import 'package:cabo_counter/utility/globals.dart';
import 'package:cabo_counter/utility/local_storage_service.dart';
import 'package:cabo_counter/views/active_game_view.dart';
@@ -44,7 +44,7 @@ class _CreateGameState extends State<CreateGame> {
padding: const EdgeInsets.fromLTRB(10, 10, 0, 0),
child: Text(
'Spiel',
style: AppTheme.createGameTitle,
style: CustomTheme.createGameTitle,
),
),
Padding(
@@ -98,7 +98,7 @@ class _CreateGameState extends State<CreateGame> {
padding: const EdgeInsets.fromLTRB(10, 10, 0, 0),
child: Text(
'Spieler:innen',
style: AppTheme.createGameTitle,
style: CustomTheme.createGameTitle,
),
),
Expanded(

View File

@@ -2,16 +2,11 @@ import 'package:cabo_counter/utility/local_storage_service.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher.dart';
class InformationView extends StatelessWidget {
const InformationView({super.key});
Future<PackageInfo> _getPackageInfo() async {
return await PackageInfo.fromPlatform();
}
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
@@ -20,141 +15,112 @@ class InformationView extends StatelessWidget {
middle: Text('Über'),
),
child: SafeArea(
child: Stack(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Padding(
padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
child: Text(
'Cabo Counter',
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
const Padding(
padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
child: Text(
'Cabo Counter',
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 10),
child: SizedBox(
height: 200,
child:
Image.asset('assets/cabo-counter-logo_rounded.png'),
)),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 30),
child: Text(
'Hey :) Danke, dass du als eine:r der ersten User '
'meiner ersten eigenen App dabei bist! Ich hab sehr '
'viel Arbeit in dieses Projekt gesteckt und auch, '
'wenn ich (hoffentlich) an vieles Gedacht hab, wird '
'auf jeden Fall noch nicht alles 100% funktionieren. '
'Solltest du also irgendwelche Fehler entdecken oder '
'Feedback zum Design oder der Benutzerfreundlichkeit'
' haben, zögere bitte nicht sie mir auf den dir '
'bekannten Wegen mitzuteilen. Danke! ',
textAlign: TextAlign.center,
softWrap: true,
)),
const SizedBox(
height: 30,
),
const Text(
'\u00A9 Felix Kirchner',
style: TextStyle(fontSize: 16),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
onPressed: () => launchUrl(
Uri.parse('https://www.instagram.com/fx.kr')),
icon: const Icon(FontAwesomeIcons.instagram)),
IconButton(
onPressed: () => launchUrl(
Uri.parse('mailto:felix.kirchner.fk@gmail.com')),
icon: const Icon(CupertinoIcons.envelope)),
IconButton(
onPressed: () => launchUrl(
Uri.parse('https://www.github.com/flixcoo')),
icon: const Icon(FontAwesomeIcons.github)),
],
),
CupertinoButton(
sizeStyle: CupertinoButtonSize.medium,
child: const Text('Spieldaten exportieren'),
onPressed: () async {
final success = await LocalStorageService.exportJsonFile();
if (!success && context.mounted) {
showCupertinoDialog(
context: context,
builder: (context) => CupertinoAlertDialog(
title: const Text('Fehler'),
content: const Text(
'Datei konnte nicht exportiert werden.'),
actions: [
CupertinoDialogAction(
child: const Text('OK'),
onPressed: () => Navigator.pop(context),
),
],
),
);
}
},
),
CupertinoButton(
sizeStyle: CupertinoButtonSize.medium,
child: const Text('Spieldaten importieren'),
onPressed: () async {
final success =
await LocalStorageService.importJsonFile();
if (!success && context.mounted) {
showCupertinoDialog(
context: context,
builder: (context) => CupertinoAlertDialog(
title: const Text('Fehler'),
content: const Text(
'Datei konnte nicht importiert werden.'),
actions: [
CupertinoDialogAction(
child: const Text('OK'),
onPressed: () => Navigator.pop(context),
),
],
));
}
}),
],
),
Positioned(
bottom: 30,
left: 0,
right: 0,
child: FutureBuilder<PackageInfo>(
future: _getPackageInfo(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(
'Alpha ${snapshot.data!.version} '
'(Build ${snapshot.data!.buildNumber})',
textAlign: TextAlign.center,
);
} else if (snapshot.hasError) {
return const Text(
'App-Version -.-.- (Build -)',
textAlign: TextAlign.center,
);
}
return const Text(
'Lade Version...',
textAlign: TextAlign.center,
);
},
),
),
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: SizedBox(
height: 200,
child: Image.asset('assets/cabo-counter-logo_rounded.png'),
)),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 30),
child: Text(
'Hey :) Danke, dass du als eine:r der ersten User '
'meiner ersten eigenen App dabei bist! Ich hab sehr '
'viel Arbeit in dieses Projekt gesteckt und auch, '
'wenn ich (hoffentlich) an vieles Gedacht hab, wird '
'auf jeden Fall noch nicht alles 100% funktionieren. '
'Solltest du also irgendwelche Fehler entdecken oder '
'Feedback zum Design oder der Benutzerfreundlichkeit'
' haben, zögere bitte nicht sie mir auf den dir '
'bekannten Wegen mitzuteilen. Danke! ',
textAlign: TextAlign.center,
softWrap: true,
)),
const SizedBox(
height: 15,
),
const Text(
'\u00A9 Felix Kirchner',
style: TextStyle(fontSize: 16),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
onPressed: () =>
launchUrl(Uri.parse('https://www.instagram.com/fx.kr')),
icon: const Icon(FontAwesomeIcons.instagram)),
IconButton(
onPressed: () => launchUrl(
Uri.parse('mailto:felix.kirchner.fk@gmail.com')),
icon: const Icon(CupertinoIcons.envelope)),
IconButton(
onPressed: () =>
launchUrl(Uri.parse('https://www.github.com/flixcoo')),
icon: const Icon(FontAwesomeIcons.github)),
],
),
const SizedBox(
height: 10,
),
CupertinoButton(
sizeStyle: CupertinoButtonSize.medium,
child: const Text('Spieldaten exportieren'),
onPressed: () async {
final success = await LocalStorageService.exportJsonFile();
if (!success && context.mounted) {
showCupertinoDialog(
context: context,
builder: (context) => CupertinoAlertDialog(
title: const Text('Fehler'),
content:
const Text('Datei konnte nicht exportiert werden.'),
actions: [
CupertinoDialogAction(
child: const Text('OK'),
onPressed: () => Navigator.pop(context),
),
],
),
);
}
},
),
CupertinoButton(
sizeStyle: CupertinoButtonSize.medium,
child: const Text('Spieldaten importieren'),
onPressed: () async {
final success = await LocalStorageService.importJsonFile();
if (!success && context.mounted) {
showCupertinoDialog(
context: context,
builder: (context) => CupertinoAlertDialog(
title: const Text('Fehler'),
content: const Text(
'Datei konnte nicht importiert werden.'),
actions: [
CupertinoDialogAction(
child: const Text('OK'),
onPressed: () => Navigator.pop(context),
),
],
));
}
}),
],
)));
}

View File

@@ -1,9 +1,9 @@
import 'package:cabo_counter/utility/apptheme.dart';
import 'package:cabo_counter/utility/custom_theme.dart';
import 'package:cabo_counter/utility/globals.dart';
import 'package:cabo_counter/utility/local_storage_service.dart';
import 'package:cabo_counter/views/active_game_view.dart';
import 'package:cabo_counter/views/create_game_view.dart';
import 'package:cabo_counter/views/information_view.dart';
import 'package:cabo_counter/views/settings_view.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@@ -35,12 +35,12 @@ class _MainMenuViewState extends State<MainMenuView> {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => const InformationView(),
builder: (context) => const SettingsView(),
),
);
},
icon: const Icon(
CupertinoIcons.info_circle,
CupertinoIcons.settings,
size: 30,
)),
middle: const Text('Cabo Counter'),
@@ -69,7 +69,7 @@ class _MainMenuViewState extends State<MainMenuView> {
child: Icon(
CupertinoIcons.plus,
size: 60,
color: AppTheme.primaryColor,
color: CustomTheme.primaryColor,
),
)),
const SizedBox(height: 10), // Abstand von oben

View File

@@ -1,4 +1,4 @@
import 'package:cabo_counter/utility/apptheme.dart';
import 'package:cabo_counter/utility/custom_theme.dart';
import 'package:flutter/cupertino.dart';
class ModeSelectionMenu extends StatelessWidget {
@@ -15,10 +15,10 @@ class ModeSelectionMenu extends StatelessWidget {
Padding(
padding: const EdgeInsets.fromLTRB(0, 16, 0, 0),
child: CupertinoListTile(
title: Text('101 Punkte', style: AppTheme.modeTitle),
title: Text('101 Punkte', style: CustomTheme.modeTitle),
subtitle: const Text(
'Es wird solange gespielt, bis einer Spieler mehr als 100 Punkte erreicht',
style: AppTheme.modeDescription,
style: CustomTheme.modeDescription,
maxLines: 3,
),
onTap: () {
@@ -29,11 +29,11 @@ class ModeSelectionMenu extends StatelessWidget {
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: CupertinoListTile(
title: Text('Unbegrenzt', style: AppTheme.modeTitle),
title: Text('Unbegrenzt', style: CustomTheme.modeTitle),
subtitle: const Text(
'Dem Spiel sind keine Grenzen gesetzt. Es wird so lange '
'gespielt, bis Ihr keine Lust mehr habt.',
style: AppTheme.modeDescription,
style: CustomTheme.modeDescription,
maxLines: 3,
),
onTap: () {

View File

@@ -1,5 +1,5 @@
import 'package:cabo_counter/data/game_session.dart';
import 'package:cabo_counter/utility/apptheme.dart';
import 'package:cabo_counter/utility/custom_theme.dart';
import 'package:cabo_counter/utility/local_storage_service.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
@@ -90,7 +90,7 @@ class _RoundViewState extends State<RoundView> {
children: [
const SizedBox(height: 40),
Text('Runde ${widget.roundNumber}',
style: AppTheme.roundTitle),
style: CustomTheme.roundTitle),
const SizedBox(height: 10),
const Text(
'Wer hat CABO gesagt?',
@@ -105,8 +105,8 @@ class _RoundViewState extends State<RoundView> {
child: SizedBox(
height: 40,
child: CupertinoSegmentedControl<int>(
unselectedColor: AppTheme.backgroundTintColor,
selectedColor: AppTheme.primaryColor,
unselectedColor: CustomTheme.backgroundTintColor,
selectedColor: CustomTheme.primaryColor,
groupValue: _caboPlayerIndex,
children: Map.fromEntries(widget.gameSession.players
.asMap()
@@ -271,7 +271,7 @@ class _RoundViewState extends State<RoundView> {
return Container(
height: 80,
padding: const EdgeInsets.only(bottom: 20),
color: AppTheme.backgroundTintColor,
color: CustomTheme.backgroundTintColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
@@ -293,8 +293,8 @@ class _RoundViewState extends State<RoundView> {
if (widget.gameSession.isGameFinished == true) {
Navigator.pop(context, widget.gameSession);
} else {
Navigator.pushReplacement(
context,
Navigator.of(context, rootNavigator: true)
.pushReplacement(
CupertinoPageRoute(
builder: (context) => RoundView(
gameSession: widget.gameSession,

View File

@@ -0,0 +1,65 @@
import 'package:flutter/cupertino.dart';
import 'package:package_info_plus/package_info_plus.dart';
class SettingsView extends StatefulWidget {
const SettingsView({super.key});
@override
State<SettingsView> createState() => _SettingsViewState();
}
class _SettingsViewState extends State<SettingsView> {
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Einstellungen'),
),
child: SafeArea(
child: Stack(
children: [
const Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Icon(
CupertinoIcons.settings,
size: 100,
)),
],
),
Positioned(
bottom: 30,
left: 0,
right: 0,
child: FutureBuilder<PackageInfo>(
future: _getPackageInfo(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(
'Alpha ${snapshot.data!.version} '
'(Build ${snapshot.data!.buildNumber})',
textAlign: TextAlign.center,
);
} else if (snapshot.hasError) {
return const Text(
'App-Version -.-.- (Build -)',
textAlign: TextAlign.center,
);
}
return const Text(
'Lade Version...',
textAlign: TextAlign.center,
);
},
)),
],
)),
);
}
Future<PackageInfo> _getPackageInfo() async {
return await PackageInfo.fromPlatform();
}
}

47
lib/views/tab_view.dart Normal file
View File

@@ -0,0 +1,47 @@
import 'package:cabo_counter/utility/custom_theme.dart';
import 'package:cabo_counter/views/information_view.dart';
import 'package:cabo_counter/views/main_menu_view.dart';
import 'package:flutter/cupertino.dart';
class TabView extends StatefulWidget {
const TabView({super.key});
@override
// ignore: library_private_types_in_public_api
_TabViewState createState() => _TabViewState();
}
class _TabViewState extends State<TabView> {
@override
Widget build(BuildContext context) {
return CupertinoTabScaffold(
tabBar: CupertinoTabBar(
backgroundColor: CustomTheme.backgroundTintColor,
iconSize: 27,
height: 55,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(
CupertinoIcons.house_fill,
),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(
CupertinoIcons.info,
),
label: 'About',
),
]),
tabBuilder: (BuildContext context, int index) {
return CupertinoTabView(builder: (BuildContext context) {
if (index == 0) {
return const MainMenuView();
} else {
return const InformationView();
}
});
},
);
}
}

View File

@@ -2,7 +2,7 @@ name: cabo_counter
description: "Mobile app for the card game Cabo"
publish_to: 'none'
version: 0.1.5+110
version: 0.1.5+113
environment:
sdk: ^3.5.4