Initial stab at swapping locales, work in progress

- stubbed in button for testing locale switching, needs design polish and direction on whether the button is available at all times or just in menu
- tested and fixed switching on the fly for wonder views, intro, timeline, artifacts, and menu
This commit is contained in:
Eddie 2022-09-01 14:45:55 -06:00
parent 4f55aa2536
commit 466fe074b8
11 changed files with 120 additions and 63 deletions

View File

@ -1,5 +1,6 @@
{ {
"appName": "Wonderous", "appName": "Wonderous",
"localeSwapButton": "简体中文",
"animatedArrowSemanticSwipe": "Explore details about {title}.", "animatedArrowSemanticSwipe": "Explore details about {title}.",
"appBarTitleFactsHistory": "Facts and History", "appBarTitleFactsHistory": "Facts and History",
"appBarTitleConstruction": "Construction", "appBarTitleConstruction": "Construction",

View File

@ -1,5 +1,6 @@
{ {
"appName": "Wonderous", "appName": "Wonderous",
"localeSwapButton": "English",
"animatedArrowSemanticSwipe": "查看关于{title}的详细信息。", "animatedArrowSemanticSwipe": "查看关于{title}的详细信息。",
"appBarTitleFactsHistory": "历史与细节", "appBarTitleFactsHistory": "历史与细节",
"appBarTitleConstruction": "建造", "appBarTitleConstruction": "建造",

View File

@ -30,8 +30,11 @@ class AppLogic {
// Localizations // Localizations
await localeLogic.load(); await localeLogic.load();
// Data load
wondersLogic.init();
// Timeline // Timeline
await timelineLogic.init(); timelineLogic.init();
// Settings // Settings
await settingsLogic.load(); await settingsLogic.load();

View File

@ -3,6 +3,7 @@ import 'dart:ui';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:intl/intl_standalone.dart'; import 'package:intl/intl_standalone.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:wonders/common_libs.dart';
class LocaleLogic { class LocaleLogic {
AppLocalizations? _strings; AppLocalizations? _strings;
@ -20,6 +21,13 @@ class LocaleLogic {
if (AppLocalizations.supportedLocales.contains(locale) == false) { if (AppLocalizations.supportedLocales.contains(locale) == false) {
locale = Locale('en'); locale = Locale('en');
} }
settingsLogic.currentLocale.value = locale.languageCode;
_strings = await AppLocalizations.delegate.load(locale); _strings = await AppLocalizations.delegate.load(locale);
} }
Future<void> refreshIfChanged(Locale locale) async {
if (_strings?.localeName != locale.languageCode && AppLocalizations.supportedLocales.contains(locale)) {
_strings = await AppLocalizations.delegate.load(locale);
}
}
} }

View File

@ -7,6 +7,7 @@ class SettingsLogic with ThrottledSaveLoadMixin {
late final hasCompletedOnboarding = ValueNotifier<bool>(false)..addListener(scheduleSave); late final hasCompletedOnboarding = ValueNotifier<bool>(false)..addListener(scheduleSave);
late final hasDismissedSearchMessage = ValueNotifier<bool>(false)..addListener(scheduleSave); late final hasDismissedSearchMessage = ValueNotifier<bool>(false)..addListener(scheduleSave);
late final currentLocale = ValueNotifier<String>('en')..addListener(scheduleSave);
final bool useBlurs = defaultTargetPlatform != TargetPlatform.android; final bool useBlurs = defaultTargetPlatform != TargetPlatform.android;

View File

@ -3,18 +3,17 @@ import 'package:wonders/logic/common/string_utils.dart';
import 'package:wonders/logic/data/timeline_data.dart'; import 'package:wonders/logic/data/timeline_data.dart';
class TimelineLogic { class TimelineLogic {
final List<TimelineEvent> events = []; List<TimelineEvent> events = [];
Future<void> init() async { void init() {
events.addAll(GlobalEventsData().globalEvents); events = [
...GlobalEventsData().globalEvents,
for (var w in wondersLogic.all) { ...wondersLogic.all.map(
events.add( (w) => TimelineEvent(
TimelineEvent(
w.startYr, w.startYr,
StringUtils.supplant($strings.timelineLabelConstruction, {'{title}': w.title}), StringUtils.supplant($strings.timelineLabelConstruction, {'{title}': w.title}),
), ),
); )
} ];
} }
} }

View File

@ -10,16 +10,7 @@ import 'package:wonders/logic/data/wonders_data/pyramids_giza_data.dart';
import 'package:wonders/logic/data/wonders_data/taj_mahal_data.dart'; import 'package:wonders/logic/data/wonders_data/taj_mahal_data.dart';
class WondersLogic { class WondersLogic {
late List<WonderData> all = [ List<WonderData> all = [];
GreatWallData(),
PetraData(),
ColosseumData(),
ChichenItzaData(),
MachuPicchuData(),
TajMahalData(),
ChristRedeemerData(),
PyramidsGizaData(),
];
final int timelineStartYear = -3000; final int timelineStartYear = -3000;
final int timelineEndYear = 2200; final int timelineEndYear = 2200;
@ -29,4 +20,17 @@ class WondersLogic {
if (result == null) throw ('Could not find data for wonder type $value'); if (result == null) throw ('Could not find data for wonder type $value');
return result; return result;
} }
void init() {
all = [
GreatWallData(),
PetraData(),
ColosseumData(),
ChichenItzaData(),
MachuPicchuData(),
TajMahalData(),
ChristRedeemerData(),
PyramidsGizaData(),
];
}
} }

View File

@ -30,19 +30,25 @@ class WondersApp extends StatelessWidget {
const WondersApp({Key? key}) : super(key: key); const WondersApp({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp.router( return ValueListenableBuilder<String>(
debugShowCheckedModeBanner: false, valueListenable: settingsLogic.currentLocale,
routerDelegate: appRouter.routerDelegate, builder: (_, localeCode, __) {
routeInformationProvider: appRouter.routeInformationProvider, return MaterialApp.router(
routeInformationParser: appRouter.routeInformationParser, locale: Locale(localeCode),
theme: ThemeData(fontFamily: $styles.text.body.fontFamily), debugShowCheckedModeBanner: false,
localizationsDelegates: const [ routerDelegate: appRouter.routerDelegate,
AppLocalizations.delegate, routeInformationProvider: appRouter.routeInformationProvider,
GlobalMaterialLocalizations.delegate, routeInformationParser: appRouter.routeInformationParser,
GlobalWidgetsLocalizations.delegate, theme: ThemeData(fontFamily: $styles.text.body.fontFamily),
GlobalCupertinoLocalizations.delegate, localizationsDelegates: const [
], AppLocalizations.delegate,
supportedLocales: AppLocalizations.supportedLocales, GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: AppLocalizations.supportedLocales,
);
}
); );
} }
} }

View File

@ -5,6 +5,15 @@ class WondersAppScaffold extends StatelessWidget with GetItMixin {
WondersAppScaffold({Key? key, required this.child}) : super(key: key); WondersAppScaffold({Key? key, required this.child}) : super(key: key);
final Widget child; final Widget child;
Future<void> _handleSwapLocale() async {
final currentLocale = settingsLogic.currentLocale.value;
final newLocale = Locale(currentLocale == 'en' ? 'zh' : 'en');
settingsLogic.currentLocale.value = newLocale.languageCode;
await localeLogic.refreshIfChanged(newLocale);
wondersLogic.init();
timelineLogic.init();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Animate.defaultDuration = $styles.times.fast; Animate.defaultDuration = $styles.times.fast;
@ -22,6 +31,24 @@ class WondersAppScaffold extends StatelessWidget with GetItMixin {
), ),
), ),
), ),
//TODO: just some test UI to check swapping behavior, need to get finalized design and location
Align(
alignment: Alignment.topRight,
child: SafeArea(
child: TextButton(
onPressed: _handleSwapLocale,
child: Container(
decoration: BoxDecoration(
color: $styles.colors.greyStrong.withOpacity(.7),
borderRadius: BorderRadius.all(Radius.circular($styles.corners.md)),
),
padding: EdgeInsets.all($styles.insets.sm),
child: Text($strings.localeSwapButton, style: $styles.text.btn.copyWith(color: $styles.colors.white)),
),
),
),
),
], ],
); );
} }

View File

@ -106,31 +106,35 @@ class HomeMenu extends StatelessWidget {
} }
Widget _buildBottomBtns(BuildContext context) { Widget _buildBottomBtns(BuildContext context) {
return SeparatedColumn( return ValueListenableBuilder(
separatorBuilder: () => Divider(thickness: 1.5, height: 1).animate().scale( valueListenable: settingsLogic.currentLocale,
duration: $styles.times.slow, builder: (_, __, ___) {
delay: $styles.times.pageTransition + 200.ms, return SeparatedColumn(
curve: Curves.easeOutBack, separatorBuilder: () => Divider(thickness: 1.5, height: 1).animate().scale(
), duration: $styles.times.slow,
children: [ delay: $styles.times.pageTransition + 200.ms,
_MenuTextBtn( curve: Curves.easeOutBack,
label: $strings.homeMenuButtonExplore, ),
icon: AppIcons.timeline, children: [
onPressed: () => _handleTimelinePressed(context)), _MenuTextBtn(
_MenuTextBtn( label: $strings.homeMenuButtonExplore,
label: $strings.homeMenuButtonView, icon: AppIcons.timeline,
icon: AppIcons.collection, onPressed: () => _handleTimelinePressed(context)),
onPressed: () => _handleCollectionPressed(context)), _MenuTextBtn(
_MenuTextBtn( label: $strings.homeMenuButtonView,
label: $strings.homeMenuButtonAbout, icon: AppIcons.collection,
icon: AppIcons.info, onPressed: () => _handleCollectionPressed(context)),
onPressed: () => _handleAboutPressed(context), _MenuTextBtn(
), label: $strings.homeMenuButtonAbout,
] icon: AppIcons.info,
.animate(interval: 50.ms) onPressed: () => _handleAboutPressed(context),
.fade(delay: $styles.times.pageTransition + 50.ms) ),
.slide(begin: Offset(0, .1), curve: Curves.easeOut), ]
); .animate(interval: 50.ms)
.fade(delay: $styles.times.pageTransition + 50.ms)
.slide(begin: Offset(0, .1), curve: Curves.easeOut),
);
});
} }
Widget _buildGridBtn(BuildContext context, WonderData btnData) { Widget _buildGridBtn(BuildContext context, WonderData btnData) {

View File

@ -19,11 +19,7 @@ class _IntroScreenState extends State<IntroScreen> {
static const double _textHeight = 155; static const double _textHeight = 155;
static const double _pageIndicatorHeight = 55; static const double _pageIndicatorHeight = 55;
static List<_PageData> pageData = [ static List<_PageData> pageData = [];
_PageData($strings.introTitleJourney, $strings.introDescriptionNavigate, 'camel', '1'),
_PageData($strings.introTitleExplore, $strings.introDescriptionUncover, 'petra', '2'),
_PageData($strings.introTitleDiscover, $strings.introDescriptionLearn, 'statue', '3'),
];
late final PageController _pageController = PageController()..addListener(_handlePageChanged); late final PageController _pageController = PageController()..addListener(_handlePageChanged);
final ValueNotifier<int> _currentPage = ValueNotifier(0); final ValueNotifier<int> _currentPage = ValueNotifier(0);
@ -51,6 +47,13 @@ class _IntroScreenState extends State<IntroScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Set the page data, as strings may have changed based on locale
pageData = [
_PageData($strings.introTitleJourney, $strings.introDescriptionNavigate, 'camel', '1'),
_PageData($strings.introTitleExplore, $strings.introDescriptionUncover, 'petra', '2'),
_PageData($strings.introTitleDiscover, $strings.introDescriptionLearn, 'statue', '3'),
];
// This view uses a full screen PageView to enable swipe navigation. // This view uses a full screen PageView to enable swipe navigation.
// However, we only want the title / description to actually swipe, // However, we only want the title / description to actually swipe,
// so we stack a PageView with that content over top of all the other // so we stack a PageView with that content over top of all the other