feat: Implemented LiveEditView
Some checks failed
Pull Request Pipeline / test (pull_request) Failing after 44s
Pull Request Pipeline / lint (pull_request) Successful in 52s

This commit is contained in:
2026-05-21 21:10:07 +02:00
parent 12b7bcdc6c
commit 46134a4f5c
3 changed files with 184 additions and 135 deletions

View File

@@ -0,0 +1,89 @@
import 'package:flutter/material.dart';
import 'package:tallee/data/models/match.dart';
import 'package:tallee/data/models/player.dart';
import 'package:tallee/data/models/team.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
import 'package:tallee/presentation/widgets/tiles/match_result_view/live_edit_list_tile.dart';
class LiveEditView extends StatefulWidget {
const LiveEditView({super.key, required this.match});
final Match match;
@override
State<LiveEditView> createState() => _LiveEditViewState();
}
class _LiveEditViewState extends State<LiveEditView> {
List<Team> get allTeams =>
(widget.match.teams ?? [])..sort((a, b) => a.name.compareTo(b.name));
List<Player> get allPlayers =>
widget.match.players..sort((a, b) => a.name.compareTo(b.name));
List<int> scores = [];
@override
void initState() {
super.initState();
if (widget.match.isTeamMatch) {
scores = List.generate(
allTeams.length,
(index) => allTeams[index].score ?? 0,
);
} else {
scores = List.generate(
allPlayers.length,
(index) => widget.match.scores[allPlayers[index].id]?.score ?? 0,
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.match.name),
leading: HapticIconButton(
onPressed: () => Navigator.pop(context, scores),
icon: const Icon(Icons.close),
),
),
body: Column(
children: [
Expanded(child: buildLiveEditWidget(widget.match.isTeamMatch)),
],
),
);
}
Widget buildLiveEditWidget(bool isTeamMatch) {
if (isTeamMatch) {
return ListView.builder(
itemCount: allTeams.length,
itemBuilder: (context, index) {
return LiveEditListTile(
title: allTeams[index].name,
onChanged: (value) {
scores[index] = value;
},
value: scores[index],
);
},
);
} else {
return ListView.builder(
itemCount: allPlayers.length,
itemBuilder: (context, index) {
return LiveEditListTile(
title: allPlayers[index].name,
onChanged: (value) {
setState(() {
scores[index] = value;
});
},
value: scores[index],
);
},
);
}
}
}

View File

@@ -2,6 +2,7 @@ import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tallee/core/adaptive_page_route.dart';
import 'package:tallee/core/common.dart'; import 'package:tallee/core/common.dart';
import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/core/enums.dart'; import 'package:tallee/core/enums.dart';
@@ -11,11 +12,11 @@ import 'package:tallee/data/models/player.dart';
import 'package:tallee/data/models/score_entry.dart'; import 'package:tallee/data/models/score_entry.dart';
import 'package:tallee/data/models/team.dart'; import 'package:tallee/data/models/team.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/views/main_menu/match_view/create_match/match_result/live_edit_view.dart';
import 'package:tallee/presentation/widgets/buttons/animated_dialog_button.dart'; import 'package:tallee/presentation/widgets/buttons/animated_dialog_button.dart';
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart'; import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
import 'package:tallee/presentation/widgets/tiles/match_result_view/custom_checkbox_list_tile.dart'; import 'package:tallee/presentation/widgets/tiles/match_result_view/custom_checkbox_list_tile.dart';
import 'package:tallee/presentation/widgets/tiles/match_result_view/custom_radio_list_tile.dart'; import 'package:tallee/presentation/widgets/tiles/match_result_view/custom_radio_list_tile.dart';
import 'package:tallee/presentation/widgets/tiles/match_result_view/live_edit_list_tile.dart';
import 'package:tallee/presentation/widgets/tiles/match_result_view/score_list_tile.dart'; import 'package:tallee/presentation/widgets/tiles/match_result_view/score_list_tile.dart';
import 'package:tallee/presentation/widgets/tiles/text_icon_list_tile.dart'; import 'package:tallee/presentation/widgets/tiles/text_icon_list_tile.dart';
@@ -38,8 +39,6 @@ class MatchResultView extends StatefulWidget {
class _MatchResultViewState extends State<MatchResultView> { class _MatchResultViewState extends State<MatchResultView> {
late final AppDatabase db; late final AppDatabase db;
bool isLiveEditMode = false;
late final Ruleset ruleset; late final Ruleset ruleset;
late final List<Player> allPlayers; late final List<Player> allPlayers;
@@ -92,121 +91,116 @@ class _MatchResultViewState extends State<MatchResultView> {
appBar: AppBar( appBar: AppBar(
automaticallyImplyLeading: true, automaticallyImplyLeading: true,
leading: HapticIconButton( leading: HapticIconButton(
icon: isLiveEditMode icon: const Icon(Icons.close),
? const Icon(Icons.arrow_back_ios) onPressed: () => {
: const Icon(Icons.close), widget.onWinnerChanged?.call(),
onPressed: isLiveEditMode Navigator.pop(context),
? () => setState(() { },
isLiveEditMode = false;
})
: () => {widget.onWinnerChanged?.call(), Navigator.pop(context)},
), ),
title: Text(widget.match.name), title: Text(widget.match.name),
), ),
body: Column( body: Column(
children: [ children: [
Expanded( Expanded(
child: isLiveEditMode child: Container(
// Live Edit Mode margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
? buildLiveEditWidet(isTeamMatch) padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
// Normal Container decoration: BoxDecoration(
: Container( color: CustomTheme.boxColor,
margin: const EdgeInsets.symmetric( border: Border.all(color: CustomTheme.boxBorderColor),
horizontal: 12, borderRadius: BorderRadius.circular(12),
vertical: 10, ),
), child: Column(
padding: const EdgeInsets.symmetric( mainAxisAlignment: MainAxisAlignment.start,
vertical: 10, crossAxisAlignment: CrossAxisAlignment.start,
horizontal: 10, children: [
), Text(
decoration: BoxDecoration( getTitleForRuleset(loc),
color: CustomTheme.boxColor, style: const TextStyle(
border: Border.all(color: CustomTheme.boxBorderColor), fontSize: 18,
borderRadius: BorderRadius.circular(12), fontWeight: FontWeight.bold,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
getTitleForRuleset(loc),
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
// Show player selection
if (rulesetSupportsPlayerSelection())
if (ruleset == Ruleset.multipleWinners)
Expanded(
child: buildMultipleWinnerSelectionWidget(
isTeamMatch,
),
)
else
Expanded(
child: buildPlayerSelectionWidget(isTeamMatch),
),
// Show score entry
if (rulesetSupportsScoreEntry())
Expanded(child: buildScoreEntryWidget(isTeamMatch)),
// Show draggable placement list
if (rulesetSupportsDragBehaviour())
Expanded(child: buildPlacementWidget(isTeamMatch)),
],
), ),
), ),
const SizedBox(height: 10),
// Show player selection
if (rulesetSupportsPlayerSelection())
if (ruleset == Ruleset.multipleWinners)
Expanded(
child: buildMultipleWinnerSelectionWidget(isTeamMatch),
)
else
Expanded(child: buildPlayerSelectionWidget(isTeamMatch)),
// Show score entry
if (rulesetSupportsScoreEntry())
Expanded(child: buildScoreEntryWidget(isTeamMatch)),
// Show draggable placement list
if (rulesetSupportsDragBehaviour())
Expanded(child: buildPlacementWidget(isTeamMatch)),
],
),
),
), ),
if (!isLiveEditMode) ...[ Padding(
Padding( padding: const EdgeInsets.fromLTRB(12, 0, 12, 20),
padding: const EdgeInsets.fromLTRB(12, 0, 12, 20), child: Column(
child: Column( mainAxisSize: MainAxisSize.min,
mainAxisSize: MainAxisSize.min, children: [
children: [ // Live Edit Mode Button
if (rulesetSupportsScoreEntry()) ...[ if (rulesetSupportsScoreEntry()) ...[
// Button to switch to live edit mode
AnimatedDialogButton(
buttonConstraints: const BoxConstraints(
minWidth: double.infinity,
minHeight: 50,
),
buttonText: loc.live_edit_mode,
buttonType: ButtonType.secondary,
onPressed: () => setState(() {
isLiveEditMode = !isLiveEditMode;
}),
),
],
// Save Changes Button
AnimatedDialogButton( AnimatedDialogButton(
buttonConstraints: const BoxConstraints( buttonConstraints: const BoxConstraints(
minWidth: double.infinity, minWidth: double.infinity,
minHeight: 50, minHeight: 50,
), ),
buttonText: loc.save_changes, buttonText: loc.live_edit_mode,
onPressed: canSave buttonType: ButtonType.secondary,
? () async { onPressed: () =>
final ending = DateTime.now(); Navigator.push(
await db.matchDao.updateMatchEndedAt( context,
matchId: widget.match.id, adaptivePageRoute(
endedAt: ending, fullscreenDialog: true,
); builder: (context) =>
await _handleSaving(); LiveEditView(match: widget.match),
if (!context.mounted) return; ),
Navigator.pop(context); ).then(
} (scores) => {
: null, if (scores != null)
{
for (int i = 0; i < scores.length; i++)
{controller[i].text = scores[i].toString()},
},
},
),
), ),
], ],
),
// Save Changes Button
AnimatedDialogButton(
buttonConstraints: const BoxConstraints(
minWidth: double.infinity,
minHeight: 50,
),
buttonText: loc.save_changes,
onPressed: canSave
? () async {
final ending = DateTime.now();
await db.matchDao.updateMatchEndedAt(
matchId: widget.match.id,
endedAt: ending,
);
await _handleSaving();
if (!context.mounted) return;
Navigator.pop(context);
}
: null,
),
],
), ),
], ),
], ],
), ),
); );
@@ -847,38 +841,4 @@ class _MatchResultViewState extends State<MatchResultView> {
); );
} }
} }
Widget buildLiveEditWidet(bool isTeamMatch) {
if (isTeamMatch) {
return ListView.builder(
itemCount: allTeams.length,
itemBuilder: (context, index) {
return LiveEditListTile(
title: allTeams[index].name,
onChanged: (value) {
setState(() {
controller[index].text = value.toString();
});
},
value: int.tryParse(controller[index].text) ?? 0,
);
},
);
} else {
return ListView.builder(
itemCount: allPlayers.length,
itemBuilder: (context, index) {
return LiveEditListTile(
title: allPlayers[index].name,
onChanged: (value) {
setState(() {
controller[index].text = value.toString();
});
},
value: int.tryParse(controller[index].text) ?? 0,
);
},
);
}
}
} }

View File

@@ -1,7 +1,7 @@
name: tallee name: tallee
description: "Tracking App for Card Games" description: "Tracking App for Card Games"
publish_to: 'none' publish_to: 'none'
version: 0.0.30+330 version: 0.0.30+331
environment: environment:
sdk: ^3.8.1 sdk: ^3.8.1