added 2 themes

This commit is contained in:
2026-03-12 11:45:59 +01:00
parent 0658b0cd5b
commit bd56d97b6d
6 changed files with 225 additions and 76 deletions

View File

@@ -77,3 +77,17 @@ Folgende Dateien wurden in diesem Prompt verändert:
- Show a placeholder in the carousel if no tournaments were found.<br><br> - Show a placeholder in the carousel if no tournaments were found.<br><br>
Folgende Dateien wurden in diesem Prompt verändert: Folgende Dateien wurden in diesem Prompt verändert:
- frontend_splatournament_manager/lib/widgets/my_tournaments_carousel.dart - frontend_splatournament_manager/lib/widgets/my_tournaments_carousel.dart
- Add 2 more themes that change the colors of the application, one light and one dark.<br><br>
Folgende Dateien wurden in diesem Prompt verändert:
- frontend_splatournament_manager/lib/providers/theme_provider.dart
- frontend_splatournament_manager/lib/main.dart
- frontend_splatournament_manager/lib/widgets/theme_selector_widget.dart
- Make the background color of the tournament detail page semi-transparent and keep the title text legible.<br><br>
Folgende Dateien wurden in diesem Prompt verändert:
- frontend_splatournament_manager/lib/pages/tournament_detail_page.dart
- In the Teams view, fix the upper TabBar text so selected and non-selected labels are readable.<br><br>
Folgende Dateien wurden in diesem Prompt verändert:
- frontend_splatournament_manager/lib/pages/home_page.dart

View File

@@ -32,15 +32,9 @@ class SplatournamentApp extends StatelessWidget {
return MaterialApp.router( return MaterialApp.router(
title: 'Splatournament Manager', title: 'Splatournament Manager',
routerConfig: routes, routerConfig: routes,
themeMode: themeProvider.theme, themeMode: themeProvider.themeMode,
theme: ThemeData( theme: themeProvider.lightTheme,
brightness: Brightness.light, darkTheme: themeProvider.darkTheme,
primarySwatch: Colors.blue,
),
darkTheme: ThemeData(
brightness: Brightness.dark,
primarySwatch: Colors.deepPurple,
),
); );
} }
} }

View File

@@ -17,7 +17,8 @@ class HomePage extends StatefulWidget {
State<HomePage> createState() => _HomePageState(); State<HomePage> createState() => _HomePageState();
} }
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin { class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
int _selectedIndex = 0; int _selectedIndex = 0;
late TabController _tabController; late TabController _tabController;
@@ -35,12 +36,23 @@ class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final appBarForeground =
Theme.of(context).appBarTheme.foregroundColor ??
Theme.of(context).colorScheme.onSurface;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(_selectedIndex == 0 ? "Tournaments" : "Teams"), title: Text(_selectedIndex == 0 ? "Tournaments" : "Teams"),
bottom: _selectedIndex == 1 bottom: _selectedIndex == 1
? TabBar( ? TabBar(
controller: _tabController, controller: _tabController,
labelColor: appBarForeground,
unselectedLabelColor: appBarForeground.withValues(alpha: 0.82),
indicatorColor: appBarForeground,
labelStyle: const TextStyle(fontWeight: FontWeight.w700),
unselectedLabelStyle: const TextStyle(
fontWeight: FontWeight.w500,
),
tabs: const [ tabs: const [
Tab(text: 'All Teams'), Tab(text: 'All Teams'),
Tab(text: 'My Teams'), Tab(text: 'My Teams'),
@@ -101,10 +113,7 @@ class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin
// Teams View with tabs // Teams View with tabs
TabBarView( TabBarView(
controller: _tabController, controller: _tabController,
children: const [ children: const [TeamsListWidget(), MyTeamsWidget()],
TeamsListWidget(),
MyTeamsWidget(),
],
), ),
], ],
), ),
@@ -120,10 +129,7 @@ class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin
icon: Icon(Icons.emoji_events), icon: Icon(Icons.emoji_events),
label: 'Tournaments', label: 'Tournaments',
), ),
BottomNavigationBarItem( BottomNavigationBarItem(icon: Icon(Icons.groups), label: 'Teams'),
icon: Icon(Icons.groups),
label: 'Teams',
),
], ],
), ),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
@@ -138,9 +144,7 @@ class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin
} else { } else {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(builder: (context) => const CreateTeamPage()),
builder: (context) => const CreateTeamPage(),
),
); );
} }
}, },

View File

@@ -26,7 +26,11 @@ class _TournamentDetailPageState extends State<TournamentDetailPage> {
if (teams.isEmpty) { if (teams.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('You are not a member of any team. Join or create a team first!')), const SnackBar(
content: Text(
'You are not a member of any team. Join or create a team first!',
),
),
); );
return; return;
} }
@@ -87,25 +91,34 @@ class _TournamentDetailPageState extends State<TournamentDetailPage> {
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text('Failed to join: ${e.toString().replaceAll('Exception: ', '')}'), content: Text(
'Failed to join: ${e.toString().replaceAll('Exception: ', '')}',
),
), ),
); );
} }
} }
} catch (e) { } catch (e) {
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(
SnackBar(content: Text('Failed to load your teams: $e')), context,
); ).showSnackBar(SnackBar(content: Text('Failed to load your teams: $e')));
} }
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final appBarBackground = colorScheme.surface.withValues(alpha: 0.78);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(widget.tournament.name, style: TextStyle(overflow: TextOverflow.ellipsis)), backgroundColor: appBarBackground,
backgroundColor: Theme.of(context).colorScheme.surface.withAlpha(180), foregroundColor: colorScheme.onSurface,
title: Text(
widget.tournament.name,
style: TextStyle(overflow: TextOverflow.ellipsis),
),
elevation: 3, elevation: 3,
actions: [ actions: [
IconButton( IconButton(

View File

@@ -1,11 +1,50 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
class ThemeProvider extends ChangeNotifier { enum AppThemeOption { lightBlue, darkPurple, lightMint, darkAmber, system }
static const String _themeKey = 'theme_mode';
ThemeMode _themeMode = ThemeMode.system;
ThemeMode get theme => _themeMode; class ThemeProvider extends ChangeNotifier {
static const String _themeKey = 'theme_option';
AppThemeOption _selectedTheme = AppThemeOption.system;
AppThemeOption get selectedTheme => _selectedTheme;
ThemeMode get themeMode {
switch (_selectedTheme) {
case AppThemeOption.lightBlue:
case AppThemeOption.lightMint:
return ThemeMode.light;
case AppThemeOption.darkPurple:
case AppThemeOption.darkAmber:
return ThemeMode.dark;
case AppThemeOption.system:
return ThemeMode.system;
}
}
ThemeData get lightTheme {
switch (_selectedTheme) {
case AppThemeOption.lightMint:
return _mintLightTheme;
case AppThemeOption.lightBlue:
case AppThemeOption.darkPurple:
case AppThemeOption.darkAmber:
case AppThemeOption.system:
return _blueLightTheme;
}
}
ThemeData get darkTheme {
switch (_selectedTheme) {
case AppThemeOption.darkAmber:
return _amberDarkTheme;
case AppThemeOption.lightBlue:
case AppThemeOption.darkPurple:
case AppThemeOption.lightMint:
case AppThemeOption.system:
return _purpleDarkTheme;
}
}
ThemeProvider() { ThemeProvider() {
_loadTheme(); _loadTheme();
@@ -14,38 +53,116 @@ class ThemeProvider extends ChangeNotifier {
Future<void> _loadTheme() async { Future<void> _loadTheme() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
final themeName = prefs.getString(_themeKey) ?? 'system'; final themeName = prefs.getString(_themeKey) ?? 'system';
_themeMode = _themeFromString(themeName); _selectedTheme = _themeFromString(themeName);
notifyListeners(); notifyListeners();
} }
Future<void> setTheme(ThemeMode mode) async { Future<void> setTheme(AppThemeOption option) async {
_themeMode = mode; _selectedTheme = option;
notifyListeners(); notifyListeners();
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
await prefs.setString(_themeKey, _themeToString(mode)); await prefs.setString(_themeKey, _themeToString(option));
} }
String _themeToString(ThemeMode mode) { String _themeToString(AppThemeOption option) {
switch (mode) { switch (option) {
case ThemeMode.light: case AppThemeOption.lightBlue:
return 'light'; return 'light_blue';
case ThemeMode.dark: case AppThemeOption.darkPurple:
return 'dark'; return 'dark_purple';
case ThemeMode.system: case AppThemeOption.lightMint:
return 'light_mint';
case AppThemeOption.darkAmber:
return 'dark_amber';
case AppThemeOption.system:
return 'system'; return 'system';
} }
} }
ThemeMode _themeFromString(String theme) { AppThemeOption _themeFromString(String theme) {
switch (theme) { switch (theme) {
case 'light': case 'light':
return ThemeMode.light; case 'light_blue':
return AppThemeOption.lightBlue;
case 'dark': case 'dark':
return ThemeMode.dark; case 'dark_purple':
return AppThemeOption.darkPurple;
case 'light_mint':
return AppThemeOption.lightMint;
case 'dark_amber':
return AppThemeOption.darkAmber;
case 'system': case 'system':
default: default:
return ThemeMode.system; return AppThemeOption.system;
} }
} }
ThemeData get _blueLightTheme {
final scheme = ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.light,
);
return ThemeData(
colorScheme: scheme,
useMaterial3: true,
appBarTheme: AppBarTheme(
backgroundColor: scheme.primary,
foregroundColor: scheme.onPrimary,
),
);
}
ThemeData get _purpleDarkTheme {
final scheme = ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.dark,
);
return ThemeData(
colorScheme: scheme,
useMaterial3: true,
appBarTheme: AppBarTheme(
backgroundColor: scheme.surface,
foregroundColor: scheme.onSurface,
),
);
}
ThemeData get _mintLightTheme {
final scheme = ColorScheme.fromSeed(
seedColor: const Color(0xFF00897B),
brightness: Brightness.light,
);
return ThemeData(
colorScheme: scheme,
useMaterial3: true,
appBarTheme: AppBarTheme(
backgroundColor: scheme.primary,
foregroundColor: scheme.onPrimary,
),
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: scheme.secondary,
foregroundColor: scheme.onSecondary,
),
);
}
ThemeData get _amberDarkTheme {
final scheme = ColorScheme.fromSeed(
seedColor: const Color(0xFFFF8F00),
brightness: Brightness.dark,
);
return ThemeData(
colorScheme: scheme,
useMaterial3: true,
appBarTheme: AppBarTheme(
backgroundColor: scheme.surface,
foregroundColor: scheme.onSurface,
),
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: scheme.primary,
foregroundColor: scheme.onPrimary,
),
);
}
} }

View File

@@ -6,26 +6,34 @@ import '../providers/theme_provider.dart';
class ThemeSelectorWidget extends StatelessWidget { class ThemeSelectorWidget extends StatelessWidget {
ThemeSelectorWidget({super.key}); ThemeSelectorWidget({super.key});
final List<DropdownMenuItem> dropdownElements = [ final List<DropdownMenuItem<AppThemeOption>> dropdownElements = [
DropdownMenuItem( const DropdownMenuItem(
value: ThemeMode.light, value: AppThemeOption.lightBlue,
child: Text("Light"), child: Text("Light Blue"),
), ),
DropdownMenuItem( const DropdownMenuItem(
value: ThemeMode.dark, value: AppThemeOption.darkPurple,
child: Text("Dark"), child: Text("Dark Purple"),
), ),
DropdownMenuItem( const DropdownMenuItem(
value: ThemeMode.system, value: AppThemeOption.lightMint,
child: Text("System"), child: Text("Light Mint"),
), ),
const DropdownMenuItem(
value: AppThemeOption.darkAmber,
child: Text("Dark Amber"),
),
const DropdownMenuItem(value: AppThemeOption.system, child: Text("System")),
]; ];
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final themeProvider = Provider.of<ThemeProvider>(context); final themeProvider = Provider.of<ThemeProvider>(context);
return Container( return Container(
decoration: BoxDecoration(color: Theme.of(context).hoverColor, borderRadius: BorderRadius.circular(8)), decoration: BoxDecoration(
color: Theme.of(context).hoverColor,
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.all(8.0), margin: const EdgeInsets.all(8.0),
child: Row( child: Row(
@@ -34,10 +42,10 @@ class ThemeSelectorWidget extends StatelessWidget {
Text("Theme"), Text("Theme"),
SizedBox( SizedBox(
width: 250, width: 250,
child: DropdownButtonFormField( child: DropdownButtonFormField<AppThemeOption>(
icon: Icon(Icons.color_lens), icon: const Icon(Icons.color_lens),
items: dropdownElements, items: dropdownElements,
initialValue: themeProvider.theme, initialValue: themeProvider.selectedTheme,
onChanged: (value) { onChanged: (value) {
if (value == null) return; if (value == null) return;
themeProvider.setTheme(value); themeProvider.setTheme(value);
@@ -48,5 +56,4 @@ class ThemeSelectorWidget extends StatelessWidget {
), ),
); );
} }
} }