Skip to content

Commit

Permalink
Add option to change the language between English and Spanish.
Browse files Browse the repository at this point in the history
  • Loading branch information
Alejandro-FA committed Oct 29, 2024
1 parent 6aabc56 commit 5f013b8
Show file tree
Hide file tree
Showing 14 changed files with 222 additions and 89 deletions.
2 changes: 1 addition & 1 deletion l10n.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
use-deferred-loading: true
use-deferred-loading: false
nullable-getter: false
format: true
19 changes: 19 additions & 0 deletions lib/controllers/localization_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import 'dart:ui';

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'localization_controller.g.dart';

@riverpod
class LocalizationController extends _$LocalizationController {
static const locales = [
Locale('en'),
Locale('es'),
];

@override
// Consider returning null to use the system locale by default
Locale build() => locales[0];

void toggleLocale() => state = state == locales[0] ? locales[1] : locales[0];
}
27 changes: 27 additions & 0 deletions lib/controllers/localization_controller.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,10 @@
"@contentPath": {
"description": "The base path to content files"
},
"cvFile": "alejandro_fernandez_cv-en.pdf"
"cvFile": "alejandro_fernandez_cv-en.pdf",
"research": "Research",
"projects": "Projects",
"cv": "Curriculum Vitae",
"experience": "Experience",
"education": "Education"
}
7 changes: 6 additions & 1 deletion lib/l10n/app_es.arb
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"contentPath": "assets/content/es/",
"cvFile": "alejandro_fernandez_cv-en.pdf"
"cvFile": "alejandro_fernandez_cv-en.pdf",
"research": "Investigación",
"projects": "Proyectos",
"cv": "Curriculum Vitae",
"experience": "Experiencia",
"education": "Educación"
}
6 changes: 4 additions & 2 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_web_plugins/url_strategy.dart';

import 'controllers/localization_controller.dart';
import 'navigation/router.dart';
import 'theme/material_theme.dart';
import 'theme/text_theme.dart';
Expand All @@ -14,13 +15,13 @@ void main() {
runApp(ProviderScope(child: MyApp(router: AppRouter())));
}

class MyApp extends StatelessWidget {
class MyApp extends ConsumerWidget {
const MyApp({required this.router, super.key});

final AppRouter router;

@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
final textTheme = createTextTheme(context, 'Noto Sans', 'Silkscreen');
final theme = MaterialTheme(textTheme);

Expand All @@ -34,6 +35,7 @@ class MyApp extends StatelessWidget {
themeMode: ThemeMode.system,
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
locale: ref.watch(localizationControllerProvider),
);
}
}
10 changes: 8 additions & 2 deletions lib/pages/cv.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ class CVPage extends ConsumerWidget {
Padding(
// Check: https://medium.com/geekculture/dynamically-pinned-list-headers-ee5aa23f1db4
padding: const EdgeInsets.symmetric(vertical: 20),
child: Text('Education', style: textTheme.displaySmall),
child: Text(
AppLocalizations.of(context).education,
style: textTheme.displaySmall,
),
),
Timeline(
lineColor: theme.colorScheme.surfaceContainerHighest,
Expand Down Expand Up @@ -88,7 +91,10 @@ After completing my Baccalaureate studies with an honorary distinction, I decide
horizontal: 0,
vertical: 20,
),
child: Text('Experience', style: textTheme.displaySmall),
child: Text(
AppLocalizations.of(context).experience,
style: textTheme.displaySmall,
),
),
Timeline(
lineColor: theme.colorScheme.surfaceContainerHighest,
Expand Down
2 changes: 1 addition & 1 deletion lib/repositories/file_repository.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 43 additions & 0 deletions lib/widgets/language_toggle_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../controllers/localization_controller.dart';

class LanguageToggleButton extends ConsumerWidget {
const LanguageToggleButton({super.key});

@override
Widget build(BuildContext context, WidgetRef ref) {
final locale = ref.watch(localizationControllerProvider);
final textTheme = Theme.of(context).textTheme;
final isEnglish = locale.languageCode == 'en';

return TextButton(
onPressed: () {
ref.read(localizationControllerProvider.notifier).toggleLocale();
},
child: Row(
children: [
Text(
'EN',
style: textTheme.bodyMedium?.copyWith(
fontWeight: isEnglish ? FontWeight.bold : null,
decoration: isEnglish ? TextDecoration.underline : null,
decorationColor: textTheme.bodyMedium?.color,
),
),
// Text('|', style: textTheme.bodySmall),
const SizedBox(height: 12, child: VerticalDivider(thickness: 1.5)),
Text(
'ES',
style: textTheme.bodyMedium?.copyWith(
fontWeight: !isEnglish ? FontWeight.bold : null,
decoration: !isEnglish ? TextDecoration.underline : null,
decorationColor: textTheme.bodyMedium?.color,
),
),
],
),
);
}
}
40 changes: 20 additions & 20 deletions lib/widgets/page_scaffold.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

import '../models/route_data.dart';
import '../models/social_media_data.dart';
Expand All @@ -21,24 +22,6 @@ class PageScaffold extends StatelessWidget {
final bool socialMediaRail;
final Widget? floatingActionButton;

static const menuRoutes = [
RouteData(
name: 'Research',
path: '/research',
icon: Icons.article,
),
RouteData(
name: 'Projects',
path: '/projects',
icon: Icons.terminal,
),
RouteData(
name: 'Curriculum Vitae',
path: '/cv',
icon: Icons.school,
),
];

static const socialMedia = [
SocialMediaData(
url: 'https://github.com/Alejandro-FA',
Expand All @@ -57,13 +40,30 @@ class PageScaffold extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final menuRoutes = [
RouteData(
name: AppLocalizations.of(context).research,
path: '/research',
icon: Icons.article,
),
RouteData(
name: AppLocalizations.of(context).projects,
path: '/projects',
icon: Icons.terminal,
),
RouteData(
name: AppLocalizations.of(context).cv,
path: '/cv',
icon: Icons.school,
),
];

return Title(
title: title,
color: theme.colorScheme.primary,
child: Scaffold(
floatingActionButton: floatingActionButton,
drawer: const MyNavigationDrawer(
drawer: MyNavigationDrawer(
menuRoutes: menuRoutes,
socialMedia: socialMedia,
),
Expand All @@ -76,7 +76,7 @@ class PageScaffold extends StatelessWidget {
CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: [
const MySliverAppBar(menuRoutes: menuRoutes),
MySliverAppBar(menuRoutes: menuRoutes),
...slivers,
],
),
Expand Down
33 changes: 22 additions & 11 deletions lib/widgets/sliver_app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import '../models/route_data.dart';
import '../theme/material_window_class.dart';
import 'better_link.dart';
import 'home_button.dart';
import 'language_toggle_button.dart';

class MySliverAppBar extends StatelessWidget {
const MySliverAppBar({required this.menuRoutes, super.key});
Expand All @@ -13,6 +14,26 @@ class MySliverAppBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
final actions = isWideScreen(context)
? [
...menuRoutes.map((route) => _MenuButton(route: route)),
const Padding(
padding: EdgeInsets.only(left: 8),
child: LanguageToggleButton(),
),
]
: [
const Padding(
padding: EdgeInsets.only(right: 8),
child: LanguageToggleButton(),
),
IconButton(
icon: const Icon(Icons.menu),
onPressed: () {
Scaffold.of(context).openDrawer();
},
),
];

return SliverAppBar(
pinned: false,
Expand All @@ -26,17 +47,7 @@ class MySliverAppBar extends StatelessWidget {
padding: const EdgeInsets.all(16),
),
titleSpacing: 0,
actions: [
if (isWideScreen(context))
...menuRoutes.map((route) => _MenuButton(route: route))
else
IconButton(
icon: const Icon(Icons.menu),
onPressed: () {
Scaffold.of(context).openDrawer();
},
),
],
actions: actions,
);
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"generate-sitemap": "tsx scripts/generate_sitemap.ts lib/pages/*.dart --baseUrl ${npm_package_config_url}",
"minify:js": "esbuild 'build/web/**/*.js' --outdir=build/web/ --minify --tree-shaking=true --allow-overwrite",
"minify:mjs": "esbuild 'build/web/**/*.mjs' --outdir=build/web/ --minify --tree-shaking=true --allow-overwrite --out-extension:.js=.mjs",
"code-generation": "dart run build_runner build"
"code-generation": "dart run build_runner build && flutter gen-l10n"
},
"repository": {
"type": "git",
Expand Down
64 changes: 64 additions & 0 deletions test/responsive_test.dart.bak
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import 'package:portfolio/models/route_data.dart';
import 'package:portfolio/widgets/sliver_app_bar.dart';

const routesEnglish = [
RouteData(
name: 'Research',
path: '/research',
icon: Icons.article,
),
RouteData(
name: 'Projects',
path: '/projects',
icon: Icons.terminal,
),
RouteData(
name: 'Curriculum Vitae',
path: '/cv',
icon: Icons.school,
),
];

void main() {
testWidgets('ResponsiveAppBar adapts based on screen width', (tester) async {
tester.view.devicePixelRatio = 1.0;
const appTitle = 'Alejandro';

// Build our app and trigger a frame.
// Build our app with English top bar and trigger a frame.
await tester.pumpWidget(
const MaterialApp(
home: Scaffold(
body: CustomScrollView(
physics: BouncingScrollPhysics(),
slivers: [
MySliverAppBar(menuRoutes: routesEnglish),
],
),
),
),
);

// Test actions visibility when screen width is greater than the breakpoint
tester.view.physicalSize = const Size(840, 600);
await tester.pump();
expect(find.byIcon(Icons.menu), findsNothing);
expect(find.text(appTitle), findsOneWidget);

// Test actions visibility when screen width is less than the breakpoint
tester.view.physicalSize = const Size(839, 600);
await tester.pump();
expect(find.byIcon(Icons.menu), findsOneWidget);
expect(find.text(appTitle), findsOneWidget);
});
}
Loading

0 comments on commit 5f013b8

Please sign in to comment.