Styling of Tournament list and detail page

This commit is contained in:
2026-03-13 15:25:44 +01:00
parent 95897ccc07
commit dfe99a85a1
3 changed files with 133 additions and 73 deletions

View File

@@ -173,4 +173,9 @@ Folgende Dateien wurden in diesem Prompt verändert:
- Always require the full amount of teams for initializing the bracket. - Always require the full amount of teams for initializing the bracket.
Folgende Dateien wurden in diesem Prompt verändert: Folgende Dateien wurden in diesem Prompt verändert:
- backend_splatournament_manager/src/app.ts - backend_splatournament_manager/src/app.ts
- frontend_splatournament_manager/lib/pages/tournament_bracket_page.dart - frontend_splatournament_manager/lib/pages/tournament_bracket_page.dart
- Style the tournament list and detail page to look closer to the rest of the app.
Folgende Dateien wurden in diesem Prompt verändert:
- frontend_splatournament_manager/lib/widgets/available_tournament_list.dart
- frontend_splatournament_manager/lib/pages/tournament_detail_page.dart

View File

@@ -243,46 +243,50 @@ class _TournamentTeamsWidgetState extends State<TournamentTeamsWidget> {
); );
} }
return ListView.builder( return ListView.builder(
padding: const EdgeInsets.fromLTRB(16, 4, 16, 16),
itemCount: teams.length, itemCount: teams.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final team = teams[index]; final team = teams[index];
return ListTile( return Card(
leading: CircleAvatar(child: Text(team.tag)), margin: const EdgeInsets.only(bottom: 12),
title: Text(team.name), child: ListTile(
subtitle: team.description.isNotEmpty leading: CircleAvatar(child: Text(team.tag)),
? Text(team.description) title: Text(team.name),
: null, subtitle: team.description.isNotEmpty
trailing: IconButton( ? Text(team.description)
icon: Icon( : null,
Icons.remove_circle_outline, trailing: IconButton(
color: Colors.red, icon: const Icon(
), Icons.remove_circle_outline,
onPressed: () async { color: Colors.red,
try { ),
await Provider.of<TeamProvider>( onPressed: () async {
context, try {
listen: false, await Provider.of<TeamProvider>(
).removeTeamFromTournament(
widget.tournament.id,
team.id,
);
setState(() {
_teamsFuture = Provider.of<TeamProvider>(
context, context,
listen: false, listen: false,
).getTeamsByTournament(widget.tournament.id); ).removeTeamFromTournament(
}); widget.tournament.id,
} catch (e) { team.id,
if (!context.mounted) return; );
ScaffoldMessenger.of(context).showSnackBar( setState(() {
SnackBar( _teamsFuture = Provider.of<TeamProvider>(
content: Text( context,
'Team konnte nicht entfernt werden: ${e.toString().replaceFirst('Exception: ', '')}', listen: false,
).getTeamsByTournament(widget.tournament.id);
});
} catch (e) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Team konnte nicht entfernt werden: ${e.toString().replaceFirst('Exception: ', '')}',
),
), ),
), );
); }
} },
}, ),
), ),
); );
}, },
@@ -379,26 +383,34 @@ class DetailHeader extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return SizedBox( return SizedBox(
height: 350, height: 250,
width: double.maxFinite, width: double.maxFinite,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadiusDirectional.vertical( borderRadius: const BorderRadiusDirectional.vertical(
bottom: Radius.circular(8), bottom: Radius.circular(16),
), ),
image: DecorationImage( gradient: LinearGradient(
fit: BoxFit.cover, colors: [
//TODO: Replace with proper image colorScheme.primaryContainer,
image: NetworkImage( colorScheme.secondaryContainer,
"https://flutter.dev/assets/image_1.w635.f71cbb614cd16a40bfb87e128278227c.png", ],
), begin: Alignment.topLeft,
end: Alignment.bottomRight,
), ),
), ),
padding: EdgeInsets.fromLTRB(16, 0, 0, 12), padding: const EdgeInsets.fromLTRB(16, 0, 16, 6),
child: Column( child: Column(
verticalDirection: VerticalDirection.up,
children: [ children: [
const SizedBox(height: 80),
Icon(
Icons.emoji_events,
size: 116,
color: colorScheme.onPrimaryContainer.withValues(alpha: 0.7),
),
Spacer(),
Row( Row(
children: [ children: [
InputChip( InputChip(

View File

@@ -9,22 +9,24 @@ class AvailableTournamentList extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Column(
padding: EdgeInsets.fromLTRB(24, 0, 24, 0), crossAxisAlignment: CrossAxisAlignment.start,
child: Column( children: [
children: [ const Padding(
const Row(children: [Text('Verfügbare Turniere')]), padding: EdgeInsets.fromLTRB(16, 8, 16, 4),
SizedBox( child: Text(
width: double.infinity, 'Verfügbare Turniere',
height: 350, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
child: Consumer<TournamentProvider>(
builder: (context, provider, _) {
return TournamentListFutureBuilder(provider: provider);
},
),
), ),
], ),
), Expanded(
child: Consumer<TournamentProvider>(
builder: (context, provider, _) {
return TournamentListFutureBuilder(provider: provider);
},
),
),
],
); );
} }
} }
@@ -58,6 +60,7 @@ class TournamentListFutureBuilder extends StatelessWidget {
} }
return ListView.builder( return ListView.builder(
padding: const EdgeInsets.fromLTRB(16, 4, 16, 16),
shrinkWrap: false, shrinkWrap: false,
itemCount: list.length, itemCount: list.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
@@ -70,6 +73,15 @@ class TournamentListFutureBuilder extends StatelessWidget {
} }
} }
String _fmtDate(String iso) {
try {
final d = DateTime.parse(iso);
return '${d.day.toString().padLeft(2, '0')}.${d.month.toString().padLeft(2, '0')}.${d.year}';
} catch (_) {
return iso;
}
}
class TournamentListItem extends StatelessWidget { class TournamentListItem extends StatelessWidget {
final Tournament tournament; final Tournament tournament;
@@ -77,19 +89,50 @@ class TournamentListItem extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListTile( final dateRange =
contentPadding: EdgeInsets.all(0), '${_fmtDate(tournament.registrationStartDate)} ${_fmtDate(tournament.registrationEndDate)}';
leading: Icon(Icons.abc), return Card(
title: Text(tournament.name), margin: const EdgeInsets.only(bottom: 12),
subtitle: Text(tournament.description), child: InkWell(
onTap: () { borderRadius: BorderRadius.circular(12),
Navigator.push( onTap: () {
context, Navigator.push(
MaterialPageRoute( context,
builder: (context) => TournamentDetailPage(tournament: tournament), MaterialPageRoute(
builder: (context) =>
TournamentDetailPage(tournament: tournament),
),
);
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
CircleAvatar(
child: const Icon(Icons.emoji_events),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
tournament.name,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 4),
Text(
'${tournament.currentTeamAmount}/${tournament.maxTeamAmount} Teams\n$dateRange',
style: const TextStyle(fontSize: 13),
),
],
),
),
const Icon(Icons.chevron_right),
],
), ),
); ),
}, ),
); );
} }
} }