schleifchenturnier_25/lib/features/tournament/presentation/screens/player_management_screen.dart
MarcWieland e636f282ee init
2025-12-10 22:51:28 +01:00

257 lines
9.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../provider/player_provider.dart';
import '../../data/models/player_model.dart';
class PlayerManagementScreen extends ConsumerWidget {
const PlayerManagementScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final players = ref.watch(playerListProvider);
return Scaffold(
body: players.isEmpty
? const Center(
child: Text(
'Noch keine Spieler hinzugefügt\nTippe auf + um zu starten!',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18, color: Colors.grey),
),
)
: ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: players.length,
itemBuilder: (context, index) {
final player = players[index];
return Card(
child: ListTile(
title: Text(
player.name,
style: TextStyle(
fontWeight: FontWeight.bold,
decoration: player.isActive ? TextDecoration.none : TextDecoration.lineThrough,
color: player.isActive ? null : Colors.grey,
),
),
subtitle: Text(
'${_genderEmoji(player.gender)} ${player.gender.name}${player.skillLevel.name.toUpperCase()} • Siege: ${player.wins}${player.isActive ? '' : ' (inaktiv)'}',
style: TextStyle(color: player.isActive ? null : Colors.grey),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.delete, color: Colors.red),
onPressed: () {
ref.read(playerListProvider.notifier).deletePlayer(player);
},
),
Switch(
value: player.isActive,
onChanged: (val) {
player.isActive = val;
ref.read(playerListProvider.notifier).updatePlayer(player);
},
),
],
),
onTap: () {
_showAddEditPlayerDialog(context, ref, player: player);
},
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => _showAddEditPlayerDialog(context, ref),
child: const Icon(Icons.add),
),
);
}
String _genderEmoji(Gender gender) {
switch (gender) {
case Gender.male:
return '👨';
case Gender.female:
return '👩';
case Gender.diverse:
return '🧑';
}
}
void _showAddEditPlayerDialog(BuildContext context, WidgetRef ref, {PlayerModel? player}) {
final isEdit = player != null;
final formKey = GlobalKey<FormState>();
// Lokale temporäre Werte auch für isActive!
String name = isEdit ? player!.name : '';
Gender gender = isEdit ? player.gender : Gender.male;
SkillLevel skillLevel = isEdit ? player.skillLevel : SkillLevel.mid;
Set<Position> selectedPositions = isEdit ? player.positions.toSet() : <Position>{};
bool isActive = isEdit ? player.isActive : true; // Standard: aktiv
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text(isEdit ? 'Spieler bearbeiten' : 'Neuen Spieler hinzufügen'),
content: SizedBox(
width: 400,
child: Form(
key: formKey,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Name
TextFormField(
initialValue: name,
decoration: const InputDecoration(
labelText: 'Name',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Bitte einen Namen eingeben';
}
return null;
},
onSaved: (value) => name = value!.trim(),
),
const SizedBox(height: 16),
// Geschlecht
DropdownButtonFormField<Gender>(
value: gender,
decoration: const InputDecoration(
labelText: 'Geschlecht',
border: OutlineInputBorder(),
),
items: Gender.values.map((g) {
return DropdownMenuItem(
value: g,
child: Text(g.name[0].toUpperCase() + g.name.substring(1)),
);
}).toList(),
onChanged: (value) => gender = value!,
),
const SizedBox(height: 16),
// Leistungsstufe
const Text('Leistungsstufe', style: TextStyle(fontSize: 16)),
const SizedBox(height: 8),
SegmentedButton<SkillLevel>(
segments: SkillLevel.values.map((level) {
return ButtonSegment(
value: level,
label: Text(level.name.toUpperCase()),
);
}).toList(),
selected: {skillLevel},
onSelectionChanged: (newSelection) {
skillLevel = newSelection.first;
(ctx as Element).markNeedsBuild();
},
),
const SizedBox(height: 16),
// Aktiv-Schalter
Row(
children: [
Checkbox(
value: isActive,
onChanged: (val) {
isActive = val!;
(ctx as Element).markNeedsBuild();
},
),
const SizedBox(width: 8),
const Text('Aktiv am Turnier teilnehmen', style: TextStyle(fontSize: 16)),
],
),
const SizedBox(height: 16),
// Positionen
const Text('Positionen (mehrere möglich)', style: TextStyle(fontSize: 16)),
const SizedBox(height: 8),
Wrap(
spacing: 8,
children: Position.values.map((pos) {
final isSelected = selectedPositions.contains(pos);
return FilterChip(
label: Text(_positionDisplayName(pos)),
selected: isSelected,
onSelected: (selected) {
if (selected) {
selectedPositions.add(pos);
} else {
selectedPositions.remove(pos);
}
(ctx as Element).markNeedsBuild();
},
);
}).toList(),
),
],
),
),
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: const Text('Abbrechen'),
),
ElevatedButton(
onPressed: () {
if (formKey.currentState!.validate()) {
formKey.currentState!.save();
if (isEdit) {
// Bearbeiten
player!.name = name;
player.gender = gender;
player.skillLevel = skillLevel;
player.positions = selectedPositions.toList();
player.isActive = isActive;
ref.read(playerListProvider.notifier).updatePlayer(player);
} else {
// Neu anlegen
ref.read(playerListProvider.notifier).addPlayer(
name: name,
gender: gender,
skillLevel: skillLevel,
positions: selectedPositions.toList(),
// Wir erweitern addPlayer später, oder wir holen den neuen Player und setzen isActive
).then((_) {
// Hinweis: Aktuell setzt addPlayer isActive = true. Wenn du es deaktiviert anlegen willst,
// müssen wir addPlayer erweitern. Für 99% der Fälle ist true ok.
// Alternativ: Später einen separaten Toggle-Provider machen.
});
}
Navigator.pop(ctx);
}
},
child: const Text('Speichern'),
),
],
),
);
}
String _positionDisplayName(Position pos) {
switch (pos) {
case Position.aussen:
return 'Außen';
case Position.mitte:
return 'Mitte';
case Position.diagonal:
return 'Diagonal';
case Position.zuspieler:
return 'Zuspieler';
case Position.libero:
return 'Libero';
}
}
}