From 9c92ded4fa5c532dcac95c00ec2c111fde5b8adb Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sun, 8 Mar 2026 10:43:58 +0100 Subject: [PATCH] merge & implement choose_color_view.dart --- lib/core/common.dart | 47 +++++++++- lib/l10n/arb/app_de.arb | 12 ++- lib/l10n/arb/app_en.arb | 18 +++- lib/l10n/generated/app_localizations.dart | 84 +++++++++++++++++ lib/l10n/generated/app_localizations_de.dart | 44 ++++++++- lib/l10n/generated/app_localizations_en.dart | 30 +++++++ .../create_match/choose_game_view.dart | 53 ++++++----- .../game_view/choose_color_view.dart | 78 ++++++++++++++++ .../game_view/choose_ruleset_view.dart | 16 ++-- .../game_view/create_game_view.dart | 89 ++++++++++++++----- .../widgets/text_input/text_input_field.dart | 8 +- 11 files changed, 425 insertions(+), 54 deletions(-) create mode 100644 lib/presentation/views/main_menu/match_view/create_match/game_view/choose_color_view.dart diff --git a/lib/core/common.dart b/lib/core/common.dart index a27daf0..8d598f1 100644 --- a/lib/core/common.dart +++ b/lib/core/common.dart @@ -1,4 +1,4 @@ -import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:tallee/core/enums.dart'; import 'package:tallee/data/dto/match.dart'; import 'package:tallee/l10n/generated/app_localizations.dart'; @@ -20,6 +20,51 @@ String translateRulesetToString(Ruleset ruleset, BuildContext context) { } } +/// Translates a [GameColor] enum value to its corresponding localized string. +String translateGameColorToString(GameColor color, BuildContext context) { + final loc = AppLocalizations.of(context); + switch (color) { + case GameColor.red: + return loc.color_red; + case GameColor.blue: + return loc.color_blue; + case GameColor.green: + return loc.color_green; + case GameColor.yellow: + return loc.color_yellow; + case GameColor.purple: + return loc.color_purple; + case GameColor.orange: + return loc.color_orange; + case GameColor.pink: + return loc.color_pink; + case GameColor.teal: + return loc.color_teal; + } +} + +/// Returns the [Color] object corresponding to a [GameColor] enum value. +Color getColorFromGameColor(GameColor color) { + switch (color) { + case GameColor.red: + return Colors.red; + case GameColor.blue: + return Colors.blue; + case GameColor.green: + return Colors.green; + case GameColor.yellow: + return Colors.yellow; + case GameColor.purple: + return Colors.purple; + case GameColor.orange: + return Colors.orange; + case GameColor.pink: + return Colors.pink; + case GameColor.teal: + return Colors.teal; + } +} + /// Counts how many players in the match are not part of the group /// Returns the count as a string, or an empty string if there is no group String getExtraPlayerCount(Match match) { diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 49801c7..b92cc4c 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -9,6 +9,7 @@ "choose_game": "Spielvorlage wählen", "choose_group": "Gruppe wählen", "choose_ruleset": "Regelwerk wählen", + "choose_color": "Farbe wählen", "could_not_add_player": "Spieler:in {playerName} konnte nicht hinzugefügt werden", "create_game": "Spielvorlage erstellen", "create_group": "Gruppe erstellen", @@ -111,5 +112,14 @@ "winner": "Gewinner:in", "winrate": "Siegquote", "wins": "Siege", - "yesterday_at": "Gestern um" + "yesterday_at": "Gestern um", + "color_red": "Rot", + "color_blue": "Blau", + "color_green": "Grün", + "color_yellow": "Gelb", + "color_purple": "Lila", + "color_orange": "Orange", + "color_pink": "Rosa", + "color_teal": "Türkis", + "color": "Farbe" } \ No newline at end of file diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 99574a8..c242a34 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -27,6 +27,9 @@ "@choose_ruleset": { "description": "Label for choosing a ruleset" }, + "@choose_color": { + "description": "Label for choosing a color" + }, "@could_not_add_player": { "description": "Error message when adding a player fails" }, @@ -337,6 +340,9 @@ "@yesterday_at": { "description": "Date format for yesterday" }, + "@color": { + "description": "Color label" + }, "all_players": "All players", "all_players_selected": "All players selected", "amount_of_matches": "Amount of Matches", @@ -346,6 +352,7 @@ "choose_game": "Choose Game", "choose_group": "Choose Group", "choose_ruleset": "Choose Ruleset", + "choose_color": "Choose Color", "could_not_add_player": "Could not add player", "create_game": "Create Game", "create_group": "Create Group", @@ -446,5 +453,14 @@ "winner": "Winner", "winrate": "Winrate", "wins": "Wins", - "yesterday_at": "Yesterday at" + "yesterday_at": "Yesterday at", + "color_red": "Red", + "color_blue": "Blue", + "color_green": "Green", + "color_yellow": "Yellow", + "color_purple": "Purple", + "color_orange": "Orange", + "color_pink": "Pink", + "color_teal": "Teal", + "color": "Color" } \ No newline at end of file diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index eb8a609..72d0a0d 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -98,6 +98,18 @@ abstract class AppLocalizations { Locale('en'), ]; + /// No description provided for @description. + /// + /// In en, this message translates to: + /// **'Description'** + String get description; + + /// No description provided for @edit_game. + /// + /// In en, this message translates to: + /// **'Edit Game'** + String get edit_game; + /// Label for all players list /// /// In en, this message translates to: @@ -152,12 +164,24 @@ abstract class AppLocalizations { /// **'Choose Ruleset'** String get choose_ruleset; + /// Label for choosing a color + /// + /// In en, this message translates to: + /// **'Choose Color'** + String get choose_color; + /// Error message when adding a player fails /// /// In en, this message translates to: /// **'Could not add player'** String could_not_add_player(Object playerName); + /// Button text to create a game + /// + /// In en, this message translates to: + /// **'Create Game'** + String get create_game; + /// Button text to create a group /// /// In en, this message translates to: @@ -230,6 +254,12 @@ abstract class AppLocalizations { /// **'Delete all data'** String get delete_all_data; + /// Button text to delete a game + /// + /// In en, this message translates to: + /// **'Delete Game'** + String get delete_game; + /// Confirmation dialog for deleting a group /// /// In en, this message translates to: @@ -733,6 +763,60 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Yesterday at'** String get yesterday_at; + + /// No description provided for @color_red. + /// + /// In en, this message translates to: + /// **'Red'** + String get color_red; + + /// No description provided for @color_blue. + /// + /// In en, this message translates to: + /// **'Blue'** + String get color_blue; + + /// No description provided for @color_green. + /// + /// In en, this message translates to: + /// **'Green'** + String get color_green; + + /// No description provided for @color_yellow. + /// + /// In en, this message translates to: + /// **'Yellow'** + String get color_yellow; + + /// No description provided for @color_purple. + /// + /// In en, this message translates to: + /// **'Purple'** + String get color_purple; + + /// No description provided for @color_orange. + /// + /// In en, this message translates to: + /// **'Orange'** + String get color_orange; + + /// No description provided for @color_pink. + /// + /// In en, this message translates to: + /// **'Pink'** + String get color_pink; + + /// No description provided for @color_teal. + /// + /// In en, this message translates to: + /// **'Teal'** + String get color_teal; + + /// Color label + /// + /// In en, this message translates to: + /// **'Color'** + String get color; } class _AppLocalizationsDelegate diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 501f9c6..e417f18 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -8,6 +8,12 @@ import 'app_localizations.dart'; class AppLocalizationsDe extends AppLocalizations { AppLocalizationsDe([String locale = 'de']) : super(locale); + @override + String get description => 'Beschreibung'; + + @override + String get edit_game => 'Spielvorlage bearbeiten'; + @override String get all_players => 'Alle Spieler:innen'; @@ -35,11 +41,17 @@ class AppLocalizationsDe extends AppLocalizations { @override String get choose_ruleset => 'Regelwerk wählen'; + @override + String get choose_color => 'Farbe wählen'; + @override String could_not_add_player(Object playerName) { return 'Spieler:in $playerName konnte nicht hinzugefügt werden'; } + @override + String get create_game => 'Spielvorlage erstellen'; + @override String get create_group => 'Gruppe erstellen'; @@ -79,7 +91,10 @@ class AppLocalizationsDe extends AppLocalizations { String get delete_all_data => 'Alle Daten löschen'; @override - String get delete_group => 'Diese Gruppe löschen'; + String get delete_game => 'Spielvorlage löschen'; + + @override + String get delete_group => 'Gruppe löschen'; @override String get delete_match => 'Spiel löschen'; @@ -343,4 +358,31 @@ class AppLocalizationsDe extends AppLocalizations { @override String get yesterday_at => 'Gestern um'; + + @override + String get color_red => 'Rot'; + + @override + String get color_blue => 'Blau'; + + @override + String get color_green => 'Grün'; + + @override + String get color_yellow => 'Gelb'; + + @override + String get color_purple => 'Lila'; + + @override + String get color_orange => 'Orange'; + + @override + String get color_pink => 'Rosa'; + + @override + String get color_teal => 'Türkis'; + + @override + String get color => 'Farbe'; } diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 0a76a50..cea3a39 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -41,6 +41,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get choose_ruleset => 'Choose Ruleset'; + @override + String get choose_color => 'Choose Color'; + @override String could_not_add_player(Object playerName) { return 'Could not add player'; @@ -354,4 +357,31 @@ class AppLocalizationsEn extends AppLocalizations { @override String get yesterday_at => 'Yesterday at'; + + @override + String get color_red => 'Red'; + + @override + String get color_blue => 'Blue'; + + @override + String get color_green => 'Green'; + + @override + String get color_yellow => 'Yellow'; + + @override + String get color_purple => 'Purple'; + + @override + String get color_orange => 'Orange'; + + @override + String get color_pink => 'Pink'; + + @override + String get color_teal => 'Teal'; + + @override + String get color => 'Color'; } diff --git a/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart b/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart index 0d0e2a4..d4eba51 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart @@ -1,13 +1,13 @@ import 'package:flutter/material.dart'; +import 'package:tallee/core/adaptive_page_route.dart'; import 'package:tallee/core/common.dart'; import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/dto/game.dart'; import 'package:tallee/l10n/generated/app_localizations.dart'; +import 'package:tallee/presentation/views/main_menu/match_view/create_match/game_view/create_game_view.dart'; import 'package:tallee/presentation/widgets/text_input/custom_search_bar.dart'; import 'package:tallee/presentation/widgets/tiles/title_description_list_tile.dart'; -import 'package:tallee/core/adaptive_page_route.dart'; -import 'package:tallee/presentation/views/main_menu/match_view/create_match/game_view/create_game_view.dart'; -import 'package:tallee/data/dto/game.dart'; class ChooseGameView extends StatefulWidget { /// A view that allows the user to choose a game from a list of available games @@ -55,17 +55,21 @@ class _ChooseGameViewState extends State { Navigator.of(context).pop(selectedGameIndex); }, ), - actions: [IconButton( - icon: const Icon(Icons.add), - onPressed: () async { - await Navigator.push(context, adaptivePageRoute( - builder: (context) => CreateGameView( - callback: () {}, //TODO: implement callback - ), - ) - ); - }, - )], + actions: [ + IconButton( + icon: const Icon(Icons.add), + onPressed: () async { + await Navigator.push( + context, + adaptivePageRoute( + builder: (context) => CreateGameView( + callback: () {}, //TODO: implement callback + ), + ), + ); + }, + ), + ], title: Text(loc.choose_game), ), body: PopScope( @@ -110,13 +114,22 @@ class _ChooseGameViewState extends State { }); }, onLongPress: () async { - await Navigator.push(context, adaptivePageRoute( - builder: (context) => CreateGameView( - //TODO: implement callback & giving real game to create game view - gameToEdit: Game(name: 'Cabo', description: '', ruleset: 'Highest Points'), - callback: () {}, + await Navigator.push( + context, + adaptivePageRoute( + builder: (context) => CreateGameView( + //TODO: implement callback & giving real game to create game view + gameToEdit: Game( + name: 'Cabo', + description: + 'Test Beschreibung mit sehr viel Inhalt', + ruleset: Ruleset.highestScore, + color: GameColor.blue, + icon: '', + ), + callback: () {}, + ), ), - ) ); }, ); diff --git a/lib/presentation/views/main_menu/match_view/create_match/game_view/choose_color_view.dart b/lib/presentation/views/main_menu/match_view/create_match/game_view/choose_color_view.dart new file mode 100644 index 0000000..bf764ad --- /dev/null +++ b/lib/presentation/views/main_menu/match_view/create_match/game_view/choose_color_view.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; +import 'package:tallee/core/common.dart'; +import 'package:tallee/core/custom_theme.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/l10n/generated/app_localizations.dart'; +import 'package:tallee/presentation/widgets/tiles/title_description_list_tile.dart'; + +class ChooseColorView extends StatefulWidget { + /// A view that allows the user to choose a color from a list of available game colors + /// - [initialColor]: The initially selected color + const ChooseColorView({super.key, this.initialColor}); + + /// The initially selected color + final GameColor? initialColor; + + @override + State createState() => _ChooseColorViewState(); +} + +class _ChooseColorViewState extends State { + /// Currently selected color + GameColor? selectedColor; + + @override + void initState() { + selectedColor = widget.initialColor; + super.initState(); + } + + @override + Widget build(BuildContext context) { + final loc = AppLocalizations.of(context); + const colors = GameColor.values; + + return Scaffold( + backgroundColor: CustomTheme.backgroundColor, + appBar: AppBar( + leading: IconButton( + icon: const Icon(Icons.arrow_back_ios), + onPressed: () { + Navigator.of(context).pop(selectedColor); + }, + ), + title: Text(loc.choose_color), + ), + body: PopScope( + canPop: false, + onPopInvokedWithResult: (bool didPop, Object? result) { + if (didPop) return; + Navigator.of(context).pop(selectedColor); + }, + child: ListView.builder( + padding: const EdgeInsets.only(bottom: 85), + itemCount: colors.length, + itemBuilder: (BuildContext context, int index) { + final color = colors[index]; + return TitleDescriptionListTile( + onTap: () { + setState(() { + if (selectedColor == color) { + selectedColor = null; + } else { + selectedColor = color; + } + }); + }, + title: translateGameColorToString(color, context), + description: '', + isHighlighted: selectedColor == color, + badgeText: ' ', //Breite für Color Badge + badgeColor: getColorFromGameColor(color), + ); + }, + ), + ), + ); + } +} diff --git a/lib/presentation/views/main_menu/match_view/create_match/game_view/choose_ruleset_view.dart b/lib/presentation/views/main_menu/match_view/create_match/game_view/choose_ruleset_view.dart index d534173..6b69b22 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/game_view/choose_ruleset_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/game_view/choose_ruleset_view.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; -import 'package:game_tracker/core/custom_theme.dart'; -import 'package:game_tracker/core/enums.dart'; -import 'package:game_tracker/l10n/generated/app_localizations.dart'; -import 'package:game_tracker/presentation/widgets/tiles/title_description_list_tile.dart'; +import 'package:tallee/core/common.dart'; +import 'package:tallee/core/custom_theme.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/l10n/generated/app_localizations.dart'; +import 'package:tallee/presentation/widgets/tiles/title_description_list_tile.dart'; + class ChooseRulesetView extends StatefulWidget { /// A view that allows the user to choose a ruleset from a list of available rulesets /// - [rulesets]: A list of tuples containing the ruleset and its description @@ -12,13 +14,16 @@ class ChooseRulesetView extends StatefulWidget { required this.rulesets, required this.initialRulesetIndex, }); + /// A list of tuples containing the ruleset and its description final List<(Ruleset, String)> rulesets; + /// The index of the initially selected ruleset final int initialRulesetIndex; @override State createState() => _ChooseRulesetViewState(); } + class _ChooseRulesetViewState extends State { /// Currently selected ruleset index late int selectedRulesetIndex; @@ -28,6 +33,7 @@ class _ChooseRulesetViewState extends State { selectedRulesetIndex = widget.initialRulesetIndex; super.initState(); } + @override Widget build(BuildContext context) { final loc = AppLocalizations.of(context); @@ -90,4 +96,4 @@ class _ChooseRulesetViewState extends State { ), ); } -} \ No newline at end of file +} diff --git a/lib/presentation/views/main_menu/match_view/create_match/game_view/create_game_view.dart b/lib/presentation/views/main_menu/match_view/create_match/game_view/create_game_view.dart index b3216f2..7c83748 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/game_view/create_game_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/game_view/create_game_view.dart @@ -1,14 +1,16 @@ import 'package:flutter/material.dart'; -import 'package:game_tracker/core/adaptive_page_route.dart'; -import 'package:game_tracker/core/constants.dart'; -import 'package:game_tracker/core/custom_theme.dart'; -import 'package:game_tracker/core/enums.dart'; -import 'package:game_tracker/data/dto/game.dart'; -import 'package:game_tracker/l10n/generated/app_localizations.dart'; -import 'package:game_tracker/presentation/views/main_menu/match_view/create_match/game_view/choose_ruleset_view.dart'; -import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart'; -import 'package:game_tracker/presentation/widgets/text_input/text_input_field.dart'; -import 'package:game_tracker/presentation/widgets/tiles/choose_tile.dart'; +import 'package:tallee/core/adaptive_page_route.dart'; +import 'package:tallee/core/common.dart'; +import 'package:tallee/core/constants.dart'; +import 'package:tallee/core/custom_theme.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/dto/game.dart'; +import 'package:tallee/l10n/generated/app_localizations.dart'; +import 'package:tallee/presentation/views/main_menu/match_view/create_match/game_view/choose_color_view.dart'; +import 'package:tallee/presentation/views/main_menu/match_view/create_match/game_view/choose_ruleset_view.dart'; +import 'package:tallee/presentation/widgets/buttons/custom_width_button.dart'; +import 'package:tallee/presentation/widgets/text_input/text_input_field.dart'; +import 'package:tallee/presentation/widgets/tiles/choose_tile.dart'; class CreateGameView extends StatefulWidget { const CreateGameView({super.key, this.gameToEdit, required this.callback}); @@ -26,6 +28,8 @@ class _CreateGameViewState extends State { int selectedRulesetIndex = -1; late List<(Ruleset, String)> _rulesets; + GameColor? selectedColor; + final _gameNameController = TextEditingController(); final _descriptionController = TextEditingController(); @@ -38,18 +42,38 @@ class _CreateGameViewState extends State { @override void didChangeDependencies() { super.didChangeDependencies(); - final loc = AppLocalizations.of(context); _rulesets = [ - (Ruleset.singleWinner, loc.ruleset_single_winner), - (Ruleset.singleLoser, loc.ruleset_single_loser), - (Ruleset.mostPoints, loc.ruleset_most_points), - (Ruleset.leastPoints, loc.ruleset_least_points), + ( + Ruleset.singleWinner, + translateRulesetToString(Ruleset.singleWinner, context), + ), + ( + Ruleset.singleLoser, + translateRulesetToString(Ruleset.singleLoser, context), + ), + ( + Ruleset.highestScore, + translateRulesetToString(Ruleset.highestScore, context), + ), + ( + Ruleset.lowestScore, + translateRulesetToString(Ruleset.lowestScore, context), + ), + ( + Ruleset.multipleWinners, + translateRulesetToString(Ruleset.multipleWinners, context), + ), ]; if (widget.gameToEdit != null) { _gameNameController.text = widget.gameToEdit!.name; - _descriptionController.text = widget.gameToEdit!.description ?? ''; - // TODO: Handle ruleset initialization from gameToEdit + _descriptionController.text = widget.gameToEdit!.description; + selectedRuleset = widget.gameToEdit!.ruleset; + selectedColor = widget.gameToEdit!.color; + + selectedRulesetIndex = _rulesets.indexWhere( + (r) => r.$1 == selectedRuleset, + ); } } @@ -99,8 +123,28 @@ class _CreateGameViewState extends State { if (mounted) { setState(() { selectedRuleset = result; - selectedRulesetIndex = - result == null ? -1 : _rulesets.indexWhere((r) => r.$1 == result); + selectedRulesetIndex = result == null + ? -1 + : _rulesets.indexWhere((r) => r.$1 == result); + }); + } + }, + ), + ChooseTile( + title: loc.color, + trailingText: selectedColor == null + ? loc.none + : translateGameColorToString(selectedColor!, context), + onPressed: () async { + final result = await Navigator.of(context).push( + adaptivePageRoute( + builder: (context) => + ChooseColorView(initialColor: selectedColor), + ), + ); + if (mounted) { + setState(() { + selectedColor = result; }); } }, @@ -120,10 +164,13 @@ class _CreateGameViewState extends State { Padding( padding: const EdgeInsets.all(12.0), child: CustomWidthButton( - text: isEditing ? loc.edit_group : loc.create_game, + text: isEditing ? loc.edit_game : loc.create_game, sizeRelativeToWidth: 1, buttonType: ButtonType.primary, - onPressed: _gameNameController.text.trim().isNotEmpty && selectedRulesetIndex != -1 + onPressed: + _gameNameController.text.trim().isNotEmpty && + selectedRulesetIndex != -1 && + selectedColor != null ? () { //TODO: Handle saving to db & updating game selection view Navigator.of(context).pop(); diff --git a/lib/presentation/widgets/text_input/text_input_field.dart b/lib/presentation/widgets/text_input/text_input_field.dart index a8a87e7..946be6a 100644 --- a/lib/presentation/widgets/text_input/text_input_field.dart +++ b/lib/presentation/widgets/text_input/text_input_field.dart @@ -19,7 +19,7 @@ class TextInputField extends StatelessWidget { this.maxLength, this.maxLines = 1, this.minLines = 1, - this.showCounterText = false + this.showCounterText = false, }); /// The controller for the text input field. @@ -58,9 +58,9 @@ class TextInputField extends StatelessWidget { hintText: hintText, hintStyle: const TextStyle(fontSize: 18), counterText: showCounterText ? null : '', - enabledBorder: OutlineInputBorder( - borderRadius: const BorderRadius.all(Radius.circular(12)), - borderSide: BorderSide(color: CustomTheme.boxBorder), + enabledBorder: const OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(12)), + borderSide: BorderSide(color: CustomTheme.boxBorderColor), ), focusedBorder: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(12)),