When i tried to use localization using provider in flutter it is showing no such method error.
I am also using provider for authentication using google and fb and email authentication but the same time i also need to implement localization in the app at as shown below.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:provider/provider.dart';
import 'package:rewahub/AppLanguage.dart';
import 'package:rewahub/locator.dart';
import 'package:rewahub/models/auth_model.dart';
import 'app_localizations.dart';
import 'views/login_page.dart';
import 'views/main_page.dart';
void main() async{
AppLanguagemodel appLanguagemodel = AppLanguagemodel();
await appLanguagemodel.fetchLocale();
setupLocator();
runApp(MyApp(appLanguagemodel:appLanguagemodel,));
}
class MyApp extends StatelessWidget {
final AppLanguagemodel appLanguagemodel;
MyApp({this.appLanguagemodel});
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
builder: (_) => appLanguagemodel,
child: Consumer<AppLanguagemodel>(builder: (context, appmodel, child) {
return MaterialApp(
//locale: model.fetchLocale(),
locale: appmodel.appLocal,
supportedLocales: [
Locale('en', 'US'),
Locale('ar', ''),
],
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
home: LogInPage(),
);
}),
),
ChangeNotifierProvider(builder: (_) => locator<AuthModel>()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
// initialRoute: '/',
//onGenerateRoute: Router.generateRoute,
home: ScreensController(),
),
);
}
}
class ScreensController extends StatelessWidget {
final StreamController<bool> _verificationNotifier =
#override
Widget build(BuildContext context) {
final user = Provider.of<AuthModel>(context);
switch (user.status) {
case Status.Unauthenticated:
// return LanguageSelect();
case Status.Authenticating:
return LogInPage();
case Status.Authenticated:
return MainPage();
default:
return LogInPage();
}
}
}
it will be like
runApp(MultiProvider(
providers: [
ChangeNotifierProvider.value(value: CurrentData()),
ChangeNotifierProvider.value(value: AppProvider()),
ChangeNotifierProvider.value(value: UserProvider.initialize()),
],
child: Consumer<CurrentData>(
builder: (context, provider, child) => MaterialApp(
debugShowCheckedModeBanner: false,
//title: 'Flutter App Localization with Provider demo',
locale: Provider.of<CurrentData>(context).locale,
home: ScreensController(),
localizationsDelegates: [
const AppLocalizationDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('en'),
const Locale('ar'),
const Locale('fr'),
const Locale('es'),
const Locale('ru'),
],
),
),
));
Related
I am developing an app in Flutter. I'm using flutter_localizations package for localization and intl package for internationalization. For this, I'm using Context in Widgets, but the problem is when I want to use internationalization inside bloc or repositories or other layers except for the UI layer.
What is the best practice for doing internationalization inside Other layers except for UI where we don't have access to Context?
I have tried to use a Singleton, but I don't know if this is the right way.
You have to pass appLocalizations. For example in the Cubits you can do something like:
main.dart
runApp(
MaterialApp(
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
const Locale('en', ''),
const Locale('pl', ''),
],
title: 'MySuperApp',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.orange,
accentColor: Colors.deepOrangeAccent,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: '/',
routes: {
UserAuthScreen.routeName: (context) => const UserAuthScreen(),
HomePage.routeName: (context) => HomePage(),
...
},
builder: (context, child) {
final appLocalizations = AppLocalizations.of(context); //IMPORTANT
return MultiBlocProvider(
providers: [
BlocProvider<ConstantsCubit>(
lazy: true,
create: (context) => ConstantsCubit(
constantsRepository: ConstantsRepository(),
),
),
BlocProvider<UserAuthCubit>(
lazy: true,
create: (context) => UserAuthCubit(
localizations: appLocalizations, //THIS IS WHERE THE MAGIC HAPPENS
repository: UserAuthRepository(),
),
),
BlocProvider<DoerInfoCubit>(
lazy: true,
create: (context) => DoerInfoCubit(
doerInfoRepository: DoerInfoRepository(),
userAuthCubit: BlocProvider.of<UserAuthCubit>(context),
)),
...
],
child: child,
);
},
home:
BlocBuilder<UserAuthCubit, UserAuthState>(builder: (context, state) {
if (state is UserAuthLogged) {
return HomePage();
} else {
return const UserAuthScreen();
}
}),
),
);
In your bloc or cubit:
class UserAuthCubit extends Cubit<UserAuthState> {
final UserAuthRepository repository;
final AppLocalizations localizations;
UserAuthCubit({
#required this.repository,
#required this.localizations,
}) : super(const UserAuthInitial()) {
getUserAuthState();
}
I have this error while trying to change language locally using the flutter provider here is the error Exception caught by widgets library
The following LateError was thrown building Builder(dirty, dependencies: [_InheritedProviderScope<LocaleProvider?>]):
LateInitializationError: Field '_locale#49012749' has not been initialized.
The relevant error-causing widget was
ChangeNotifierProvider
When the exception was thrown, this was the stack
my code:
`
import 'package:flutter/material.dart';
import '../l10n/l10n.dart';
class LocaleProvider extends ChangeNotifier {
late Locale? _locale;
Locale get locale => _locale!;
void setLocale(Locale locale) {
if (!L10n.all.contains(locale)) return;
_locale = locale;
notifyListeners();
}
void clearlocale() {
_locale = null;
notifyListeners();
}
}
final provider = Provider.of<LocaleProvider>(context);
final locale = provider.locale;
DropdownButtonHideUnderline(
child: DropdownButton(
value: locale,
icon: Container(width: 12),
items: L10n.all.map(
(locale) {
final flag = L10n.getFlag(locale.languageCode);
return DropdownMenuItem(
child: Center(
child: Text(flag),
),
value: locale,
onTap: () {
final provider = Provider.of<LocaleProvider>(context,
listen: false);
provider.setLocale(locale);
},
);
},
).toList(),
onChanged: (Locale? value) {
// value=locale
},
)),
import 'package:flutter/material.dart';
class L10n {
static final all = [
const Locale('en'),
const Locale('ar'),
];
static getFlag(String code) {
switch (code) {
case 'ar':
return '🇱🇧';
case 'en':
default:
return 'en';
}
}
}
`
main:
`
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:thrifty/constants/utils.dart';
import 'package:thrifty/provider/locale_provider.dart';
import 'l10n/l10n.dart';
import 'screens/splash_screen.dart';
import 'package:flutter_gen/gen_l10n/app_localization.dart';
import 'package:provider/provider.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
final navigatorKey = GlobalKey<NavigatorState>();
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) => ChangeNotifierProvider(
create: (context) => LocaleProvider(),
builder: (context, child) {
final provider = Provider.of<LocaleProvider>(context);
return MaterialApp(
scaffoldMessengerKey: Utils.messengerKey,
navigatorKey: navigatorKey,
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
supportedLocales: L10n.all,
// ignore: prefer_const_literals_to_create_immutables
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
locale: provider.locale,
home: SplashScreen(),
);
});
}
`
language only can change when I change language in my phone, but I am trying to do it locally.
This happens because access to the _local property happens before initialisation.
Add initialisation with default value or pass it via constructor:
class LocaleProvider extends ChangeNotifier {
Locale? _locale;
Locale get locale => _locale!;
LocaleProvider(Locale locale): _locale = locale;
void setLocale(Locale locale) {
if (!L10n.all.contains(locale)) return;
_locale = locale;
notifyListeners();
}
void clearlocale() {
_locale = null;
notifyListeners();
}
}
Update
Add to your pubspec.yaml dependency flutter_localisation
dependencies:
flutter:
sdk: flutter
flutter_localizations: # Add this line
sdk: flutter # Add this line
In your main.dart file add import
import 'package:flutter_localizations/flutter_localizations.dart';
Wrap your MyApp widget with MaterialApp
void main() {
runApp(
const MaterialApp(
title: 'Application title',
localizationsDelegates: <LocalizationsDelegate<dynamic>>[
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: <Locale>[
// Your supported locales
Locale('de'),
Locale('en'),
Locale('es')
],
home: MyApp(), // All Localized Widgets must be placed inside home widget.
)
);
}
And your main will look like:
final navigatorKey = GlobalKey<NavigatorState>();
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MaterialApp(
title: 'Application title',
localizationsDelegates: <LocalizationsDelegate<dynamic>>[
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: <Locale>[
// Your supported locales
Locale('de'),
Locale('en'),
Locale('es')
],
home: MyApp(), // All Localized Widgets must be placed inside home widget.
));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) => ChangeNotifierProvider(
create: (context) => LocaleProvider(Localizations.localeOf(context)),
builder: (context, child) {
final provider = Provider.of<LocaleProvider>(context);
return MaterialApp(
scaffoldMessengerKey: Utils.messengerKey,
navigatorKey: navigatorKey,
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
supportedLocales: L10n.all,
// ignore: prefer_const_literals_to_create_immutables
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
locale: provider.locale,
home: SplashScreen(),
);
});
}
The application needs to implement language switching at runtime. Wrote a bloc with event and state and called BlocBuilder in main.dart. But I don't know how to implement the switch. How can I do that?
In total, the application has two languages.
My bloc:
class LanguageBloc extends Bloc<LanguageEvent, LanguageState> {
LanguageBloc() : super(InitialLang()) {
on<ChangeLang>(
(event, emit) {
emit(NewLang());
},
);
}
#immutable
abstract class LanguageEvent {}
class ChangeLang extends LanguageEvent {}
#immutable
abstract class LanguageState {}
class InitialLang extends LanguageState {}
class NewLang extends LanguageState {}
My main.dart
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => BottomNavyBloc(),
),
BlocProvider(
create: (context) => LanguageBloc(),
),
],
child: BlocBuilder<LanguageBloc, LanguageState>(
builder: (context, state) {
return MaterialApp(
title: 'Flutter Demo',
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: S.delegate.supportedLocales,
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: const HomeScreen(),
);
},
),
);
}
My lang btn:
ElevatedButton(onPressed: (){}, child: Text('Switch lang'))
What you can do is to send a variable Locale with the language of your choice, and in your MaterialApp in the locale attribute, you attach it.
Without complications you can use Cubit instead of Bloc, because it is not necessary to have events, then you could do the following:
class LanguageCubit extends Cubit<Locale?> { // change state here, you dont use LanguageState
LanguageCubit() : super(null);
void initialLang () { // your initial lang
emit(
Locale("en", ""),
);
}
void newLang(
bool isEnglish, // in your checkbox you are gonna send the boolean value here
) {
emit(
isEnglish ? Locale("en") : Locale("fr"),
);
}
}
Now in your main, as you have it, it would only look like this:
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => BottomNavyBloc(),
),
BlocProvider(
create: (context) => LanguageBloc(),
),
],
child: BlocBuilder<LanguageBloc, Locale?>( // change the state for Locale? cause could be null
builder: (context, lang) { // different name to lang
return MaterialApp(
title: 'Flutter Demo',
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: S.delegate.supportedLocales,
theme: ThemeData(
primarySwatch: Colors.blue,
),
locale: lang, // here you set the lang
debugShowCheckedModeBanner: false,
home: const HomeScreen(),
);
},
),
);
}
----- EDIT WITH BUTTON -----
I thought you would need a Switch Button that handles the booleans but no, you only need one button that will be the one to change it so I did it this way:
class _HomePageState extends State<HomePage> {
bool _currentLanguageBool = false; // variable to know if is english or french
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => LanguageCubit(), // with this we can use the Cubit in all the page, normally you have to have it in main and in the MaterialApp
child: Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
BlocProvider.of<LanguageCubit>(context)
.newLang(_currentLanguageBool);
setState(() {
_currentLanguageBool = !_currentLanguageBool;
}); // with this you change the variable
},
child: Text('Switch lang'),
),
),
),
);
}
}
We will make our widget a StatefulWidget so we can just change the boolean variable and know if it is in English or French. If you don't want to use Stateful let me know, because we can use it with the same Cubit, but it would change the code and a little bit the logic of the LanguageCubit.
I am using Flutter to build a Web-App and I want to use the internationalization feature of flutter on my new app. I was following the Flutter-Tutorial and I try to set the app-title using the arb-file. As mentioned in the tutorial, the app_localization.dart-files are created properly for 'en' and 'de'. Yet, I get a null pointer exception in the following code.
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'MyApp',
localizationsDelegates: [
AppLocalizations.delegate, // Post-EDIT due to croxx5f
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
Locale('de', ''),
Locale('en', ''),
],
theme: ThemeData(
primarySwatch: Colors.red,
),
home: Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.appTitle),
),
body: Text(AppLocalizations.of(context)!.appTitle)
),
);
}
}
In fact, AppLocalizations.of(context) returns null.
You should add the AppLocalizations in your MaterialApp:
MaterialApp(
...
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
],
supportedLocales: AppLocalizations.supportedLocales,
...
On my case, using the Scaffold() as home: of MaterialApp() for testing purpose caused the issue.
Once I created a Separate HomePage() widget and used that as
home: HomePage() the problem is gone.
When I put the Scaffold(), as a direct child to MaterialApp, that treats as there were no parent contexts available with AppLocalizationDelegate, as the MaterialApp is the main context builder for all the underlying widgets.
Bad code:
return MaterialApp(
home: Scaffold(
body: Txt(text: AppLocalizations.of(context)?.helloWorld)
),
locale: Locale('ar'),
supportedLocales: [Locale('en'),Locale('ar')],
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
]);
},
));
Good code:
return MaterialApp(
home: HomePag(),
locale: Locale('ar'),
supportedLocales: [Locale('en'),Locale('ar')],
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
]);
},
));
class HomePag extends StatefulWidget {
const HomePag({Key? key}) : super(key: key);
#override
State<HomePag> createState() => _HomePagState();
}
class _HomePagState extends State<HomePag> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
Txt(text: AppLocalizations.of(context)?.helloWorld),
// Txt(text: AppLocalizations.of(context)?.translate('help'))
],
),
),
);
}
}
Thats helped me. I had a CupertinoApp above MaterielApp, this override the MaterielApp and my AppLocalization.of(content) was null
https://github.com/flutter/flutter/issues/26365#issuecomment-523536339
Bloc is good because it works without context. Internalization requires context. You can try to pass it to the block, but in my case, the bloc is initialized before the internalization. MultiProvider before MaterialApp, how to change the sequence and use internalization inside bloc?
class MyApp extends StatelessWidget {
final routes = <String, WidgetBuilder>{
StartPage.routeName: (BuildContext context) => new StartPage(),
EditorPage.routeName: (BuildContext context) => new EditorPage(),
};
#override
Widget build(BuildContext context) {
//localization always == null
var localization = S.of(context);
return MultiProvider(
providers: [
ChangeNotifierProvider.value(
value: ChannelModel(),
),
BlocProvider<InternetBloc>(
create: (context) => InternetBloc(localizations: localization),
),
BlocProvider<MultiBloc>(
create: (context) => MultiBloc(),
),
BlocProvider<DbBloc>(
create: (context) => DbBloc(),
),
BlocProvider<FileBloc>(
create: (context) => FileBloc(),
),
],
child: MaterialApp(
localizationsDelegates: [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: S.delegate.supportedLocales,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: StartPage(),
routes: routes,
));
}
}