added 2 themes
This commit is contained in:
@@ -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>
|
||||
Folgende Dateien wurden in diesem Prompt verändert:
|
||||
- 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
|
||||
|
||||
@@ -32,15 +32,9 @@ class SplatournamentApp extends StatelessWidget {
|
||||
return MaterialApp.router(
|
||||
title: 'Splatournament Manager',
|
||||
routerConfig: routes,
|
||||
themeMode: themeProvider.theme,
|
||||
theme: ThemeData(
|
||||
brightness: Brightness.light,
|
||||
primarySwatch: Colors.blue,
|
||||
),
|
||||
darkTheme: ThemeData(
|
||||
brightness: Brightness.dark,
|
||||
primarySwatch: Colors.deepPurple,
|
||||
),
|
||||
themeMode: themeProvider.themeMode,
|
||||
theme: themeProvider.lightTheme,
|
||||
darkTheme: themeProvider.darkTheme,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -50,10 +44,10 @@ final routes = GoRouter(
|
||||
redirect: (context, state) async {
|
||||
final authProvider = Provider.of<AuthProvider>(context, listen: false);
|
||||
await authProvider.checkAuthStatus();
|
||||
|
||||
|
||||
final isLoggedIn = authProvider.isLoggedIn;
|
||||
final isGoingToLogin = state.matchedLocation == '/login';
|
||||
// redirect to login
|
||||
// redirect to login
|
||||
if (!isLoggedIn && !isGoingToLogin) {
|
||||
return '/login';
|
||||
}
|
||||
@@ -61,7 +55,7 @@ final routes = GoRouter(
|
||||
if (isLoggedIn && isGoingToLogin) {
|
||||
return '/';
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
},
|
||||
routes: [
|
||||
|
||||
@@ -17,7 +17,8 @@ class HomePage extends StatefulWidget {
|
||||
State<HomePage> createState() => _HomePageState();
|
||||
}
|
||||
|
||||
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
|
||||
class _HomePageState extends State<HomePage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
int _selectedIndex = 0;
|
||||
late TabController _tabController;
|
||||
|
||||
@@ -35,12 +36,23 @@ class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final appBarForeground =
|
||||
Theme.of(context).appBarTheme.foregroundColor ??
|
||||
Theme.of(context).colorScheme.onSurface;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(_selectedIndex == 0 ? "Tournaments" : "Teams"),
|
||||
bottom: _selectedIndex == 1
|
||||
? TabBar(
|
||||
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 [
|
||||
Tab(text: 'All Teams'),
|
||||
Tab(text: 'My Teams'),
|
||||
@@ -101,10 +113,7 @@ class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin
|
||||
// Teams View with tabs
|
||||
TabBarView(
|
||||
controller: _tabController,
|
||||
children: const [
|
||||
TeamsListWidget(),
|
||||
MyTeamsWidget(),
|
||||
],
|
||||
children: const [TeamsListWidget(), MyTeamsWidget()],
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -120,10 +129,7 @@ class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin
|
||||
icon: Icon(Icons.emoji_events),
|
||||
label: 'Tournaments',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.groups),
|
||||
label: 'Teams',
|
||||
),
|
||||
BottomNavigationBarItem(icon: Icon(Icons.groups), label: 'Teams'),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
@@ -138,9 +144,7 @@ class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin
|
||||
} else {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const CreateTeamPage(),
|
||||
),
|
||||
MaterialPageRoute(builder: (context) => const CreateTeamPage()),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -18,15 +18,19 @@ class _TournamentDetailPageState extends State<TournamentDetailPage> {
|
||||
|
||||
void _showJoinTeamDialog(BuildContext context, int tournamentId) async {
|
||||
final teamProvider = Provider.of<TeamProvider>(context, listen: false);
|
||||
|
||||
|
||||
try {
|
||||
final teams = await teamProvider.getUserTeams();
|
||||
|
||||
|
||||
if (!context.mounted) return;
|
||||
|
||||
|
||||
if (teams.isEmpty) {
|
||||
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;
|
||||
}
|
||||
@@ -46,8 +50,8 @@ class _TournamentDetailPageState extends State<TournamentDetailPage> {
|
||||
return ListTile(
|
||||
leading: CircleAvatar(child: Text(team.tag)),
|
||||
title: Text(team.name),
|
||||
subtitle: team.description.isNotEmpty
|
||||
? Text(team.description)
|
||||
subtitle: team.description.isNotEmpty
|
||||
? Text(team.description)
|
||||
: null,
|
||||
onTap: () => Navigator.pop(dialogContext, team),
|
||||
);
|
||||
@@ -70,15 +74,15 @@ class _TournamentDetailPageState extends State<TournamentDetailPage> {
|
||||
tournamentId,
|
||||
selectedTeam.id,
|
||||
);
|
||||
|
||||
|
||||
if (!context.mounted) return;
|
||||
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('${selectedTeam.name} joined the tournament!'),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
// Refresh teams list if currently showing
|
||||
if (isShowingTeams) {
|
||||
setState(() {});
|
||||
@@ -87,25 +91,34 @@ class _TournamentDetailPageState extends State<TournamentDetailPage> {
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Failed to join: ${e.toString().replaceAll('Exception: ', '')}'),
|
||||
content: Text(
|
||||
'Failed to join: ${e.toString().replaceAll('Exception: ', '')}',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Failed to load your teams: $e')),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Failed to load your teams: $e')));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final appBarBackground = colorScheme.surface.withValues(alpha: 0.78);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(widget.tournament.name, style: TextStyle(overflow: TextOverflow.ellipsis)),
|
||||
backgroundColor: Theme.of(context).colorScheme.surface.withAlpha(180),
|
||||
backgroundColor: appBarBackground,
|
||||
foregroundColor: colorScheme.onSurface,
|
||||
title: Text(
|
||||
widget.tournament.name,
|
||||
style: TextStyle(overflow: TextOverflow.ellipsis),
|
||||
),
|
||||
elevation: 3,
|
||||
actions: [
|
||||
IconButton(
|
||||
|
||||
@@ -1,11 +1,50 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class ThemeProvider extends ChangeNotifier {
|
||||
static const String _themeKey = 'theme_mode';
|
||||
ThemeMode _themeMode = ThemeMode.system;
|
||||
enum AppThemeOption { lightBlue, darkPurple, lightMint, darkAmber, 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() {
|
||||
_loadTheme();
|
||||
@@ -14,38 +53,116 @@ class ThemeProvider extends ChangeNotifier {
|
||||
Future<void> _loadTheme() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final themeName = prefs.getString(_themeKey) ?? 'system';
|
||||
_themeMode = _themeFromString(themeName);
|
||||
_selectedTheme = _themeFromString(themeName);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> setTheme(ThemeMode mode) async {
|
||||
_themeMode = mode;
|
||||
Future<void> setTheme(AppThemeOption option) async {
|
||||
_selectedTheme = option;
|
||||
notifyListeners();
|
||||
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(_themeKey, _themeToString(mode));
|
||||
await prefs.setString(_themeKey, _themeToString(option));
|
||||
}
|
||||
|
||||
String _themeToString(ThemeMode mode) {
|
||||
switch (mode) {
|
||||
case ThemeMode.light:
|
||||
return 'light';
|
||||
case ThemeMode.dark:
|
||||
return 'dark';
|
||||
case ThemeMode.system:
|
||||
String _themeToString(AppThemeOption option) {
|
||||
switch (option) {
|
||||
case AppThemeOption.lightBlue:
|
||||
return 'light_blue';
|
||||
case AppThemeOption.darkPurple:
|
||||
return 'dark_purple';
|
||||
case AppThemeOption.lightMint:
|
||||
return 'light_mint';
|
||||
case AppThemeOption.darkAmber:
|
||||
return 'dark_amber';
|
||||
case AppThemeOption.system:
|
||||
return 'system';
|
||||
}
|
||||
}
|
||||
|
||||
ThemeMode _themeFromString(String theme) {
|
||||
AppThemeOption _themeFromString(String theme) {
|
||||
switch (theme) {
|
||||
case 'light':
|
||||
return ThemeMode.light;
|
||||
case 'light_blue':
|
||||
return AppThemeOption.lightBlue;
|
||||
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':
|
||||
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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,26 +6,34 @@ import '../providers/theme_provider.dart';
|
||||
class ThemeSelectorWidget extends StatelessWidget {
|
||||
ThemeSelectorWidget({super.key});
|
||||
|
||||
final List<DropdownMenuItem> dropdownElements = [
|
||||
DropdownMenuItem(
|
||||
value: ThemeMode.light,
|
||||
child: Text("Light"),
|
||||
final List<DropdownMenuItem<AppThemeOption>> dropdownElements = [
|
||||
const DropdownMenuItem(
|
||||
value: AppThemeOption.lightBlue,
|
||||
child: Text("Light Blue"),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: ThemeMode.dark,
|
||||
child: Text("Dark"),
|
||||
const DropdownMenuItem(
|
||||
value: AppThemeOption.darkPurple,
|
||||
child: Text("Dark Purple"),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: ThemeMode.system,
|
||||
child: Text("System"),
|
||||
const DropdownMenuItem(
|
||||
value: AppThemeOption.lightMint,
|
||||
child: Text("Light Mint"),
|
||||
),
|
||||
const DropdownMenuItem(
|
||||
value: AppThemeOption.darkAmber,
|
||||
child: Text("Dark Amber"),
|
||||
),
|
||||
const DropdownMenuItem(value: AppThemeOption.system, child: Text("System")),
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeProvider = Provider.of<ThemeProvider>(context);
|
||||
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),
|
||||
margin: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
@@ -34,10 +42,10 @@ class ThemeSelectorWidget extends StatelessWidget {
|
||||
Text("Theme"),
|
||||
SizedBox(
|
||||
width: 250,
|
||||
child: DropdownButtonFormField(
|
||||
icon: Icon(Icons.color_lens),
|
||||
child: DropdownButtonFormField<AppThemeOption>(
|
||||
icon: const Icon(Icons.color_lens),
|
||||
items: dropdownElements,
|
||||
initialValue: themeProvider.theme,
|
||||
initialValue: themeProvider.selectedTheme,
|
||||
onChanged: (value) {
|
||||
if (value == null) return;
|
||||
themeProvider.setTheme(value);
|
||||
@@ -48,5 +56,4 @@ class ThemeSelectorWidget extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user