Shuffle vorbereitungen

This commit is contained in:
MarcWieland 2025-12-10 23:14:38 +01:00
parent e636f282ee
commit 41a4b2481a
8 changed files with 423 additions and 11 deletions

View File

@ -68,4 +68,12 @@ class PlayerNotifier extends StateNotifier<List<PlayerModel>> {
}
state = box.values.toList();
}
void resetAllWins(){
for(final player in box.values){
player.wins = 0;
player.save();
}
state = box.values.toList();
}
}

View File

@ -0,0 +1,99 @@
// lib/features/tournament/presentation/provider/shuffle_settings_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive/hive.dart';
part 'shuffle_settings_provider.g.dart'; // Für den Hive-Generator
// Enum mit Hive-Annotationen
@HiveType(typeId: 5) // Freie typeId wählen (z. B. 5, nicht bereits vergeben)
enum ShuffleMode {
@HiveField(0)
random,
@HiveField(1)
balanced,
@HiveField(2)
aggressive,
@HiveField(3)
positionBased,
}
// ShuffleSettings-Klasse
@HiveType(typeId: 4)
class ShuffleSettings extends HiveObject {
@HiveField(0)
ShuffleMode mode;
@HiveField(1)
bool prioritizeGenderBalance;
@HiveField(2)
bool prioritizeLevelBalance;
@HiveField(3)
bool usePositions;
ShuffleSettings({
this.mode = ShuffleMode.balanced,
this.prioritizeGenderBalance = true,
this.prioritizeLevelBalance = true,
this.usePositions = true,
});
// copyWith für immutable Updates
ShuffleSettings copyWith({
ShuffleMode? mode,
bool? prioritizeGenderBalance,
bool? prioritizeLevelBalance,
bool? usePositions,
}) {
return ShuffleSettings(
mode: mode ?? this.mode,
prioritizeGenderBalance: prioritizeGenderBalance ?? this.prioritizeGenderBalance,
prioritizeLevelBalance: prioritizeLevelBalance ?? this.prioritizeLevelBalance,
usePositions: usePositions ?? this.usePositions,
);
}
}
// Provider
final shuffleSettingsProvider = StateNotifierProvider<ShuffleSettingsNotifier, ShuffleSettings>((ref) {
return ShuffleSettingsNotifier();
});
class ShuffleSettingsNotifier extends StateNotifier<ShuffleSettings> {
static const String _boxName = 'shuffle_settings';
static const String _key = 'settings';
ShuffleSettingsNotifier() : super(ShuffleSettings()) {
_load();
}
Future<void> _load() async {
final box = await Hive.openBox<ShuffleSettings>(_boxName);
final saved = box.get(_key);
if (saved != null) {
state = saved;
}
}
Future<void> update({
ShuffleMode? mode,
bool? prioritizeGenderBalance,
bool? prioritizeLevelBalance,
bool? usePositions,
}) async {
state = state.copyWith(
mode: mode,
prioritizeGenderBalance: prioritizeGenderBalance,
prioritizeLevelBalance: prioritizeLevelBalance,
usePositions: usePositions,
);
final box = await Hive.openBox<ShuffleSettings>(_boxName);
await box.put(_key, state);
}
}

View File

@ -0,0 +1,99 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'shuffle_settings_provider.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class ShuffleSettingsAdapter extends TypeAdapter<ShuffleSettings> {
@override
final int typeId = 4;
@override
ShuffleSettings read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return ShuffleSettings(
mode: fields[0] as ShuffleMode,
prioritizeGenderBalance: fields[1] as bool,
prioritizeLevelBalance: fields[2] as bool,
usePositions: fields[3] as bool,
);
}
@override
void write(BinaryWriter writer, ShuffleSettings obj) {
writer
..writeByte(4)
..writeByte(0)
..write(obj.mode)
..writeByte(1)
..write(obj.prioritizeGenderBalance)
..writeByte(2)
..write(obj.prioritizeLevelBalance)
..writeByte(3)
..write(obj.usePositions);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ShuffleSettingsAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class ShuffleModeAdapter extends TypeAdapter<ShuffleMode> {
@override
final int typeId = 5;
@override
ShuffleMode read(BinaryReader reader) {
switch (reader.readByte()) {
case 0:
return ShuffleMode.random;
case 1:
return ShuffleMode.balanced;
case 2:
return ShuffleMode.aggressive;
case 3:
return ShuffleMode.positionBased;
default:
return ShuffleMode.random;
}
}
@override
void write(BinaryWriter writer, ShuffleMode obj) {
switch (obj) {
case ShuffleMode.random:
writer.writeByte(0);
break;
case ShuffleMode.balanced:
writer.writeByte(1);
break;
case ShuffleMode.aggressive:
writer.writeByte(2);
break;
case ShuffleMode.positionBased:
writer.writeByte(3);
break;
}
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ShuffleModeAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@ -4,6 +4,7 @@ import '../../data/models/player_model.dart';
import '../provider/active_players_provider.dart';
import '../provider/player_provider.dart'; // für awardWinToPlayers
import '../widgets/volleyball_field_widget.dart';
import 'package:flutter/services.dart';
class FieldsScreen extends ConsumerWidget {
const FieldsScreen({super.key});
@ -66,13 +67,14 @@ class FieldsScreen extends ConsumerWidget {
),
onPressed: teamA.isEmpty
? null
: () {
: () async {
await HapticFeedback.mediumImpact();
ref.read(playerListProvider.notifier).awardWinToPlayers(teamA);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Oben gewinnt! +1 Schleifchen für ${teamA.length} Spieler'),
backgroundColor: Colors.green,
duration: const Duration(seconds: 2),
duration: const Duration(seconds: 1),
),
);
},
@ -93,13 +95,14 @@ class FieldsScreen extends ConsumerWidget {
),
onPressed: teamB.isEmpty
? null
: () {
: () async{
HapticFeedback.mediumImpact();
ref.read(playerListProvider.notifier).awardWinToPlayers(teamB);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Unten gewinnt! +1 Schleifchen für ${teamB.length} Spieler'),
backgroundColor: Colors.red,
duration: const Duration(seconds: 2),
duration: const Duration(seconds: 1),
),
);
},
@ -144,7 +147,8 @@ class FieldsScreen extends ConsumerWidget {
child: ElevatedButton.icon(
icon: const Icon(Icons.shuffle),
label: const Text('Neu mischen', style: TextStyle(fontSize: 18)),
onPressed: () {
onPressed: () async{
HapticFeedback.mediumImpact();
ref.invalidate(activePlayersProvider);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Neue Teams gemischt!'), duration: Duration(milliseconds: 100)),

View File

@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:schleifchenturnier/features/tournament/presentation/screens/fields_screen.dart';
import 'package:schleifchenturnier/features/tournament/presentation/screens/stats_screen.dart';
import 'package:schleifchenturnier/features/tournament/presentation/screens/timer_setup_screen.dart';
import 'player_management_screen.dart'; // wird gleich erstellt
// Später: timer_screen.dart, fields_screen.dart, stats_screen.dart
import 'player_management_screen.dart';
import 'timer_setup_screen.dart';
import 'fields_screen.dart';
import 'stats_screen.dart';
import 'settings_screen.dart';
class HomeScreen extends ConsumerStatefulWidget {
const HomeScreen({super.key});
@ -20,7 +20,7 @@ class _HomeScreenState extends ConsumerState<HomeScreen> {
PlayerManagementScreen(),
TimerSetupScreen(),
FieldsScreen(),
StatsScreen()
StatsScreen(),
];
void _onTabTapped(int index) {
@ -29,6 +29,11 @@ class _HomeScreenState extends ConsumerState<HomeScreen> {
});
}
// Zeigt das Zahnrad nur auf Spieler- und Statistik-Tab
bool _showSettingsIcon() {
return _selectedIndex == 0 || _selectedIndex == 3;
}
@override
Widget build(BuildContext context) {
return Scaffold(
@ -36,12 +41,28 @@ class _HomeScreenState extends ConsumerState<HomeScreen> {
title: const Text('Schleifchenturnier'),
centerTitle: true,
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
foregroundColor: Theme.of(context).colorScheme.onPrimaryContainer,
elevation: 4,
actions: _showSettingsIcon()
? [
IconButton(
icon: const Icon(Icons.settings),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => const SettingsScreen()),
);
},
),
]
: null,
),
body: _screens[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: _selectedIndex,
onTap: _onTabTapped,
selectedItemColor: Theme.of(context).colorScheme.primary,
unselectedItemColor: Colors.grey.shade600,
items: const [
BottomNavigationBarItem(icon: Icon(Icons.people), label: 'Spieler'),
BottomNavigationBarItem(icon: Icon(Icons.timer), label: 'Timer'),

View File

@ -0,0 +1,95 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:schleifchenturnier/features/tournament/presentation/screens/shuffle_settings_screen.dart';
import '../provider/player_provider.dart';
class SettingsScreen extends ConsumerWidget {
const SettingsScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
title: const Text('Einstellungen'),
centerTitle: true,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
const SizedBox(height: 40),
// Zurücksetzen der Siege
Card(
child: ListTile(
leading: const Icon(Icons.restore, color: Colors.orange, size: 32),
title: const Text(
'Alle Siege zurücksetzen',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
subtitle: const Text('Setzt die Schleifchen-Anzahl aller Spieler auf 0'),
trailing: const Icon(Icons.arrow_forward_ios),
onTap: () {
_showResetConfirmationDialog(context, ref);
},
),
),
Card(
child: ListTile(
leading: const Icon(Icons.shuffle, color: Colors.deepPurple, size: 32),
title: const Text('Shuffle-Einstellungen', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
subtitle: const Text('Modus und Balance-Optionen konfigurieren'),
trailing: const Icon(Icons.arrow_forward_ios),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => const ShuffleSettingsScreen()));
},
),
),
const Spacer(),
// Version oder Footer
const Text(
'Schleifchenturnier v1.0\nMade with ❤️ für die beste Volleyball-Crew',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey, fontSize: 14),
),
const SizedBox(height: 20),
],
),
),
);
}
void _showResetConfirmationDialog(BuildContext context, WidgetRef ref) {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Wirklich alle Siege zurücksetzen?'),
content: const Text(
'Dadurch werden alle Schleifchen (Siege) aller Spieler auf 0 gesetzt.\n\nDas kann nicht rückgängig gemacht werden!',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: const Text('Abbrechen'),
),
ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
onPressed: () {
ref.read(playerListProvider.notifier).resetAllWins();
Navigator.pop(ctx);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Alle Siege wurden zurückgesetzt!'),
backgroundColor: Colors.orange,
),
);
},
child: const Text('Zurücksetzen'),
),
],
),
);
}
}

View File

@ -0,0 +1,83 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../provider/shuffle_settings_provider.dart';
class ShuffleSettingsScreen extends ConsumerWidget {
const ShuffleSettingsScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final settings = ref.watch(shuffleSettingsProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Shuffle-Einstellungen'),
centerTitle: true,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
const Text(
'Shuffle-Modus',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
RadioListTile<ShuffleMode>(
title: const Text('Random'),
subtitle: const Text('Einfach zufällig mischen'),
value: ShuffleMode.random,
groupValue: settings.mode,
onChanged: (val) => ref.read(shuffleSettingsProvider.notifier).update(mode: val),
),
RadioListTile<ShuffleMode>(
title: const Text('Ausgewogen'),
subtitle: const Text('Gute Balance von Geschlecht und Level'),
value: ShuffleMode.balanced,
groupValue: settings.mode,
onChanged: (val) => ref.read(shuffleSettingsProvider.notifier).update(mode: val),
),
RadioListTile<ShuffleMode>(
title: const Text('Aggressiv ausgewogen'),
subtitle: const Text('Strenge Regeln perfekte Teams oder gar nicht'),
value: ShuffleMode.aggressive,
groupValue: settings.mode,
onChanged: (val) => ref.read(shuffleSettingsProvider.notifier).update(mode: val),
),
RadioListTile<ShuffleMode>(
title: const Text('Positionsbasiert'),
subtitle: const Text('Versucht ideale Aufstellung: 1 Zuspieler, 2 Außen, etc.'),
value: ShuffleMode.positionBased,
groupValue: settings.mode,
onChanged: (val) => ref.read(shuffleSettingsProvider.notifier).update(mode: val),
),
const Divider(height: 40),
const Text(
'Zusätzliche Optionen',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
SwitchListTile(
title: const Text('Geschlechterbalance priorisieren'),
subtitle: const Text('Versucht 3M/3F pro Team'),
value: settings.prioritizeGenderBalance,
onChanged: (val) => ref.read(shuffleSettingsProvider.notifier).update(prioritizeGenderBalance: val),
),
SwitchListTile(
title: const Text('Level-Balance priorisieren'),
subtitle: const Text('Ähnliche Spielstärke pro Team'),
value: settings.prioritizeLevelBalance,
onChanged: (val) => ref.read(shuffleSettingsProvider.notifier).update(prioritizeLevelBalance: val),
),
SwitchListTile(
title: const Text('Positionsbesetzung beachten'),
subtitle: const Text('Ideale Aufstellung pro Team versuchen'),
value: settings.usePositions,
onChanged: (val) => ref.read(shuffleSettingsProvider.notifier).update(usePositions: val),
),
],
),
);
}
}

View File

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:schleifchenturnier/features/tournament/data/models/player_model.dart';
import 'package:schleifchenturnier/features/tournament/presentation/provider/shuffle_settings_provider.dart';
import 'features/tournament/presentation/screens/home_screen.dart';
@ -14,6 +15,8 @@ void main() async{
await Hive.initFlutter();
Hive.registerAdapter(PlayerModelAdapter());
Hive.registerAdapter(ShuffleSettingsAdapter());
Hive.registerAdapter(ShuffleModeAdapter());
Hive.registerAdapter(GenderAdapter());
Hive.registerAdapter(SkillLevelAdapter());
Hive.registerAdapter(PositionAdapter());