Merge branch 'development' into feature/168-teamspiele-implementieren
# Conflicts: # lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart # lib/presentation/views/main_menu/match_view/match_detail_view.dart # lib/presentation/views/main_menu/match_view/match_result_view.dart # lib/presentation/widgets/buttons/main_menu_button.dart # pubspec.yaml
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:tallee/core/custom_theme.dart';
|
||||
import 'package:tallee/core/enums.dart';
|
||||
|
||||
@@ -48,7 +49,12 @@ class CustomWidthButton extends StatelessWidget {
|
||||
)!;
|
||||
|
||||
return ElevatedButton(
|
||||
onPressed: onPressed,
|
||||
onPressed: onPressed == null
|
||||
? null
|
||||
: () async {
|
||||
await HapticFeedback.selectionClick();
|
||||
onPressed!.call();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: textcolor,
|
||||
disabledForegroundColor: disabledTextColor,
|
||||
@@ -78,7 +84,12 @@ class CustomWidthButton extends StatelessWidget {
|
||||
: Color.lerp(CustomTheme.primaryColor, Colors.black, 0.5)!;
|
||||
|
||||
return OutlinedButton(
|
||||
onPressed: onPressed,
|
||||
onPressed: onPressed == null
|
||||
? null
|
||||
: () async {
|
||||
await HapticFeedback.selectionClick();
|
||||
onPressed!.call();
|
||||
},
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: textcolor,
|
||||
disabledForegroundColor: disabledTextColor,
|
||||
@@ -110,7 +121,12 @@ class CustomWidthButton extends StatelessWidget {
|
||||
disabledBackgroundColor = Colors.transparent;
|
||||
|
||||
return TextButton(
|
||||
onPressed: onPressed,
|
||||
onPressed: onPressed == null
|
||||
? null
|
||||
: () async {
|
||||
await HapticFeedback.selectionClick();
|
||||
onPressed!.call();
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: textcolor,
|
||||
disabledForegroundColor: disabledTextColor,
|
||||
|
||||
23
lib/presentation/widgets/buttons/haptic_back_button.dart
Normal file
23
lib/presentation/widgets/buttons/haptic_back_button.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
|
||||
|
||||
class HapticBackButton extends StatelessWidget {
|
||||
const HapticBackButton({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final iconData = switch (defaultTargetPlatform) {
|
||||
TargetPlatform.iOS ||
|
||||
TargetPlatform.macOS => Icons.arrow_back_ios_new_rounded,
|
||||
_ => Icons.arrow_back_rounded,
|
||||
};
|
||||
|
||||
return HapticIconButton(
|
||||
icon: Icon(iconData),
|
||||
onPressed: () async {
|
||||
Navigator.of(context).maybePop();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
23
lib/presentation/widgets/buttons/haptic_close_button.dart
Normal file
23
lib/presentation/widgets/buttons/haptic_close_button.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
|
||||
|
||||
class HapticCloseButton extends StatelessWidget {
|
||||
const HapticCloseButton({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final iconData = switch (defaultTargetPlatform) {
|
||||
TargetPlatform.iOS || TargetPlatform.macOS => CupertinoIcons.xmark,
|
||||
_ => Icons.close_rounded,
|
||||
};
|
||||
|
||||
return HapticIconButton(
|
||||
icon: Icon(iconData),
|
||||
onPressed: () async {
|
||||
Navigator.of(context).maybePop();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
51
lib/presentation/widgets/buttons/haptic_icon_button.dart
Normal file
51
lib/presentation/widgets/buttons/haptic_icon_button.dart
Normal file
@@ -0,0 +1,51 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class HapticIconButton extends StatelessWidget {
|
||||
const HapticIconButton({
|
||||
super.key,
|
||||
required this.icon,
|
||||
required this.onPressed,
|
||||
this.iconSize,
|
||||
this.color,
|
||||
this.padding,
|
||||
this.alignment,
|
||||
this.constraints,
|
||||
this.style,
|
||||
this.isSelected,
|
||||
this.selectedIcon,
|
||||
});
|
||||
|
||||
final Widget icon;
|
||||
final VoidCallback? onPressed;
|
||||
final double? iconSize;
|
||||
final Color? color;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
final AlignmentGeometry? alignment;
|
||||
final BoxConstraints? constraints;
|
||||
final ButtonStyle? style;
|
||||
final bool? isSelected;
|
||||
final Widget? selectedIcon;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IconButton(
|
||||
iconSize: iconSize,
|
||||
highlightColor: Colors.transparent, //disable splash animation
|
||||
color: color,
|
||||
padding: padding,
|
||||
alignment: alignment ?? Alignment.center,
|
||||
constraints: constraints,
|
||||
style: style,
|
||||
isSelected: isSelected,
|
||||
selectedIcon: selectedIcon,
|
||||
icon: icon,
|
||||
onPressed: onPressed == null
|
||||
? null
|
||||
: () async {
|
||||
await HapticFeedback.selectionClick();
|
||||
onPressed!.call();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class MainMenuButton extends StatefulWidget {
|
||||
/// A button for the main menu with an optional icon and a press animation.
|
||||
@@ -84,14 +85,21 @@ class _MainMenuButtonState extends State<MainMenuButton>
|
||||
} else {
|
||||
_animationController.forward();
|
||||
if (widget.onLongPressed != null) {
|
||||
_longPressTimer = Timer(const Duration(milliseconds: 400), () {
|
||||
_isLongPressing = true;
|
||||
widget.onLongPressed?.call();
|
||||
_repeatTimer = Timer.periodic(
|
||||
const Duration(milliseconds: 250),
|
||||
(_) => widget.onLongPressed?.call(),
|
||||
);
|
||||
});
|
||||
_longPressTimer = Timer(
|
||||
const Duration(milliseconds: 400),
|
||||
() async {
|
||||
_isLongPressing = true;
|
||||
widget.onLongPressed?.call();
|
||||
await HapticFeedback.heavyImpact();
|
||||
_repeatTimer = Timer.periodic(
|
||||
const Duration(milliseconds: 250),
|
||||
(_) async {
|
||||
widget.onLongPressed?.call();
|
||||
await HapticFeedback.heavyImpact();
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -101,6 +109,7 @@ class _MainMenuButtonState extends State<MainMenuButton>
|
||||
} else {
|
||||
_cancelTimers();
|
||||
if (mounted && !_isLongPressing) {
|
||||
await HapticFeedback.selectionClick();
|
||||
widget.onPressed?.call();
|
||||
}
|
||||
_isLongPressing = false;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:tallee/core/enums.dart';
|
||||
import 'package:tallee/presentation/widgets/buttons/animated_dialog_button.dart';
|
||||
|
||||
@@ -26,7 +27,10 @@ class CustomDialogAction extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedDialogButton(
|
||||
onPressed: onPressed,
|
||||
onPressed: () async {
|
||||
await HapticFeedback.selectionClick();
|
||||
onPressed.call();
|
||||
},
|
||||
buttonText: text,
|
||||
buttonType: buttonType,
|
||||
isDescructive: isDestructive,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tallee/core/common.dart';
|
||||
import 'package:tallee/core/constants.dart';
|
||||
@@ -143,7 +144,8 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
text: player.name,
|
||||
suffixText: getNameCountText(player),
|
||||
onIconTap: () {
|
||||
setState(() {
|
||||
setState(() async {
|
||||
await HapticFeedback.selectionClick();
|
||||
// Removes the player from the selection and notifies the parent.
|
||||
selectedPlayers.remove(player);
|
||||
widget.onChanged([...selectedPlayers]);
|
||||
@@ -197,7 +199,8 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
text: suggestedPlayers[index].name,
|
||||
suffixText: getNameCountText(suggestedPlayers[index]),
|
||||
icon: Icons.add,
|
||||
onPressed: () {
|
||||
onPressed: () async {
|
||||
await HapticFeedback.selectionClick();
|
||||
setState(() {
|
||||
// If the player is not already selected
|
||||
if (!selectedPlayers.contains(
|
||||
@@ -294,8 +297,10 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
|
||||
if (success) {
|
||||
_handleSuccessfulPlayerCreation(createdPlayer);
|
||||
await HapticFeedback.successNotification();
|
||||
showSnackBarMessage(loc.successfully_added_player(playerName));
|
||||
} else {
|
||||
await HapticFeedback.errorNotification();
|
||||
showSnackBarMessage(loc.could_not_add_player(playerName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:tallee/core/custom_theme.dart';
|
||||
|
||||
class ChooseTile extends StatefulWidget {
|
||||
@@ -30,7 +31,14 @@ class _ChooseTileState extends State<ChooseTile> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: widget.onPressed,
|
||||
onTap: widget.onPressed != null
|
||||
? () async {
|
||||
await HapticFeedback.selectionClick();
|
||||
if (widget.onPressed != null) {
|
||||
widget.onPressed!.call();
|
||||
}
|
||||
}
|
||||
: null,
|
||||
child: Container(
|
||||
margin: CustomTheme.tileMargin,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:tallee/core/common.dart';
|
||||
import 'package:tallee/core/custom_theme.dart';
|
||||
import 'package:tallee/core/enums.dart';
|
||||
@@ -53,8 +54,18 @@ class GameTile extends StatelessWidget {
|
||||
final gameColor = badgeColor ?? getColorFromGameColor(GameColor.orange);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
onTap: () async {
|
||||
await HapticFeedback.selectionClick();
|
||||
if (onTap != null) {
|
||||
onTap!.call();
|
||||
}
|
||||
},
|
||||
onLongPress: () async {
|
||||
await HapticFeedback.heavyImpact();
|
||||
if (onLongPress != null) {
|
||||
onLongPress!.call();
|
||||
}
|
||||
},
|
||||
child: AnimatedContainer(
|
||||
margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
|
||||
decoration: !isHighlighted
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:tallee/core/common.dart';
|
||||
import 'package:tallee/core/custom_theme.dart';
|
||||
import 'package:tallee/data/models/group.dart';
|
||||
@@ -33,7 +34,12 @@ class _GroupTileState extends State<GroupTile> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: widget.onTap,
|
||||
onTap: () async {
|
||||
await HapticFeedback.selectionClick();
|
||||
if (widget.onTap != null) {
|
||||
widget.onTap!.call();
|
||||
}
|
||||
},
|
||||
child: AnimatedContainer(
|
||||
margin: CustomTheme.standardMargin,
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:tallee/core/custom_theme.dart';
|
||||
import 'package:tallee/presentation/views/main_menu/settings_view/licenses/license_detail_view.dart';
|
||||
import 'package:tallee/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart';
|
||||
@@ -15,8 +16,10 @@ class LicenseTile extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
onTap: () async {
|
||||
final navigator = Navigator.of(context);
|
||||
await HapticFeedback.selectionClick();
|
||||
navigator.push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LicenseDetailView(package: package),
|
||||
),
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:tallee/core/custom_theme.dart';
|
||||
|
||||
class CustomCheckboxListTile extends StatelessWidget {
|
||||
const CustomCheckboxListTile({
|
||||
super.key,
|
||||
required this.text,
|
||||
required this.value,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
final String text;
|
||||
final bool value;
|
||||
final ValueChanged<bool> onChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
await HapticFeedback.selectionClick();
|
||||
onChanged(!value);
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: CustomTheme.boxColor,
|
||||
border: Border.all(color: CustomTheme.boxBorderColor),
|
||||
borderRadius: CustomTheme.standardBorderRadiusAll,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: value,
|
||||
onChanged: (bool? v) async {
|
||||
await HapticFeedback.selectionClick();
|
||||
if (v == null) return;
|
||||
onChanged(v);
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
text,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:core' hide Match;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:tallee/core/common.dart';
|
||||
import 'package:tallee/core/custom_theme.dart';
|
||||
@@ -52,7 +53,10 @@ class _MatchTileState extends State<MatchTile> {
|
||||
final loc = AppLocalizations.of(context);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: widget.onTap,
|
||||
onTap: () async {
|
||||
await HapticFeedback.selectionClick();
|
||||
widget.onTap.call();
|
||||
},
|
||||
child: Container(
|
||||
margin: EdgeInsets.zero,
|
||||
width: widget.width,
|
||||
@@ -332,6 +336,9 @@ class _MatchTileState extends State<MatchTile> {
|
||||
return '${loc.winner}: $mvpNames (${getPointLabel(loc, mvpScore)})';
|
||||
} else if (ruleset == Ruleset.placement) {
|
||||
return '${loc.winner}: ${widget.match.mvp.first.name}';
|
||||
} else if (ruleset == Ruleset.multipleWinners) {
|
||||
final mvpNames = widget.match.mvp.map((player) => player.name).join(', ');
|
||||
return '${loc.winners}: $mvpNames';
|
||||
}
|
||||
return '${loc.winner}: n.A.';
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:tallee/core/custom_theme.dart';
|
||||
import 'package:tallee/presentation/widgets/colored_icon_container.dart';
|
||||
|
||||
@@ -36,7 +37,10 @@ class SettingsListTile extends StatelessWidget {
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.95,
|
||||
child: GestureDetector(
|
||||
onTap: onPressed ?? () {},
|
||||
onTap: () async {
|
||||
await HapticFeedback.selectionClick();
|
||||
onPressed?.call();
|
||||
},
|
||||
child: Container(
|
||||
margin: EdgeInsets.zero,
|
||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
|
||||
|
||||
Reference in New Issue
Block a user