I'm trying to show the apps content based on the user location. For this, I'm trying to get the languageCode from they country, but it's not working. The retrieved languageCode corresponds to the device language instead the country.
I'm doint this:
#override
Widget build(BuildContext context) {
return ScopedModel<UserModel>(
model: UserModel(),
child: ScopedModelDescendant<UserModel>(builder: (context, child, snapshot) {
return MaterialApp(
title: "APP",
debugShowCheckedModeBanner: false,
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: allTranslations.supportedLocales(),
);
}));
}
Where my supportedLocales are a list with all country/language code:
Iterable<Locale> supportedLocales() => [
const Locale('ar','AE'),
const Locale('ar','BH'),
const Locale('ar','DZ'),
const Locale('ar','EG'),
const Locale('ar','IL'),
const Locale('ar','IQ'),
const Locale('ar','JO'),
const Locale('ar','KW'),
const Locale('ar','LB'),
const Locale('ar','LY'),
const Locale('ar','MA'),
const Locale('ar','MR'),
const Locale('ar','OM'),
const Locale('ar','PS'),
const Locale('ar','QA'),
...
I'm trying get the correct languageCode doing this:
String language = Localizations.localeOf(context).languageCode;
But the method always ignores the location and takes the device default language :(
E.g: my friend lives in Brazil and his device are with the en-CA language and he can't see the brazilian content becaus the language code always is 'en' instead 'pt'.
import dart.io;
...
final localeStr = Platform.localeName; // 'en-US'
Related
I have these classes
class CustomPopupAction<T> extends CustomAction {
final Icon icon;
final List<CustomPopupActionItem<T>> actions;
final void Function(T) onActionSelected;
CustomPopupAction({
required this.icon,
required this.actions,
required this.onActionSelected,
});
}
class CustomPopupActionItem<T> {
final T value;
final Widget Function(T) itemBuilder;
CustomPopupActionItem({
required this.value,
required this.itemBuilder,
});
}
and I am trying to create overflow menu which will work like this:
if the button is visible, I will create PopupMenuButton
if the button is overflown, I will create ListTile which will open dialog
it can hold multiple different types like CustomAction, CustomPopupAction<Locale>, CustomPopupAction<String>...
I am building that row like this
if (a is CustomPopupAction) {
return PopupMenuButton(
icon: a.icon,
onSelected: (i) => a.onActionSelected(i),
itemBuilder: (context) {
return a.actions.map((i) => PopupMenuItem(
value: i.value,
child: i.itemBuilder(i.value),
)).toList();
},
);
} else {
return IconButton(...);
}
and finally my main code:
...
return OverflowMenu(
actions: [
CustomPopupAction<Locale>(
icon: Icon(Icons.translate),
actions: [
CustomPopupActionItem<Locale>(
value: Locale('en'),
itemBuilder: (l) => ListTile(title: Text(l.toString()),
),
],
onActionSelected: (l) => print(l),
],
);
But this doesn't work for me, I am getting an exception Expected a value of type '(dynamic) => Widget', but got one of type '(Locale) => ListTile'.
I know it's because if (a is CustomPopupAction) is actually getting CustomPopupAction<dynamic>.
can I somehow convince Dart that a nas not dynamic type and that it should work with it's real type?
if not, why am I getting that exception? Locale can be assigned to dynamic variable and ListTile is clearly a Widget.
can I do this without going through dynamics at all?
I'm a bit at loss here.
return MaterialApp(
title: 'App Title',
theme: ThemeData(brightness: Brightness.dark),
initialRoute: '/',
routes: SOMETHING_HERE,
);
I want to push SOMETHING_HERE from a different file, but I can't seem to push a correct value there.
Other file (attempt):
import '../screens/home.dart';
import '../screens/charts.dart';
class Routes {
factory Routes(context) {
Map<String, Widget Function(BuildContext)> _routes;
_routes = {
'/': (context) => ScreenHome(),
'/charts': (context) => ScreenCharts(),
};
return _routes;
}
}
This doesn't work cause it says:
The argument type 'Routes' can't be assigned to the parameter type 'Map<String, (BuildContext) → Widget>'
OF course I can just pass a Map to this argument but I want to define my routes in a separate file.
Any suggestions on how to accomplish this?
I just had the same problem and found the solution.
You don't need to create a class, just create a var that equals your routes Map
main.dart:
import 'package:flutter/material.dart';
import './custom_routes.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(brightness: Brightness.dark),
initialRoute: '/',
routes: customRoutes,
);
}
}
custom_routes.dart:
import 'package:flutter/material.dart';
import '../screens/home.dart';
import '../screens/charts.dart';
var customRoutes = <String, WidgetBuilder>{
'/': (context) => ScreenHome(),
'/charts': (context) => ScreenCharts(),
};
**
There is another way you can try if you wish
**
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: LoginScreen.id,
routes: route,
);
},
}
Create your route dart file. No need to create a class
var route = <String, WidgetBuilder>{
LoginScreen.id: (_) => const LoginScreen(),
// call the classes
Dashboard.id: (_) => const Dashboard(),
// with value
Dashboard.id: (_) => const Dashboard(value: ''),
};
If you don't use call by id. You can do that as well. Also you can pass values shown example
Just create any function with return of Map<String, WidgetBuilder>, here i will show how to do that with pass data to your routes class:
1- Create new file routes.dart, this full code (i used my custom variables like serverToken, notifierThemeMode) to fully explain the process:
import 'package:rxdart/rxdart.dart';
import 'package:flutter/material.dart';
import 'package:path/to/home_screen.dart';
import 'package:path/to/login_screen.dart';
class AppRoutes{
// get initial route
static getInitialRoute({String? serverToken}){
return serverToken == null
? LoginScreen.routeName
: HomeScreen.routeName;
}
// get all app routes
static Map<String, WidgetBuilder> getRoutes({
required BehaviorSubject<ThemeMode?> notifierThemeMode,
required BehaviorSubject<Locale?> notifierLocale,
}){
return {
HomeScreen.routeName: (BuildContext context) => HomeScreen(
notifierThemeMode: notifierThemeMode,
),
LoginScreen.routeName: (BuildContext context) => LoginScreen(
notifierLocale: notifierLocale,
),
}
}
2- In MaterialApp widget call the previous functions:
MaterialApp(
...
routes: AppRoutes.getRoutes(
notifierThemeMode: _notifierThemeMode,
notifierLocale: _notifierLocale
),
initialRoute: AppRoutes.getInitialRoute(
serverToken: _appServerToken
),
);
I followed the steps here to start the translation of my application. I used the .arb file method.
I understand the application get translated when changing the language of the OS, but is it possible, within the application, to toggle the language and make the application dynamically translated?
If so, what should I do? I tried to call the 'load' method, but nothing happened.
Thanks for your help.
Set Locale of MaterialApp and then when you want to change just call
setState(() {
_locale = Locale('b'); // Desired locale
});
Below code changes Locale in every 5 seconds.
Locale _locale;
#override
void initState() {
_locale = Locale('a');
Timer.periodic(Duration(seconds: 5), (t) {
setState(() {
_locale = _locale == Locale('a') ? Locale('b') : Locale('a');
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
supportedLocales: [
const Locale('a'),
const Locale('b'),
const Locale('c'),
],
localizationsDelegates: [
MaterialLocalizationDelegateKa(),
const InfosLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
locale: _locale,
);
}
I used flutter_i18n plugin (Android Studio) to generate i18n.dart(class S) and S.of(context).locale_msg will return the locale string. The main code is shown below.
Language should be changed programmatically by invoking onLocaleChange(locale) when click the button in HomePage. It works well in Android simulator, but won't change language in iOS simulator. Wonder what's wrong with my code?
class _PaperMoonAppState extends State<PaperMoonApp> {
SpecifiedLocalizationDelegate _localeOverrideDelegate;
void onLocaleChange(Locale locale) {
setState(() {
if (appVars.appConfig.changeLanguage(locale)) {
_localeOverrideDelegate = new SpecifiedLocalizationDelegate(locale);
appVars.saveConfig(); //print save config file...
}
});
}
#override
void initState() {
SpecifiedLocalizationDelegate.onLocaleChange = this.onLocaleChange;
appVars.loadConfig().then((AppConfig _config) {
appVars.appConfig = _config;
setState(() {
_localeOverrideDelegate =
new SpecifiedLocalizationDelegate(appVars.appConfig.getLocale());
});
});
_localeOverrideDelegate =
new SpecifiedLocalizationDelegate(Locale('zh', ''));
super.initState();
}
#override
Widget build(BuildContext context) {
print(_localeOverrideDelegate.overriddenLocale);
return MaterialApp(
debugShowCheckedModeBanner: false,
title: "Paper Moon",
color: Colors.blueAccent,
localizationsDelegates: [
_localeOverrideDelegate,
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
supportedLocales: const <Locale>[
Locale("ja", ""),
Locale("en", ""),
Locale("zh", ""),
],
localeResolutionCallback:
S.delegate.resolution(fallback: _localeOverrideDelegate.overriddenLocale),
home: HomePage(),
// routes: _buildRoutes(),
);
}
}
Custom LocalizationDelegate:
class SpecifiedLocalizationDelegate
extends LocalizationsDelegate<WidgetsLocalizations> {
//class static vars:
//onLocaleChange should be bind to MaterialApp function containing setState().
static LocaleChangeCallback onLocaleChange;
// for instance
final Locale overriddenLocale;
const SpecifiedLocalizationDelegate(this.overriddenLocale);
#override
bool isSupported(Locale locale) => overriddenLocale != null;
#override
Future<WidgetsLocalizations> load(Locale locale) =>
S.delegate.load(overriddenLocale);
#override
bool shouldReload(SpecifiedLocalizationDelegate old) => true;
}
Based on your code, the only thing that seems to be missing is this:
open ios/Runner/Info.plist and add:
<key>CFBundleLocalizations</key>
<array>
<string>ja</string>
<string>en</string>
<string>zh</string>
</array>
As far I as know, by now (march/2019), flutter doesn't yet add automatically the list of supported languages to this file.
I'm using i18n_extensions, but with the same issue...
What worked for me, was use this:
supportedLocales: const <Locale>[
const Locale('en'),
const Locale('pt'),
],
Instead of this:
supportedLocales: const <Locale>[
const Locale('en', 'US'),
const Locale('pt', 'BR'),
],
And then, my i18n.dart. file i've change from this:
extension Localization on String {
static final _t = Translations.from("en_us", {
passwordInput: {
"en_us": "Password",
"pt_br": "Senha",
},
searchingTitle: {
"en_us": "Scanning for devices...",
"pt_br": "Procurando dispositivos...",
},
...
To this:
extension Localization on String {
static final _t = Translations.from("en", {
passwordInput: {
"en": "Password",
"pt": "Senha",
},
searchingTitle: {
"en": "Scanning for devices...",
"pt": "Procurando dispositivos...",
},
It works fine for me.
I need to change application locale programmatically when button pressed
my code :
MaterialApp(
localizationsDelegates: [
_newLocaleDelegate,
const AppTranslationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('en', ''),
const Locale('ar', ''),
],
locale: mylocale,
title: appTitle,
home: Scaffold(
body: new MyCustomForm(),
),
debugShowCheckedModeBanner: false
);
new MaterialButton(
onPressed: () {
setState(() {
mylocale=Locale("ar","");
_newLocaleDelegate = AppTranslationsDelegate(newLocale:mylocale);
});
},
),
translation code :
AppTranslations.of(context).text("text")
AppTranslations Class:
class AppTranslations {
Locale locale;
static Map<dynamic, dynamic> _localisedValues;
AppTranslations(Locale locale) {
this.locale = locale;
_localisedValues = null;
}
static AppTranslations of(BuildContext context) {
return Localizations.of<AppTranslations>(context, AppTranslations);
}
static Future<AppTranslations> load(Locale locale) async {
AppTranslations appTranslations = AppTranslations(locale);
String jsonContent =
await rootBundle.loadString("assets/locale/localization_${locale.languageCode}.json");
_localisedValues = json.decode(jsonContent);
return appTranslations;
}
get currentLanguage => locale.languageCode;
String text(String key) {
print(key);
if(_localisedValues!=null)
return _localisedValues[key] ?? "$key";
else
return key;
}
}
my problem :
when Locale change page direction changed without translation ,
to get effect translation need to refresh page or go to another page and return back,
any help
Could you try wrapping your MaterialApp in an AnimatedSwitcher like so:
AnimatedSwitcher(
// Following two fields for your reference
// duration: const Duration(milliseconds: 500),
// transitionBuilder: (Widget child, Animation<double> animation) {
// return ScaleTransition(child: child, scale: animation);
// },
child: MaterialApp(
// As before, the same code, however:
key: ValueKey<Locale>(mylocale),
)
)
BTW, good practice to prefix private variables with an underscore, e.g. _myLocale.