diff --git a/devtools_options.yaml b/devtools_options.yaml
new file mode 100644
index 00000000..7e7e7f67
--- /dev/null
+++ b/devtools_options.yaml
@@ -0,0 +1 @@
+extensions:
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index ff608092..7adb878d 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -43,5 +43,10 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
+ CFBundleLocalizations
+
+ en
+ pt
+
diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart
index 33a3f0e4..3dbed7ae 100644
--- a/lib/app/view/app.dart
+++ b/lib/app/view/app.dart
@@ -11,7 +11,7 @@ import 'package:super_dash/map_tester/map_tester.dart';
import 'package:super_dash/settings/settings.dart';
import 'package:super_dash/share/share.dart';
-class App extends StatelessWidget {
+class App extends StatefulWidget {
const App({
required this.audioController,
required this.settingsController,
@@ -29,6 +29,21 @@ class App extends StatelessWidget {
final AuthenticationRepository authenticationRepository;
final LeaderboardRepository leaderboardRepository;
+ @override
+ State createState() => _AppState();
+
+ static _AppState? of(BuildContext context) =>
+ context.findAncestorStateOfType<_AppState>();
+}
+
+class _AppState extends State {
+ Locale? _locale;
+ void setLocale(Locale value) {
+ setState(() {
+ _locale = value;
+ });
+ }
+
@override
Widget build(BuildContext context) {
return AppLifecycleObserver(
@@ -38,31 +53,33 @@ class App extends StatelessWidget {
create: (context) {
final lifecycleNotifier =
context.read>();
- return audioController
+ return widget.audioController
..attachLifecycleNotifier(lifecycleNotifier);
},
lazy: false,
),
RepositoryProvider.value(
- value: settingsController,
+ value: widget.settingsController,
),
RepositoryProvider.value(
- value: shareController,
+ value: widget.shareController,
),
RepositoryProvider.value(
- value: authenticationRepository..signInAnonymously(),
+ value: widget.authenticationRepository..signInAnonymously(),
),
RepositoryProvider.value(
- value: leaderboardRepository,
+ value: widget.leaderboardRepository,
),
],
child: MaterialApp(
+ locale: _locale,
theme: ThemeData(
textTheme: AppTextStyles.textTheme,
),
supportedLocales: AppLocalizations.supportedLocales,
localizationsDelegates: AppLocalizations.localizationsDelegates,
- home: isTesting ? const MapTesterView() : const GameIntroPage(),
+ home:
+ widget.isTesting ? const MapTesterView() : const GameIntroPage(),
),
),
);
diff --git a/lib/game_intro/game_instructions/view/game_instructions_overlay.dart b/lib/game_intro/game_instructions/view/game_instructions_overlay.dart
index d5142999..e387aee1 100644
--- a/lib/game_intro/game_instructions/view/game_instructions_overlay.dart
+++ b/lib/game_intro/game_instructions/view/game_instructions_overlay.dart
@@ -166,20 +166,20 @@ class _CardContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
- return Column(
- children: [
- _CardImage(assetPath: assetPath),
- const SizedBox(height: 24),
- Text(
- title,
- style: theme.textTheme.headlineSmall?.copyWith(
- color: Colors.white,
- fontWeight: FontWeight.bold,
+ return SingleChildScrollView(
+ child: Column(
+ children: [
+ _CardImage(assetPath: assetPath),
+ const SizedBox(height: 24),
+ Text(
+ title,
+ style: theme.textTheme.headlineSmall?.copyWith(
+ color: Colors.white,
+ fontWeight: FontWeight.bold,
+ ),
),
- ),
- const SizedBox(height: 12),
- Expanded(
- child: Padding(
+ const SizedBox(height: 12),
+ Padding(
padding: const EdgeInsets.symmetric(
horizontal: 24,
),
@@ -191,8 +191,8 @@ class _CardContent extends StatelessWidget {
),
),
),
- ),
- ],
+ ],
+ ),
);
}
}
diff --git a/lib/game_intro/game_select_language/view/game_select_language_overlay.dart b/lib/game_intro/game_select_language/view/game_select_language_overlay.dart
new file mode 100644
index 00000000..00c5fca7
--- /dev/null
+++ b/lib/game_intro/game_select_language/view/game_select_language_overlay.dart
@@ -0,0 +1,100 @@
+import 'dart:ui' as ui;
+
+import 'package:app_ui/app_ui.dart';
+import 'package:flutter/material.dart';
+import 'package:super_dash/app/view/app.dart';
+import 'package:super_dash/l10n/l10n.dart';
+
+class GameSelectLanguageOverlay extends StatelessWidget {
+ const GameSelectLanguageOverlay({super.key});
+
+ static PageRoute route() {
+ return HeroDialogRoute(
+ builder: (_) => BackdropFilter(
+ filter: ui.ImageFilter.blur(sigmaX: 8, sigmaY: 8),
+ child: const GameSelectLanguageOverlay(),
+ ),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return const GameSelectLanguageOverlayView();
+ }
+}
+
+class GameSelectLanguageOverlayView extends StatefulWidget {
+ const GameSelectLanguageOverlayView({super.key});
+
+ @override
+ State createState() =>
+ _GameSelectLanguageOverlayViewState();
+}
+
+class _GameSelectLanguageOverlayViewState
+ extends State {
+ @override
+ Widget build(BuildContext context) {
+ final l10n = context.l10n;
+ final theme = Theme.of(context);
+ final textTheme = theme.textTheme;
+ return AppDialog(
+ showCloseButton: false,
+ border: Border.all(color: Colors.white24),
+ backgroundColor: Colors.white24,
+ child: Column(
+ children: [
+ Text(
+ l10n.selectLanguage,
+ textAlign: TextAlign.center,
+ style: textTheme.headlineSmall?.copyWith(
+ color: Colors.white,
+ ),
+ ),
+ const SizedBox(
+ height: 15,
+ ),
+ Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: AppLocalizations.supportedLocales
+ .map(
+ (e) => InkWell(
+ onTap: () {
+ App.of(context)?.setLocale(
+ Locale.fromSubtags(languageCode: e.languageCode),
+ );
+ },
+ child: Padding(
+ padding: const EdgeInsets.only(
+ bottom: 10,
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ e.languageCode.toUpperCase(),
+ style: textTheme.bodyLarge?.copyWith(
+ color: Colors.white,
+ ),
+ ),
+ const SizedBox(
+ width: 10,
+ ),
+ if (e.languageCode == context.l10n.localeName)
+ const Icon(
+ Icons.check,
+ size: 15,
+ color: Colors.white,
+ ),
+ ],
+ ),
+ ),
+ ),
+ )
+ .toList(),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/game_intro/view/game_intro_page.dart b/lib/game_intro/view/game_intro_page.dart
index 5b04c8f1..f1d6a40e 100644
--- a/lib/game_intro/view/game_intro_page.dart
+++ b/lib/game_intro/view/game_intro_page.dart
@@ -92,6 +92,7 @@ class _IntroPage extends StatelessWidget {
AudioButton(),
LeaderboardButton(),
InfoButton(),
+ LanguageButton(),
HowToPlayButton(),
],
),
diff --git a/lib/game_intro/widgets/game_intro_buttons.dart b/lib/game_intro/widgets/game_intro_buttons.dart
index f0a88c99..e05dea2f 100644
--- a/lib/game_intro/widgets/game_intro_buttons.dart
+++ b/lib/game_intro/widgets/game_intro_buttons.dart
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:super_dash/game_intro/game_intro.dart';
+import 'package:super_dash/game_intro/game_select_language/view/game_select_language_overlay.dart';
import 'package:super_dash/leaderboard/leaderboard.dart';
import 'package:super_dash/settings/settings_controller.dart';
@@ -61,3 +62,17 @@ class HowToPlayButton extends StatelessWidget {
);
}
}
+
+class LanguageButton extends StatelessWidget {
+ const LanguageButton({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return GameIconButton(
+ icon: Icons.public_rounded,
+ onPressed: () => Navigator.of(context).push(
+ GameSelectLanguageOverlay.route(),
+ ),
+ );
+ }
+}
diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb
index 3e4486c7..adf93bb2 100644
--- a/lib/l10n/arb/app_en.arb
+++ b/lib/l10n/arb/app_en.arb
@@ -212,5 +212,9 @@
"downloadAppLabel": "Download the app",
"@downloadAppLabel": {
"description": "Button lable for the app download"
+ },
+ "selectLanguage": "Select language",
+ "@selectLanguage": {
+ "description": "Text shown in language select overlay"
}
}
diff --git a/lib/l10n/arb/app_pt.arb b/lib/l10n/arb/app_pt.arb
new file mode 100644
index 00000000..270f6ea3
--- /dev/null
+++ b/lib/l10n/arb/app_pt.arb
@@ -0,0 +1,220 @@
+{
+ "@@locale": "pt",
+ "gameIntroPageHeadline": "Voe no Super Dash, evite os bugs, colete pontos e veja o quão longe você pode ir!",
+ "@gameIntroPageHeadline": {
+ "description": "Texto exibido no cabeçalho da página de informações do jogo"
+ },
+ "gameIntroPagePlayButtonText": "Jogar",
+ "@gameIntroPagePlayButtonText": {
+ "description": "Texto exibido no botão Jogar da página de informações do jogo"
+ },
+ "gameInstructionsPageAutoRunTitle": "Corrida Automática do Dash",
+ "@gameInstructionsPageAutoRunTitle": {
+ "description": "Texto exibido no título da instrução de corrida automática da sobreposição de instruções do jogo"
+ },
+ "gameInstructionsPageAutoRunDescription": "Bem-vindo ao Super Dash. Neste jogo, o Dash corre automaticamente.",
+ "@gameInstructionsPageAutoRunDescription": {
+ "description": "Texto exibido na descrição da instrução de corrida automática da sobreposição de instruções do jogo"
+ },
+ "gameInstructionsPageTapToJumpTitle": "Toque para Pular",
+ "@gameInstructionsPageTapToJumpTitle": {
+ "description": "Texto exibido no título da instrução de toque para pular da sobreposição de instruções do jogo"
+ },
+ "gameInstructionsPageTapToJumpDescription": "Toque na tela para fazer o Dash pular.",
+ "@gameInstructionsPageTapToJumpDescription": {
+ "description": "Texto exibido na descrição da instrução de toque para pular da sobreposição de instruções do jogo"
+ },
+ "gameInstructionsPageTapToJumpDescriptionDesktop": "Pressione a barra de espaço para fazer o Dash pular.",
+ "@gameInstructionsPageTapToJumpDescriptionDesktop": {
+ "description": "Texto exibido na descrição da instrução de toque para pular da sobreposição de instruções do jogo"
+ },
+ "gameInstructionsPageCollectEggsAcornsTitle": "Colete Ovos e nozes",
+ "@gameInstructionsPageCollectEggsAcornsTitle": {
+ "description": "Texto exibido no título da instrução de coletar ovos e nozes da sobreposição de instruções do jogo"
+ },
+ "gameInstructionsPageCollectEggsAcornsDescription": "Ganhe pontos coletando ovos e nozes no estágio.",
+ "@gameInstructionsPageCollectEggsAcornsDescription": {
+ "description": "Texto exibido na descrição da instrução de coletar ovos e nozes da sobreposição de instruções do jogo"
+ },
+ "gameInstructionsPagePowerfulWingsTitle": "Asas Poderosas",
+ "@gameInstructionsPagePowerfulWingsTitle": {
+ "description": "Texto exibido no título da instrução de asas poderosas da sobreposição de instruções do jogo"
+ },
+ "gameInstructionsPagePowerfulWingsDescription": "Colete a pena dourada para dar poder ao Dash com o Flutter. Enquanto estiver no ar, toque para dar um pulo duplo e veja o Dash voar!",
+ "@gameInstructionsPagePowerfulWingsDescription": {
+ "description": "Texto exibido na descrição da instrução de asas poderosas da sobreposição de instruções do jogo"
+ },
+ "gameInstructionsPageLevelGatesTitle": "Portões de Nível",
+ "@gameInstructionsPageLevelGatesTitle": {
+ "description": "Texto exibido no título da instrução de portões de nível da sobreposição de instruções do jogo"
+ },
+ "gameInstructionsPageLevelGatesDescription": "Avance pelos portões para enfrentar desafios mais difíceis nos estágios mais avançados.",
+ "@gameInstructionsPageLevelGatesDescription": {
+ "description": "Texto exibido na descrição da instrução de portões de nível da sobreposição de instruções do jogo"
+ },
+ "gameInstructionsPageAvoidBugsTitle": "Evite os Bugs",
+ "@gameInstructionsPageAvoidBugsTitle": {
+ "description": "Texto exibido no título da instrução de evitar bugs da sobreposição de instruções do jogo"
+ },
+ "gameInstructionsPageAvoidBugsDescription": "Ninguém gosta de bugs! Pule para desviá-los e evite receber dano.",
+ "@gameInstructionsPageAvoidBugsDescription": {
+ "description": "Texto exibido na descrição da instrução de evitar bugs da sobreposição de instruções do jogo"
+ },
+ "mobileAppsComingSoon": "Apps Móveis em Breve",
+ "@mobileAppsComingSoon": {
+ "description": "Título exibido na página de informações do jogo para dispositivos móveis que estão acessando por meio de um navegador"
+ },
+ "mobileAppsComingSoonGrabThe": "Pegue o ",
+ "@mobileAppsComingSoonGrabThe": {
+ "description": "Descrição exibida na página de informações do jogo para dispositivos móveis que estão acessando por meio de um navegador"
+ },
+ "mobileAppsComingSoonMobileSourceCode": "código-fonte móvel ",
+ "@mobileAppsComingSoonMobileSourceCode": {
+ "description": "Descrição exibida na página de informações do jogo para dispositivos móveis que estão acessando por meio de um navegador"
+ },
+ "mobileAppsComingSoonDescription": "no repositório e procure pelos aplicativos disponíveis para download em breve nas lojas! Abra este link em um navegador web de desktop para jogar hoje.",
+ "@mobileModeUnavailableDescription": {
+ "description": "Descrição exibida na página de informações do jogo para dispositivos móveis que estão acessando por meio de um navegador"
+ },
+ "superDash": "Super Dash",
+ "@superDash": {
+ "description": "Nome do jogo"
+ },
+ "howItsMade": "Como é feito",
+ "@howItsMade": {
+ "description": "Texto exibido na página de informações do jogo"
+ },
+ "aboutSuperDash": "Sobre o Super Dash",
+ "@aboutSuperDash": {
+ "description": "Texto exibido no pop-up de informações do jogo"
+ },
+ "learn": "Aprenda ",
+ "@learn": {
+ "description": "Texto exibido no pop-up de informações do jogo"
+ },
+ "howWeBuiltSuperDash": "como construímos o Super Dash",
+ "@howWeBuiltSuperDash": {
+ "description": "Texto exibido no pop-up de informações do jogo"
+ },
+ "inFlutterAndGrabThe": " no Flutter e pegue o ",
+ "@inFlutterAndGrabThe": {
+ "description": "Texto exibindo no pop-up de informações do jogo"
+ },
+ "openSourceCode": "código aberto.",
+ "@openSourceCode": {
+ "description": "Texto exibido no pop-up de informações do jogo"
+ },
+ "otherLinks": "Outros Links",
+ "@otherLinks": {
+ "description": "Texto exibido no pop-up de informações do jogo"
+ },
+ "flutterGames": "Jogos Flutter",
+ "@flutterGames": {
+ "description": "Texto exibido no pop-up de informações do jogo"
+ },
+ "privacyPolicy": "Política de Privacidade",
+ "@privacyPolicy": {
+ "description": "Texto exibido no pop-up de informações do jogo"
+ },
+ "termsOfService": "Termos de Serviço",
+ "@termsOfService": {
+ "description": "Texto exibido no pop-up de informações do jogo"
+ },
+ "enter": "Entrar",
+ "@enter": {
+ "description": "Texto exibido na página de envio de pontuação"
+ },
+ "initialsBlacklistedMessage": "Mantenha PG, use iniciais diferentes",
+ "@initialsBlacklistedMessage": {
+ "description": "Texto exibido na página de envio de pontuação"
+ },
+ "initialsErrorMessage": "Por favor, insira três iniciais",
+ "@initialsErrorMessage": {
+ "description": "Texto exibido na página de envio de pontuação"
+ },
+ "scoreSubmissionErrorMessage": "Houve um erro ao enviar sua pontuação",
+ "@scoreSubmissionErrorMessage": {
+ "description": "Texto exibido na página de envio de pontuação"
+ },
+ "shareYourScore": "Compartilhe sua pontuação do Super Dash e desafie seus amigos a fazerem mais!",
+ "@shareYourScore": {
+ "description": "Texto exibido na página de compartilhamento"
+ },
+ "pts": "Pts",
+ "@pts": {
+ "description": "Texto exibido na página de compartilhamento"
+ },
+ "shareOn": "Compartilhar em:",
+ "@shareOn": {
+ "description": "Texto exibido na página de compartilhamento"
+ },
+ "share": "Compartilhar",
+ "@share": {
+ "description": "Texto exibido na página de compartilhamento"
+ },
+ "seeTheRanking": "Ver o ranking",
+ "@seeTheRanking": {
+ "description": "Texto exibido na página de compartilhamento"
+ },
+ "gameOver": "Fim de Jogo!",
+ "@gameOver": {
+ "description": "Texto exibido na página de fim de jogo"
+ },
+ "betterLuckNextTime": "Boa sorte na próxima vez.",
+ "@betterLuckNextTime": {
+ "description": "Texto exibido na página de fim de jogo"
+ },
+ "totalScore": "Pontuação Total",
+ "@totalScore": {
+ "description": "Texto exibido na página de fim de jogo"
+ },
+ "submitScore": "Enviar pontuação",
+ "@submitScore": {
+ "description": "Texto exibido na página de fim de jogo"
+ },
+ "playAgain": "Jogar Novamente",
+ "@playAgain": {
+ "description": "Texto exibido na página de fim de jogo"
+ },
+ "gameScoreLabel": "{points} Pts",
+ "@gameScoreLabel": {
+ "description": "Texto exibido no rótulo da pontuação do jogo",
+ "placeholders": {
+ "points": {
+ "type": "int"
+ }
+ }
+ },
+ "leaderboardPageLeaderboardHeadline": "Placar",
+ "@leaderboardPageLeaderboardHeadline": {
+ "description": "Texto exibido no cabeçalho da página de placar"
+ },
+ "leaderboardPageLeaderboardErrorText": "Houve um erro ao buscar o placar.",
+ "@leaderboardPageLeaderboardErrorText": {
+ "description": "Texto exibido na página de placar quando há um erro"
+ },
+ "leaderboardPageLeaderboardNoEntries": "Sem entradas",
+ "@leaderboardPageLeaderboardNoEntries": {
+ "description": "Texto exibido na página de placar quando não há entradas"
+ },
+ "leaderboardPageGoBackButton": "Voltar",
+ "@leaderboardPageGoBackButton": {
+ "description": "Texto exibido no botão de voltar da página de placar"
+ },
+ "tapToStart": "Toque/pressione Espaço para começar",
+ "@tapToStart": {
+ "description": "Texto exibido na página do jogo"
+ },
+ "downloadAppMessage": "Voe no Super Dash, evite os bugs, colete pontos e veja o quão longe você pode ir!",
+ "@downloadAppMessage": {
+ "description": "Texto exibido na página de download do aplicativo móvel"
+ },
+ "downloadAppLabel": "Baixe o aplicativo",
+ "@downloadAppLabel": {
+ "description": "Rótulo do botão para o download do aplicativo"
+ },
+ "selectLanguage": "Selecione o idioma",
+ "@selectLanguage": {
+ "description": "Texto mostrado no overlay de selecionar idioma"
+ }
+}
\ No newline at end of file