How to you change the locale of a flutter app while running? - flutter

I'm using
import 'package:flutter_localizations/flutter_localizations.dart'; //For Cupertino stuff
import 'package:localization/localization.dart'; //For actual translations
import 'package:intl/intl.dart'; //For locales
to localize my flutter app. It comes up in the language the phone is set to, but I'd like to add a way to change the language within th e app...I tried using LocalJsonLocalization.delegate.load(locale(lang)) from a button (for now; I'd like to use a dropdown list box but that doesn't seem to be null safe yet), but that didn't actually make the change (I saw the debug log and it said it couldn't load the json language file)...

Try wrapping your MaterialApp in a BlocBuilder.
return BlocBuilder<MainBloc, MainState>(
builder: (context, mainState) {
return MaterialApp(
title: 'MyApp',
locale: mainState.locale,
supportedLocales: L10n.all,
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
);
Then from your button just call the Bloc Event that will change the state of the locale.
Do not forget to add:
WidgetsFlutterBinding.ensureInitialized();
at the beginning of main() function
It should also work fine using Provider.

Related

How can i add 2 functions in void main before run app , in Flutter?

I need to run 2 function inside my main.dart file, before run app.
One is the bottom navigation bar, the other one is the easy localization.
My main.dart is:
void main() => runApp(
MyApp()
);
Future <void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized();
runApp(
EasyLocalization(
supportedLocales: [Locale('en', 'US'), Locale('it', 'IT'), Locale('fr', 'FR')],
path: 'assets/translations', // <-- change the path of the translation files
fallbackLocale: Locale('en', 'US'),
assetLoader: CodegenLoader(),
child: MyLangApp(),
),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context){
return new MaterialApp(
home: MyBottomNavBar(),
);
}
}
So basically, i cannot have 2 void main inside the main.dart. How can i put the 2 functions together?
There are two separate parts to your question, and the answers are different.
You're making main() async, and that handles your localization. If you want even more control, we had a discussion about using async functions before rendering the first frame on HumpDayQandA this week, and went into how you can extend the native splash screen as long as needed so your functions can finish. It's the episode for 29 June, 2022 on the FlutterCommunity YouTube channel. https://www.youtube.com/watch?v=9KFk4lypFD4
The BottomAppBar issue is probably why you got dinged for a -1 on this question. That's because this isn't how you use BottomAppBar. It goes in a Scaffold. If you want to not have the AppBar on top then just leave it out.
But you don't want to be calling a BottomNavBar as the home parameter of a MaterialApp. Whatever you use for home is going to be the foundational visible widget in your tree, the lowest widget that all others are built on.
Does your whole app fit inside that BottomNavBar? Then you want to put the bar inside of something else that takes up the whole screen and provides a base to build the rest of your app on... Like a Scaffold.

Flutter get context value from outside context

I am very new to Flutter so I'm not sure if my train of thoughts even make sense. I'm currently using a package called EasyLocalization to localize my app. In doing so, I am trying to reference the current locale of my application which is set by EasyLocalization in a service class completely independent of widgets but the only methods offered by the package was to reference the context
In their example, the following code works
print(context.locale.toString());
However, since I want the value in a "service" class that doesn't make use of widgets, I'm not able to call on any context at all. So something like this code works on my widget but not in an independent service class since context doesn't exist there
var currentLocale = EasyLocalization.of(context)?.locale ?? 'en';
I've also tried some other code to get the localization but they turn out different from the actual localization my app is sync'ed to. For example when my app is running off 'zh' in EasyLocalization, other methods such as the ones below only return 'en-US'
print(Intl().locale);
print(Intl.getCurrentLocale());
One way I've gotten it to partially work is to create a function inside my widget that sets a global value when clicked and then reference that value but it feels "hacky" and isn't sufficient for my use-case where data gets loaded on application start which is then passed through a translation function using the context locale. Most other search results also only turn up information for navigation and snackbars which don't seem to help my use-case so I'm currently out of ideas and turning to SO for help.
Below is my MaterialApp() if it helps
Widget build(BuildContext context) {
return ProviderScope(
child: MaterialApp(
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
debugShowCheckedModeBanner: false,
// Todo: Implement dark mode color theme
theme: lightTheme,
onGenerateRoute: AppRouter.generateRoutes,
));
}
You can use navigator key to access current context from anywhere. You have to create global key and pass it to material app.
//create key
final navigatorKey = new GlobalKey<NavigatorState>();
//pass it to material app
Widget build(BuildContext context) {
return ProviderScope(
child: MaterialApp(
navigatorKey: navigatorKey, //key
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
debugShowCheckedModeBanner: false,
// Todo: Implement dark mode color theme
theme: lightTheme,
onGenerateRoute: AppRouter.generateRoutes,
));
}
//access context
print(navigatorKey.currentContext.locale.toString());
Your service class should be locale independent by design, so taking locale as an input, for example:
class I18nService {
final String locale;
I18nService(this.locale);
String sayHello() {
if (locale == 'en_CA') {
return 'hello Canada';
}
return 'hello world';
}
}
I'd also recommend looking into the provider package, you can instantiate your service using a ProxyProvider and pass in the locale there.

Problem in displaying localized labels in Dart

I am not able to set localization in my app.
I am trying to add language settings and associated localization in my app. I am able to get-set the language option. I am using 'intl' plug-in for internationalization. My code looks like below in pretty much all the UI .dart files.
AppTranslations.of(context).accountNumber +
" ${accountDetails.accountNumber}",
The getters is set as :
String get accountNumber => _text("account_number");
String _text(String key) {
return _localisedValues[key] ?? _defaultLocaleValues[key];
}
I've also placed json files containing localized labels in 3 different languages. However, it seems there is some instantiation problem of the locazation plug-in. The code doesn't go the getter line.
Any help would be highly appreciated.
AppTranslations.of(context) is a standard way of accessing the localised labels. You are right about the instantiation. If the program doesn't go to the getter line them it means, there's a problem in somewhere in the initial part of the code. It could be in the main.dart.
Check where you are initialising LocalStorageProvider(). In case it is not initialised then that's the problem. Assuming you are using a MaterialApp, try the below suggestion then :
Wrap the MaterialApp with LocalStorageProvider(). I mean, in the main widget build, return LocalStorageProvider() and pass your existing code of MaterialApp() as a child to it. Sample below (Please ignore the theme etc since I just copied the code from one of my app) :
#override
Widget build(BuildContext context) {
LocalStorage localStorage = LocalStorage();
return LocalStorageProvider(
localStorage: localStorage,
child: LocaleProvider(
localStorage: localStorage,
localeWrapper: LocaleWrapper(),
child: Builder(
builder: (context) {
return AnimatedBuilder(
animation: LocaleProvider.of(context).localeWrapper,
builder: (context, _) {
return MaterialApp(
onGenerateTitle: (context) =>
AppTranslations.of(context).appName,
locale: LocaleProvider.of(context).locale,
title: "App Title",
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MapsDemo(),
localizationsDelegates: [
AppTranslationsDelegate(
LocaleProvider.of(context).supportedLanguagesCodes,
),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: LocaleProvider.of(context).supportedLocales,
);
},
);
},
),
),
);
}

How can I use a localized string from GlobalMaterialLocalizations?

I want to use one of the predefined localized strings available in the GlobalMaterialLocalizations class. I have added the necessary bits and pieces to my MaterialApp
MaterialApp(
localizationsDelegates: [
const LocalizationDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('en', ''),
const Locale('sv', ''),
],
localeResolutionCallback:(Locale locale, Iterable<Locale> supportedLocales) {
return locale; // Return a different locale if the user choose another language in the settings
},
...
and my custom LocalizationDelegate is working fine. I just can't figure out how to use the predefined strings in GlobalMaterialLocalizations, since there is no GlobalMaterialLocalizations.of(BuildContext) method?
Turns out I was looking for the .of(BuildContext) method in the wrong class. To actually use the strings, the MaterialLocalizations class should be used.
Text( MaterialLocalizations.of(context).okButtonLabel )
Hope it might help someone else struggling with the same problem.

Flutter CupertinoDatePicker - can I swap month and day positions?

I need to use CupertinoDatePicker, however its date formatting is mm-dd-yyyy, which in fact is not common for a specific location, where my app will be distributed. I would like to change the format to dd-mm-yyyy, which IMO seems more general.
Is that possible, using that picker?
EDIT: It should be possible in Flutter version 1.7
According to the CupertinoDatePicker documentation:
the class will display its children as
consecutive columns. Its children order is based on
internationalization.
You can read more about internationalization Flutter apps here.
You need to add this to your pubspec.yaml file:
dependencies:
flutter_localizations:
sdk: flutter
And then in your root widget add proper localizationsDelegates:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My Application',
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('en', ''),
const Locale('fr', ''),
],
home: Scaffold(
body: Container(),
),
);
}
}
If you'll use in app one of the localizations that supports dd-mm-yyyy format, e.g. UK English, and you'll have this language set on your device, you should see this widget changed accordingly.