diff --git a/lib/app/app_widget.dart b/lib/app/app_widget.dart index 0e2d792..0404acb 100644 --- a/lib/app/app_widget.dart +++ b/lib/app/app_widget.dart @@ -1,5 +1,9 @@ +import 'package:corona_data/app/shared/config/config.dart'; +import 'package:corona_data/app/shared/utils/localization/constants.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_modular/flutter_modular.dart'; +import 'package:i18n_extension/i18n_widget.dart'; import 'package:mobx/mobx.dart'; import 'app_controller.dart'; @@ -11,21 +15,61 @@ class AppWidget extends StatefulWidget { class _AppWidgetState extends State { final AppController controller = Modular.get(); + final GlobalKey _rootKey = GlobalKey(); + List disposers = []; @override void initState() { super.initState(); - reaction((_) => controller.globalSettingsController.theme, (_) => (setState((){}))); + + disposers.add( + reaction( + (_) => controller.globalSettingsController.theme, + (_) => (setState(() {})), + ), + ); + + disposers.add( + reaction( + (_) => controller.globalSettingsController.locale, + (_) { + I18n.of(_rootKey.currentContext).locale = + controller.globalSettingsController.locale.getLocale(); + }, + ), + ); } @override Widget build(BuildContext context) { return MaterialApp( navigatorKey: Modular.navigatorKey, - title: 'Flutter Slidy', - theme: controller.globalSettingsController.theme, + title: 'C19 Tracker', + theme: controller.globalSettingsController.theme.themeData, initialRoute: '/', onGenerateRoute: Modular.generateRoute, + localizationsDelegates: [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: [ + const Locale('en', "US"), + const Locale('pt', "BR"), + ], + builder: (context, widget) { + return I18n( + initialLocale: + controller.globalSettingsController.locale.getLocale(), + child: Container(key: _rootKey, child: widget)); + }, ); } + + @override + void dispose() { + disposers.forEach((disposer) => disposer()); + + super.dispose(); + } } diff --git a/lib/app/modules/charts/widgets/world_cases/world_cases_controller.dart b/lib/app/modules/charts/widgets/world_cases/world_cases_controller.dart index 360fc90..c91f7c2 100644 --- a/lib/app/modules/charts/widgets/world_cases/world_cases_controller.dart +++ b/lib/app/modules/charts/widgets/world_cases/world_cases_controller.dart @@ -16,7 +16,7 @@ abstract class _WorldCasesControllerBase with Store implements IChartController final HistoricalRepository historicalRepository; _WorldCasesControllerBase(this.historicalRepository){ - print("W---"); + fetchGraphData(); } diff --git a/lib/app/modules/country/country_page.dart b/lib/app/modules/country/country_page.dart index 3a18ef8..62cce71 100644 --- a/lib/app/modules/country/country_page.dart +++ b/lib/app/modules/country/country_page.dart @@ -6,9 +6,9 @@ import 'package:corona_data/app/shared/widgets/try_again_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_modular/flutter_modular.dart'; - import 'country_controller.dart'; + class CountryPage extends StatefulWidget { final String title; final AnimationController controller; diff --git a/lib/app/modules/country/country_page_stagger_animation.dart b/lib/app/modules/country/country_page_stagger_animation.dart index 11c6d10..9732b35 100644 --- a/lib/app/modules/country/country_page_stagger_animation.dart +++ b/lib/app/modules/country/country_page_stagger_animation.dart @@ -9,7 +9,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_modular/flutter_modular.dart'; import '../../app_controller.dart'; - +import "translations/country_page.i18n.dart"; class CoutryPageStaggerAnimation extends StatelessWidget { final AnimationController controller; final InfoModel info; @@ -54,7 +54,7 @@ class CoutryPageStaggerAnimation extends StatelessWidget { title: "Número de Mortes", todayNum: "${info.todayDeaths}", percentageBadge: PercentageBadgeWidget( - color: Color(0xffFF5656), + color: appController.globalSettingsController.theme.extraPallete.error, percentage: (info.deaths) / (info.cases), ), ), @@ -63,7 +63,7 @@ class CoutryPageStaggerAnimation extends StatelessWidget { number: "${info.recovered}", title: "Pacientes recuperados", percentageBadge: PercentageBadgeWidget( - color: Color(0xff40CA87), + color: appController.globalSettingsController.theme.extraPallete.success, percentage: (info.recovered) / (info.cases), ), ), @@ -72,7 +72,7 @@ class CoutryPageStaggerAnimation extends StatelessWidget { number: "${info.critical}", title: "Pacientes em estado grave", percentageBadge: PercentageBadgeWidget( - color: Color(0xffDBC716), + color: appController.globalSettingsController.theme.extraPallete.warning, percentage: (info.critical) / (info.cases), ), ), diff --git a/lib/app/modules/country/translations/country_page.i18n.dart b/lib/app/modules/country/translations/country_page.i18n.dart new file mode 100644 index 0000000..58ac63e --- /dev/null +++ b/lib/app/modules/country/translations/country_page.i18n.dart @@ -0,0 +1,12 @@ +import 'package:i18n_extension/i18n_extension.dart'; + +extension Localization on String { + + static var _t = Translations("en_us") + + { + "en_us": "Brazil", + "pt_br": "Brasil", + }; + + String get i18n => localize(this, _t); +} \ No newline at end of file diff --git a/lib/app/modules/home/home_controller.dart b/lib/app/modules/home/home_controller.dart index c57d00d..63b3d46 100644 --- a/lib/app/modules/home/home_controller.dart +++ b/lib/app/modules/home/home_controller.dart @@ -1,7 +1,9 @@ import 'package:corona_data/app/modules/country/country_module.dart'; +import 'package:corona_data/app/modules/settings/global_settings_controller.dart'; import 'package:corona_data/app/modules/states_map/states_map_module.dart'; import 'package:corona_data/app/modules/world/world_module.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_modular/flutter_modular.dart'; import 'package:mobx/mobx.dart'; part 'home_controller.g.dart'; @@ -11,11 +13,12 @@ class HomeController = _HomeControllerBase with _$HomeController; abstract class _HomeControllerBase with Store { @observable int selectedIndex; + GlobalSettingsController globalSettingsController = Modular.get(); - final _titleName = ["country", "Mundo", "Mapa"]; + final _titleName = ["country", "World", "Map"]; @computed - String get title => _titleName[selectedIndex]; + String get title => _titleName[selectedIndex]=='country'?globalSettingsController.country.value.name:_titleName[selectedIndex]; final List _pages = [ CountryModule(), diff --git a/lib/app/modules/home/home_page.dart b/lib/app/modules/home/home_page.dart index ee8befc..156d2c4 100644 --- a/lib/app/modules/home/home_page.dart +++ b/lib/app/modules/home/home_page.dart @@ -5,6 +5,7 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_modular/flutter_modular.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'translations/home_page.i18n.dart'; class HomePage extends StatefulWidget { HomePage({Key key}) : super(key: key); @@ -15,9 +16,16 @@ class HomePage extends StatefulWidget { class _HomePageState extends ModularState { final AppController appController = Modular.get(); + @override + void initState() { + // TODO: implement initState + super.initState(); + + } @override Widget build(BuildContext context) { + return Observer( builder: (BuildContext context) { @@ -26,9 +34,7 @@ class _HomePageState extends ModularState { backgroundColor: Theme.of(context).backgroundColor, appBar: AppBar( title: Text( - controller.title == 'country' - ? appController.globalSettingsController.country.value.name - : controller.title, + controller.title.i18n, style: GoogleFonts.robotoSlab( fontSize: 24, letterSpacing: -1.9, diff --git a/lib/app/modules/home/translations/home_page.i18n.dart b/lib/app/modules/home/translations/home_page.i18n.dart new file mode 100644 index 0000000..ea4841d --- /dev/null +++ b/lib/app/modules/home/translations/home_page.i18n.dart @@ -0,0 +1,24 @@ +import 'package:i18n_extension/i18n_extension.dart'; + +extension Localization on String { + + static var _t = Translations("en_us") + + { + "en_us": "Brazil", + "pt_br": "Brasil", + }+ + { + "en_us": "United States", + "pt_br": "Estados Unidos", + }+ + { + "en_us": "Map", + "pt_br": "Mapa", + }+ + { + "en_us": "World", + "pt_br": "Mundo", + }; + + String get i18n => localize(this, _t); +} \ No newline at end of file diff --git a/lib/app/modules/settings/global_settings_controller.dart b/lib/app/modules/settings/global_settings_controller.dart index c4ba581..7869245 100644 --- a/lib/app/modules/settings/global_settings_controller.dart +++ b/lib/app/modules/settings/global_settings_controller.dart @@ -1,7 +1,9 @@ import 'package:corona_data/app/shared/models/country_model.dart'; import 'package:corona_data/app/shared/repositories/local_storage_interface.dart'; +import 'package:corona_data/app/shared/utils/localization/localization_interface.dart'; +import 'package:corona_data/app/shared/utils/localization/localization_utils.dart'; +import 'package:corona_data/app/shared/utils/theme/theme_interface.dart'; import 'package:corona_data/app/shared/utils/theme/theme_utils.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_modular/flutter_modular.dart'; import 'package:mobx/mobx.dart'; @@ -16,28 +18,40 @@ abstract class _GlobalSettingsControllerBase with Store { @observable ObservableFuture themeName; + @observable + ObservableFuture localeKey; + @observable ObservableFuture country; @computed - ThemeData get theme { - return ThemeUtils.getThemeData(themeName?.value); + ITheme get theme { + return ThemeUtils.getTheme(themeName?.value); } + @computed + ILocalization get locale { + + return LocalizationUtils.getLocale(localeKey?.value); + } + @computed bool get isReady { - return themeName.value != null && country.value != null; + + return themeName.value != null && country.value != null && localeKey.value != null; } @action void init() { getTheme(); getCountry(); + getLocale(); } @computed int get isChanged => - (country.value.name + themeName.value.toString()).hashCode; + + (country.value.name + themeName.value.toString() + localeKey.value).hashCode; @action void getTheme() { @@ -66,6 +80,22 @@ abstract class _GlobalSettingsControllerBase with Store { this.country = localStorage.getCountry().asObservable(); } + @action + void setLocale(String localeKey) { + if (localeKey!= this.localeKey.value) { + this.localeKey = ObservableFuture.value(localeKey); + localStorage.setLocale(localeKey); + + } + } + + @action + void getLocale() { + this.localeKey = localStorage.getLocale().asObservable(); + + } + + } diff --git a/lib/app/modules/settings/global_settings_controller.g.dart b/lib/app/modules/settings/global_settings_controller.g.dart index ce05a4d..0d7dc9f 100644 --- a/lib/app/modules/settings/global_settings_controller.g.dart +++ b/lib/app/modules/settings/global_settings_controller.g.dart @@ -9,11 +9,16 @@ part of 'global_settings_controller.dart'; // ignore_for_file: non_constant_identifier_names, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic mixin _$GlobalSettingsController on _GlobalSettingsControllerBase, Store { - Computed _$themeComputed; + Computed _$themeComputed; @override - ThemeData get theme => - (_$themeComputed ??= Computed(() => super.theme)).value; + ITheme get theme => + (_$themeComputed ??= Computed(() => super.theme)).value; + Computed _$localeComputed; + + @override + ILocalization get locale => + (_$localeComputed ??= Computed(() => super.locale)).value; Computed _$isReadyComputed; @override @@ -42,6 +47,23 @@ mixin _$GlobalSettingsController on _GlobalSettingsControllerBase, Store { }, _$themeNameAtom, name: '${_$themeNameAtom.name}_set'); } + final _$localeKeyAtom = Atom(name: '_GlobalSettingsControllerBase.localeKey'); + + @override + ObservableFuture get localeKey { + _$localeKeyAtom.context.enforceReadPolicy(_$localeKeyAtom); + _$localeKeyAtom.reportObserved(); + return super.localeKey; + } + + @override + set localeKey(ObservableFuture value) { + _$localeKeyAtom.context.conditionallyRunInAction(() { + super.localeKey = value; + _$localeKeyAtom.reportChanged(); + }, _$localeKeyAtom, name: '${_$localeKeyAtom.name}_set'); + } + final _$countryAtom = Atom(name: '_GlobalSettingsControllerBase.country'); @override @@ -117,10 +139,32 @@ mixin _$GlobalSettingsController on _GlobalSettingsControllerBase, Store { } } + @override + void setLocale(String localeKey) { + final _$actionInfo = + _$_GlobalSettingsControllerBaseActionController.startAction(); + try { + return super.setLocale(localeKey); + } finally { + _$_GlobalSettingsControllerBaseActionController.endAction(_$actionInfo); + } + } + + @override + void getLocale() { + final _$actionInfo = + _$_GlobalSettingsControllerBaseActionController.startAction(); + try { + return super.getLocale(); + } finally { + _$_GlobalSettingsControllerBaseActionController.endAction(_$actionInfo); + } + } + @override String toString() { final string = - 'themeName: ${themeName.toString()},country: ${country.toString()},theme: ${theme.toString()},isReady: ${isReady.toString()},isChanged: ${isChanged.toString()}'; + 'themeName: ${themeName.toString()},localeKey: ${localeKey.toString()},country: ${country.toString()},theme: ${theme.toString()},locale: ${locale.toString()},isReady: ${isReady.toString()},isChanged: ${isChanged.toString()}'; return '{$string}'; } } diff --git a/lib/app/modules/settings/settings_controller.dart b/lib/app/modules/settings/settings_controller.dart index 9b1ba52..9eb105f 100644 --- a/lib/app/modules/settings/settings_controller.dart +++ b/lib/app/modules/settings/settings_controller.dart @@ -23,6 +23,6 @@ abstract class _SettingsControllerBase with Store implements Disposable { @override void dispose() { - print("Settings dispose"); + // print("Settings dispose"); } } diff --git a/lib/app/modules/settings/settings_page.dart b/lib/app/modules/settings/settings_page.dart index 34eda87..2e45d4a 100644 --- a/lib/app/modules/settings/settings_page.dart +++ b/lib/app/modules/settings/settings_page.dart @@ -1,10 +1,10 @@ import 'package:autocomplete_textfield/autocomplete_textfield.dart'; import 'package:corona_data/app/app_controller.dart'; -import 'package:corona_data/app/modules/settings/widgets/theme_dropdown.dart'; +import 'package:corona_data/app/modules/settings/widgets/locale/locale_row.dart'; +import 'package:corona_data/app/modules/settings/widgets/theme/theme_row.dart'; import 'package:corona_data/app/shared/models/country_model.dart'; import 'package:corona_data/app/shared/utils/constants.dart'; import 'package:corona_data/app/shared/utils/snackbar_util.dart'; -import 'package:corona_data/app/shared/utils/theme/constants.dart'; import 'package:corona_data/app/shared/utils/widgets/custom_divider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -26,7 +26,7 @@ class _SettingsPageState extends ModularState { AppController appController = Modular.get(); - GlobalKey> key = GlobalKey(); + GlobalKey> _autoCompleteKey = GlobalKey(); final GlobalKey _scaffoldKey = GlobalKey(); SnackBarUtil snackbar; @@ -47,7 +47,7 @@ class _SettingsPageState (value) { snackbar.enqueueMessage( message: 'Settings has been changed!', - color: ThemeColors.success, + color: appController.globalSettingsController.theme.extraPallete.success, id: "SettingsForm"); }); @@ -58,7 +58,7 @@ class _SettingsPageState @override Widget build(BuildContext context) { - key = GlobalKey(); + _autoCompleteKey = GlobalKey(); return Scaffold( key: _scaffoldKey, backgroundColor: Theme.of(context).primaryColorDark, @@ -79,34 +79,14 @@ class _SettingsPageState Container( height: 10, ), - Container( - padding: EdgeInsets.symmetric(horizontal: 20.0), - color: Theme.of(context).primaryColor, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Tema', - style: GoogleFonts.robotoSlab( - color: Theme.of(context).accentColor, fontSize: 16), - ), - Observer(builder: (_) { - - return ThemeDropdown( - value: appController - .globalSettingsController.themeName.value - .toUpperCase(), - onChanged: (String newValue) { - appController.globalSettingsController.setTheme(newValue); - }, - ); - }), - ], - ), + ThemeRow(appController: appController), + Padding( + padding: const EdgeInsets.only(top:20), + child: LocaleRow(appController:appController), ), Observer(builder: (context) { return Padding( - padding: const EdgeInsets.only(top: 10.0), + padding: const EdgeInsets.only(top: 20.0), child: Container( padding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 5.0), color: Theme.of(context).primaryColor, @@ -115,7 +95,7 @@ class _SettingsPageState children: [ SimpleAutoCompleteTextField( style: TextStyle(color: Theme.of(context).accentColor), - key: key, + key: _autoCompleteKey, decoration: InputDecoration( errorText: null, labelText: "País", @@ -133,7 +113,7 @@ class _SettingsPageState textSubmitted: (text) { final int idx = countriesNames.indexOf(text); if (countriesNames.indexOf(text) != -1) { - print(COUNTRIES[idx]); + appController.globalSettingsController .setCountry(CountryModel.fromJson(COUNTRIES[idx])); @@ -166,3 +146,5 @@ class _SettingsPageState } + + diff --git a/lib/app/modules/settings/widgets/locale/locale_dropdown.dart b/lib/app/modules/settings/widgets/locale/locale_dropdown.dart new file mode 100644 index 0000000..4c14855 --- /dev/null +++ b/lib/app/modules/settings/widgets/locale/locale_dropdown.dart @@ -0,0 +1,74 @@ +import 'package:corona_data/app/shared/config/config.dart'; +import 'package:corona_data/app/shared/utils/localization/localization_interface.dart'; +import 'package:corona_data/app/shared/utils/localization/localization_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:google_fonts/google_fonts.dart'; + +class LocaleDropDown extends StatelessWidget { + final String value; + final String countryCode; + final void Function(String newValue) onChanged; + + const LocaleDropDown({Key key, this.value, this.onChanged, this.countryCode}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + + + child: DropdownButton( + value: value, + iconEnabledColor: Theme.of(context).primaryColor, + focusColor: Theme.of(context).primaryColor, + icon: null, + iconSize: 15, + elevation: 16, + style: GoogleFonts.robotoSlab( + color: Theme.of(context).accentColor, + fontSize: 16, + fontWeight: FontWeight.w600), + underline: Container( + height: 0, + color: Theme.of(context).accentColor, + ), + onChanged: onChanged, + selectedItemBuilder: _selectedItemBuilder, + items: _dropdownMenuItem(), + ), + ); + } + + List _selectedItemBuilder(context) { + return LocalizationUtils.getLocaleNamesPretty() + .map( + (locale) => Container( + alignment: Alignment.centerLeft, + height: 35, + width: 35, + child: SvgPicture.asset( + "assets/flags/$countryCode.svg", + ), + ), + ) + .toList(); + } + + List> _dropdownMenuItem(){ + return Configuration.locales.keys.map>((String key) { + ILocalization localeTemp = LocalizationUtils.getLocale(key); + return DropdownMenuItem( + value: key.toUpperCase(), + child: Container( + padding: EdgeInsets.only(top:5), + height: 40, + width: 40, + child: SvgPicture.asset( + "assets/flags/${localeTemp.countryCode.toLowerCase()}.svg", + ), + ), + ); + }).toList(); + + } +} diff --git a/lib/app/modules/settings/widgets/locale/locale_row.dart b/lib/app/modules/settings/widgets/locale/locale_row.dart new file mode 100644 index 0000000..07c0ee5 --- /dev/null +++ b/lib/app/modules/settings/widgets/locale/locale_row.dart @@ -0,0 +1,42 @@ +import 'package:corona_data/app/app_controller.dart'; +import 'package:corona_data/app/modules/settings/widgets/locale/locale_dropdown.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:google_fonts/google_fonts.dart'; + +class LocaleRow extends StatelessWidget { + const LocaleRow({ + Key key, + @required this.appController, + }) : super(key: key); + + final AppController appController; + + @override + Widget build(BuildContext context) { + + return Container( + padding: EdgeInsets.symmetric(horizontal: 20.0), + color: Theme.of(context).primaryColor, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Language', + style: GoogleFonts.robotoSlab( + color: Theme.of(context).accentColor, fontSize: 16), + ), + Observer(builder: (_) { + return LocaleDropDown( + value: appController.globalSettingsController.localeKey.value.toUpperCase(), + countryCode: appController.globalSettingsController.locale.countryCode.toLowerCase(), + onChanged: (String newValue) { + appController.globalSettingsController.setLocale(newValue); + }, + ); + }), + ], + ), + ); + } +} diff --git a/lib/app/modules/settings/widgets/theme_dropdown.dart b/lib/app/modules/settings/widgets/theme/theme_dropdown.dart similarity index 100% rename from lib/app/modules/settings/widgets/theme_dropdown.dart rename to lib/app/modules/settings/widgets/theme/theme_dropdown.dart diff --git a/lib/app/modules/settings/widgets/theme/theme_row.dart b/lib/app/modules/settings/widgets/theme/theme_row.dart new file mode 100644 index 0000000..ba7cb3c --- /dev/null +++ b/lib/app/modules/settings/widgets/theme/theme_row.dart @@ -0,0 +1,43 @@ +import 'package:corona_data/app/app_controller.dart'; +import 'package:corona_data/app/modules/settings/widgets/theme/theme_dropdown.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:google_fonts/google_fonts.dart'; + +class ThemeRow extends StatelessWidget { + const ThemeRow({ + Key key, + @required this.appController, + }) : super(key: key); + + final AppController appController; + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.symmetric(horizontal: 20.0), + color: Theme.of(context).primaryColor, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Tema', + style: GoogleFonts.robotoSlab( + color: Theme.of(context).accentColor, fontSize: 16), + ), + Observer(builder: (_) { + + return ThemeDropdown( + value: appController + .globalSettingsController.themeName.value + .toUpperCase(), + onChanged: (String newValue) { + appController.globalSettingsController.setTheme(newValue); + }, + ); + }), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/app/modules/world/world_page_stagger_animation.dart b/lib/app/modules/world/world_page_stagger_animation.dart index 24e5f76..af71732 100644 --- a/lib/app/modules/world/world_page_stagger_animation.dart +++ b/lib/app/modules/world/world_page_stagger_animation.dart @@ -1,3 +1,4 @@ +import 'package:corona_data/app/app_controller.dart'; import 'package:corona_data/app/shared/models/info_model.dart'; import 'package:corona_data/app/shared/utils/constants.dart'; import 'package:corona_data/app/shared/utils/widgets/summary_header_widget.dart'; @@ -6,10 +7,12 @@ import 'package:corona_data/app/shared/widgets/animations/virus_circular_animati import 'package:corona_data/app/shared/widgets/info_tile_widget.dart'; import 'package:corona_data/app/shared/widgets/percentage_badge_widget.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_modular/flutter_modular.dart'; class WorldPageStaggerAnimation extends StatelessWidget { final AnimationController controller; final InfoModel info; + final AppController appController = Modular.get(); WorldPageStaggerAnimation({Key key, this.controller, this.info}) : super(key: key) { @@ -50,7 +53,7 @@ class WorldPageStaggerAnimation extends StatelessWidget { todayNum: "${info.todayDeaths}", title: "Número de Mortes", percentageBadge: PercentageBadgeWidget( - color: Color(0xffFF5656), + color: appController.globalSettingsController.theme.extraPallete.error, percentage: (info.deaths) / (info.cases), ), ), @@ -59,7 +62,7 @@ class WorldPageStaggerAnimation extends StatelessWidget { number: "${info.recovered}", title: "Pacientes recuperados", percentageBadge: PercentageBadgeWidget( - color: Color(0xff40CA87), + color: appController.globalSettingsController.theme.extraPallete.success, percentage: (info.recovered) / (info.cases), ), ), @@ -68,7 +71,7 @@ class WorldPageStaggerAnimation extends StatelessWidget { number: "${info.critical}", title: "Pacientes em estado grave", percentageBadge: PercentageBadgeWidget( - color: Color(0xffDBC716), + color: appController.globalSettingsController.theme.extraPallete.warning, percentage: (info.critical) / (info.cases), ), ), diff --git a/lib/app/shared/config/config.dart b/lib/app/shared/config/config.dart index 9da662d..394b639 100644 --- a/lib/app/shared/config/config.dart +++ b/lib/app/shared/config/config.dart @@ -1,9 +1,11 @@ +import 'package:corona_data/app/shared/utils/localization/constants.dart'; import 'package:corona_data/app/shared/utils/theme/constants.dart'; -import 'package:flutter/material.dart'; -final Map themes = { - "light": themeLight, - "dark": themeDark, - "dracula": themeDracula, -}; +class Configuration { + static final themes = kThemes; + static const defaultLocaleKey=kPTBR; + static final locales = kLocales; + + const Configuration._(); +} diff --git a/lib/app/shared/repositories/local_storage_hive.dart b/lib/app/shared/repositories/local_storage_hive.dart index 3cdb005..7e30685 100644 --- a/lib/app/shared/repositories/local_storage_hive.dart +++ b/lib/app/shared/repositories/local_storage_hive.dart @@ -1,7 +1,9 @@ import 'dart:async'; import 'dart:io'; +import 'package:corona_data/app/shared/config/config.dart'; import 'package:corona_data/app/shared/models/country_model.dart'; +import 'package:corona_data/app/shared/utils/localization/localization_utils.dart'; import 'package:hive/hive.dart'; import 'package:path_provider/path_provider.dart'; @@ -11,6 +13,7 @@ import 'local_storage_interface.dart'; class LocalStorageHive implements ILocalStorage{ final String _themeKey = 'THEME'; final String _countryKey = 'COUNTRY'; + final String _localeKey = 'LOCALE'; Completer _instance = Completer(); @@ -60,5 +63,23 @@ class LocalStorageHive implements ILocalStorage{ await box.put(_countryKey, value); } + + @override + Future getLocale() async{ + var box = await _instance.future; + + var value = await box.get(_localeKey); + + value ??= Configuration.defaultLocaleKey; + + return value; + } + + @override + Future setLocale(String value) async { + var box = await _instance.future; + + await box.put(_localeKey, value); + } } \ No newline at end of file diff --git a/lib/app/shared/repositories/local_storage_interface.dart b/lib/app/shared/repositories/local_storage_interface.dart index 88d9a29..9a08ef7 100644 --- a/lib/app/shared/repositories/local_storage_interface.dart +++ b/lib/app/shared/repositories/local_storage_interface.dart @@ -4,6 +4,9 @@ abstract class ILocalStorage { Future getTheme(); Future setTheme(String value); + Future getLocale(); + Future setLocale(String value); + Future getCountry(); Future setCountry(CountryModel country); diff --git a/lib/app/shared/utils/localization/constants.dart b/lib/app/shared/utils/localization/constants.dart new file mode 100644 index 0000000..149c19f --- /dev/null +++ b/lib/app/shared/utils/localization/constants.dart @@ -0,0 +1,13 @@ + + +import 'package:corona_data/app/shared/utils/localization/locales/en_us_locale.dart'; +import 'package:corona_data/app/shared/utils/localization/locales/pt_br_locale.dart'; +import 'package:corona_data/app/shared/utils/localization/localization_interface.dart'; + +const kPTBR = "pt_br"; +const kENUS = "en_us"; + +final Map kLocales = { + kPTBR: PTBRLocale(), + kENUS: ENUSLocale(), + }; diff --git a/lib/app/shared/utils/localization/locales/en_us_locale.dart b/lib/app/shared/utils/localization/locales/en_us_locale.dart new file mode 100644 index 0000000..149bf8e --- /dev/null +++ b/lib/app/shared/utils/localization/locales/en_us_locale.dart @@ -0,0 +1,14 @@ +import 'package:corona_data/app/shared/utils/localization/localization_interface.dart'; + +class ENUSLocale extends ILocalization{ + @override + String get countryCode => 'US'; + + @override + String get languageCode => 'en'; + + @override + String get name => 'United States'; + + +} \ No newline at end of file diff --git a/lib/app/shared/utils/localization/locales/pt_br_locale.dart b/lib/app/shared/utils/localization/locales/pt_br_locale.dart new file mode 100644 index 0000000..0ad417d --- /dev/null +++ b/lib/app/shared/utils/localization/locales/pt_br_locale.dart @@ -0,0 +1,13 @@ +import 'package:corona_data/app/shared/utils/localization/localization_interface.dart'; + +class PTBRLocale extends ILocalization{ + @override + String get countryCode => "BR"; + + @override + String get languageCode => 'pt'; + + @override + String get name => 'Brazil'; + +} \ No newline at end of file diff --git a/lib/app/shared/utils/localization/localization_interface.dart b/lib/app/shared/utils/localization/localization_interface.dart new file mode 100644 index 0000000..ded749d --- /dev/null +++ b/lib/app/shared/utils/localization/localization_interface.dart @@ -0,0 +1,11 @@ +import 'package:flutter/material.dart'; + +abstract class ILocalization{ + final String name = null; + final String languageCode = null; + final String countryCode = null; + + Locale getLocale(){ + return Locale(languageCode,countryCode); + } +} \ No newline at end of file diff --git a/lib/app/shared/utils/localization/localization_utils.dart b/lib/app/shared/utils/localization/localization_utils.dart new file mode 100644 index 0000000..6c35a0b --- /dev/null +++ b/lib/app/shared/utils/localization/localization_utils.dart @@ -0,0 +1,29 @@ +import 'package:corona_data/app/shared/config/config.dart'; +import 'package:corona_data/app/shared/utils/localization/locales/pt_br_locale.dart'; +import 'package:corona_data/app/shared/utils/localization/localization_interface.dart'; + +class LocalizationUtils { + const LocalizationUtils._(); + static ILocalization getLocale(String locale) { + ILocalization themeInstance; + locale = locale != null?locale.toLowerCase():''; + + if(locale == null){ + themeInstance = Configuration.locales[Configuration.defaultLocaleKey]; + } + + + else if (Configuration.locales.containsKey(locale)) { + themeInstance = Configuration.locales[locale]; + } else { + themeInstance = Configuration.locales[Configuration.defaultLocaleKey]; + } + return themeInstance; + } + + static List getLocaleNamesPretty(){ + return Configuration.locales.keys.map((name)=>name.toUpperCase()).toList(); + } + + +} \ No newline at end of file diff --git a/lib/app/shared/utils/theme/constants.dart b/lib/app/shared/utils/theme/constants.dart index 78dff87..2ee4abf 100644 --- a/lib/app/shared/utils/theme/constants.dart +++ b/lib/app/shared/utils/theme/constants.dart @@ -1,47 +1,15 @@ -import 'package:flutter/material.dart'; +import 'package:corona_data/app/shared/utils/theme/theme_interface.dart'; +import 'package:corona_data/app/shared/utils/theme/themes/theme_dark.dart'; +import 'package:corona_data/app/shared/utils/theme/themes/theme_dracula.dart'; +import 'package:corona_data/app/shared/utils/theme/themes/theme_light.dart'; +const String kLightThemeKey = "light"; +const String kDarkThemeKey = "dark"; +const String kDraculaThemeKey = "dracula"; -final ThemeData themeLight = ThemeData( - primaryColor: Color(0xffdcddda), - primaryColorLight: Color(0xff83867e), - primaryColorDark: Color(0xffEBE9E7), - accentColor: Color(0xff231F1C), - backgroundColor: Color(0xffEBE9E7), - buttonColor: Color(0xff231F1C), - secondaryHeaderColor: Color(0xffcfd0cd), - -); +final Map kThemes = { + kLightThemeKey: ThemeLight(), + kDarkThemeKey: ThemeDark(), + kDraculaThemeKey: ThemeDracula(), + }; -final ThemeData themeDark = ThemeData( - primaryColor: Color(0xff393e3b), - primaryColorLight: Color(0xff9C9F98), - primaryColorDark: Color(0xff231F1C), - accentColor: Color(0xffEBE9E7), - canvasColor: Color(0xff393e3b), - backgroundColor: Color(0xff231F1C), - secondaryHeaderColor: Color(0xff2C2F26), -); - -final ThemeData themeDracula = ThemeData( - primaryColor: Color(0xff44475a), - primaryColorLight: Color(0xffbd93f9), - primaryColorDark: Color(0xff282a36), - accentColor: Color(0xfff8f8f2), - canvasColor: Color(0xff282a36), - backgroundColor: Color(0xff282a36), - errorColor: Color(0xffff5555), - iconTheme: IconThemeData(color: Color(0xfff8f8f2)), - primaryIconTheme: IconThemeData(color: Color(0xfff8f8f2)), - accentIconTheme: IconThemeData(color: Color(0xfff8f8f2)), - buttonTheme: ButtonThemeData(buttonColor: Color(0xfff1fa8c)), - buttonColor: Color(0xfff1fa8c), - secondaryHeaderColor: Color(0xff44475a), -); - -class ThemeColors{ - const ThemeColors._(); - static const Color success = Colors.green; - static const Color error = Colors.red; - static const Color warning = Colors.orange; - static const Color info = Colors.blue; -} \ No newline at end of file diff --git a/lib/app/shared/utils/theme/extra_pallete.dart b/lib/app/shared/utils/theme/extra_pallete.dart new file mode 100644 index 0000000..c727f1e --- /dev/null +++ b/lib/app/shared/utils/theme/extra_pallete.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class ExtraPallete { + final Color success; + final Color info; + final Color warning; + final Color error; + + ExtraPallete({this.success, this.info, this.warning, this.error}); +} \ No newline at end of file diff --git a/lib/app/shared/utils/theme/theme_interface.dart b/lib/app/shared/utils/theme/theme_interface.dart new file mode 100644 index 0000000..42346a4 --- /dev/null +++ b/lib/app/shared/utils/theme/theme_interface.dart @@ -0,0 +1,25 @@ +import 'package:corona_data/app/shared/utils/theme/extra_pallete.dart'; +import 'package:flutter/material.dart'; + +abstract class ITheme { + ThemeData themeData; + ExtraPallete extraPallete; + + ThemeData getThemeData(); + ExtraPallete getExtraPallete(); + + ITheme initialize(){ + this.themeData = getThemeData(); + this.extraPallete = getExtraPallete(); + + return this; + } +} + + + + + + + + diff --git a/lib/app/shared/utils/theme/theme_utils.dart b/lib/app/shared/utils/theme/theme_utils.dart index 863c7f1..4c2b1dc 100644 --- a/lib/app/shared/utils/theme/theme_utils.dart +++ b/lib/app/shared/utils/theme/theme_utils.dart @@ -1,21 +1,28 @@ import 'package:corona_data/app/shared/config/config.dart'; -import 'package:flutter/material.dart'; +import 'package:corona_data/app/shared/utils/theme/constants.dart'; +import 'package:corona_data/app/shared/utils/theme/theme_interface.dart'; class ThemeUtils { const ThemeUtils._(); - static ThemeData getThemeData(String theme) { - if(theme == null){ - return themes['dark']; + static ITheme getTheme(String themeName) { + ITheme themeInstance; + themeName = themeName != null?themeName.toLowerCase():''; + + if(themeName == null){ + themeInstance = Configuration.themes[kDarkThemeKey]; + } - theme=theme.toLowerCase(); - if (themes.containsKey(theme) || theme == null) { - return themes[theme]; + + + else if (Configuration.themes.containsKey(themeName)) { + themeInstance = Configuration.themes[themeName]; } else { - return themes['dark']; + themeInstance = Configuration.themes[kDarkThemeKey]; } + return themeInstance.initialize(); } static List getThemeNamesPretty(){ - return themes.keys.map((name)=>name.toUpperCase()).toList(); + return Configuration.themes.keys.map((name)=>name.toUpperCase()).toList(); } } \ No newline at end of file diff --git a/lib/app/shared/utils/theme/themes/theme_dark.dart b/lib/app/shared/utils/theme/themes/theme_dark.dart new file mode 100644 index 0000000..ff1c625 --- /dev/null +++ b/lib/app/shared/utils/theme/themes/theme_dark.dart @@ -0,0 +1,27 @@ +import 'package:corona_data/app/shared/utils/theme/extra_pallete.dart'; +import 'package:corona_data/app/shared/utils/theme/theme_interface.dart'; +import 'package:flutter/material.dart'; + +class ThemeDark extends ITheme { + + @override + ExtraPallete getExtraPallete() { + return ExtraPallete( + success: Color(0xff40CA87), + error: Color(0xffFF5656), + warning: Color(0xffDBC716), + info: Colors.blue); + } + + @override + ThemeData getThemeData() { + return ThemeData( + primaryColor: Color(0xff393e3b), + primaryColorLight: Color(0xff9C9F98), + primaryColorDark: Color(0xff231F1C), + accentColor: Color(0xffEBE9E7), + canvasColor: Color(0xff393e3b), + backgroundColor: Color(0xff231F1C), + secondaryHeaderColor: Color(0xff2C2F26)); + } +} \ No newline at end of file diff --git a/lib/app/shared/utils/theme/themes/theme_dracula.dart b/lib/app/shared/utils/theme/themes/theme_dracula.dart new file mode 100644 index 0000000..53dde4d --- /dev/null +++ b/lib/app/shared/utils/theme/themes/theme_dracula.dart @@ -0,0 +1,33 @@ +import 'package:corona_data/app/shared/utils/theme/extra_pallete.dart'; +import 'package:corona_data/app/shared/utils/theme/theme_interface.dart'; +import 'package:flutter/material.dart'; + +class ThemeDracula extends ITheme { + @override + ExtraPallete getExtraPallete() { + return ExtraPallete( + success: Color(0xff50fa7b), + error: Color(0xffff5555), + warning: Color(0xffffb86c), + info: Color(0xff8be9fd)); + } + + @override + ThemeData getThemeData() { + return ThemeData( + primaryColor: Color(0xff44475a), + primaryColorLight: Color(0xffbd93f9), + primaryColorDark: Color(0xff282a36), + accentColor: Color(0xfff8f8f2), + canvasColor: Color(0xff282a36), + backgroundColor: Color(0xff282a36), + errorColor: Color(0xffff5555), + iconTheme: IconThemeData(color: Color(0xfff8f8f2)), + primaryIconTheme: IconThemeData(color: Color(0xfff8f8f2)), + accentIconTheme: IconThemeData(color: Color(0xfff8f8f2)), + buttonTheme: ButtonThemeData(buttonColor: Color(0xfff1fa8c)), + buttonColor: Color(0xfff1fa8c), + secondaryHeaderColor: Color(0xff44475a), + ); + } +} \ No newline at end of file diff --git a/lib/app/shared/utils/theme/themes/theme_light.dart b/lib/app/shared/utils/theme/themes/theme_light.dart new file mode 100644 index 0000000..41d6a97 --- /dev/null +++ b/lib/app/shared/utils/theme/themes/theme_light.dart @@ -0,0 +1,26 @@ +import 'package:corona_data/app/shared/utils/theme/extra_pallete.dart'; +import 'package:corona_data/app/shared/utils/theme/theme_interface.dart'; +import 'package:flutter/material.dart'; + +class ThemeLight extends ITheme { + @override + ExtraPallete getExtraPallete() { + return ExtraPallete( + success: Colors.green, + error: Colors.red, + warning: Colors.orange, + info: Colors.blue); + } + + @override + ThemeData getThemeData() { + return ThemeData( + primaryColor: Color(0xffdcddda), + primaryColorLight: Color(0xff83867e), + primaryColorDark: Color(0xffEBE9E7), + accentColor: Color(0xff231F1C), + backgroundColor: Color(0xffEBE9E7), + buttonColor: Color(0xff231F1C), + secondaryHeaderColor: Color(0xffcfd0cd)); + } +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index f37f9ca..5cc57d4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -216,6 +216,11 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" flutter_mobx: dependency: "direct main" description: @@ -326,6 +331,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.3" + i18n_extension: + dependency: "direct main" + description: + name: i18n_extension + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.5" image: dependency: transitive description: @@ -339,7 +351,7 @@ packages: name: intl url: "https://pub.dartlang.org" source: hosted - version: "0.16.1" + version: "0.16.0" io: dependency: transitive description: @@ -583,6 +595,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.5.5" + sprintf: + dependency: transitive + description: + name: sprintf + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.2" stack_trace: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 0d99396..d53f729 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,11 +14,11 @@ description: A new Flutter project. version: 1.0.0+1 environment: - sdk: ">=2.2.0 <3.0.0" + sdk: ">=2.6.0 <3.0.0" dependencies: flutter_svg: ^0.17.4 - intl: ^0.16.1 + intl: ^0.16.0 number_display: ^2.2.1 modal_bottom_sheet: ^0.1.5 path_provider: ^1.6.5 @@ -33,6 +33,9 @@ dependencies: flutter_modular: ^0.5.6 flare_flutter: ^2.0.2 autocomplete_textfield: ^1.7.3 + i18n_extension: ^1.3.5 + flutter_localizations: + sdk: flutter flutter: sdk: flutter @@ -46,6 +49,7 @@ dev_dependencies: build_runner: ^1.8.0 flutter_test: sdk: flutter + # For information on the generic Dart part of this file, see the diff --git a/test/app/mocks/local_storage_mock.dart b/test/app/mocks/local_storage_mock.dart index 0f143be..8850894 100644 --- a/test/app/mocks/local_storage_mock.dart +++ b/test/app/mocks/local_storage_mock.dart @@ -1,5 +1,7 @@ +import 'package:corona_data/app/shared/config/config.dart'; import 'package:corona_data/app/shared/models/country_model.dart'; import 'package:corona_data/app/shared/repositories/local_storage_interface.dart'; +import 'package:corona_data/app/shared/utils/localization/localization_utils.dart'; import 'package:mockito/mockito.dart'; class LocalStorageMock extends Mock implements ILocalStorage { @@ -10,5 +12,8 @@ class LocalStorageMock extends Mock implements ILocalStorage { when(this.getCountry()).thenAnswer( (_) async => Future.value( CountryModel(name: "Brazil", code: "br")), ); + when(this.getLocale()).thenAnswer( + (_) async => Future.value(Configuration.defaultLocaleKey), + ); } } diff --git a/test/app/modules/home/home_controller_test.dart b/test/app/modules/home/home_controller_test.dart index 5de38aa..6d64944 100644 --- a/test/app/modules/home/home_controller_test.dart +++ b/test/app/modules/home/home_controller_test.dart @@ -1,20 +1,36 @@ import 'package:corona_data/app/app_module.dart'; import 'package:corona_data/app/modules/country/country_module.dart'; +import 'package:corona_data/app/modules/settings/global_settings_controller.dart'; import 'package:corona_data/app/modules/states_map/states_map_module.dart'; import 'package:corona_data/app/modules/world/world_module.dart'; +import 'package:corona_data/app/shared/repositories/local_storage_interface.dart'; import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:corona_data/app/modules/home/home_controller.dart'; import 'package:corona_data/app/modules/home/home_module.dart'; +import 'package:mobx/mobx.dart' as mobx; + +import '../../mocks/local_storage_mock.dart'; void main() { - initModule(AppModule()); + LocalStorageMock covidRepositoryMock = LocalStorageMock(); + initModule(AppModule(),changeBinds: [ + Bind((i) => covidRepositoryMock), + ]); + // initModule(HomeModule()); + + initModule(HomeModule()); HomeController controller; - setUp(() { + GlobalSettingsController globalSettingsController; + + setUp(() async { + globalSettingsController = Modular.get(); + globalSettingsController.init(); + await mobx.asyncWhen((_) => globalSettingsController.isReady); controller = Modular.get(); }); @@ -22,7 +38,7 @@ void main() { test("Inicialization", () { expect(controller.selectedIndex, 0); expect(controller.page.runtimeType, CountryModule().runtimeType); - expect(controller.title, 'country'); + expect(controller.title, 'Brazil'); }); test("Set Page to World", () { @@ -30,14 +46,14 @@ void main() { expect(controller.selectedIndex, 1); expect(controller.page.runtimeType, WorldModule().runtimeType); - expect(controller.title, 'Mundo'); + expect(controller.title, 'World'); }); test("Set Page to Map", () { controller.setPage(2); expect(controller.selectedIndex, 2); expect(controller.page.runtimeType, StatesMapModule().runtimeType); - expect(controller.title, 'Mapa'); + expect(controller.title, 'Map'); }); test("Set Page to Country", () { @@ -45,7 +61,7 @@ void main() { expect(controller.selectedIndex, 0); expect(controller.page.runtimeType, CountryModule().runtimeType); - expect(controller.title, 'country'); + expect(controller.title, 'Brazil'); }); }); } diff --git a/test/app/modules/home/home_page_test.dart b/test/app/modules/home/home_page_test.dart index a61fc26..19f23c4 100644 --- a/test/app/modules/home/home_page_test.dart +++ b/test/app/modules/home/home_page_test.dart @@ -58,7 +58,7 @@ main() { final widgetFinder = find.byType(WorldPage); expect(widgetFinder, findsOneWidget); - final titleFinder = find.widgetWithText(AppBar, 'Mundo'); + final titleFinder = find.widgetWithText(AppBar, 'World'); expect(titleFinder, findsOneWidget); }); @@ -73,7 +73,7 @@ main() { final widgetFinder = find.byType(StatesMapPage); expect(widgetFinder, findsOneWidget); - final titleFinder = find.widgetWithText(AppBar, 'Mapa'); + final titleFinder = find.widgetWithText(AppBar, 'Map'); expect(titleFinder, findsOneWidget); }); });