Add localization to Widget without using MaterialApp? - flutter

I understand how to add localization to an app by adding localizationsDelegates and supportedLocales to the MaterialApp widget. Localizing my app is working fine.
I'm creating a Flutter package that can be used within other Flutter apps. Some of the widgets within the package need to have localized text, like some of the error messages and button labels. The package contains all of its own localized strings. How can I localize the strings in my package without MaterialApp?

I've used Localizations widget in this way for testing without using a MaterialApp:
A simple demo widget:
class WidgetToTest extends StatelessWidget {
#override
Widget build(BuildContext context) {
//AppLocalizations.of(context)!.hello = 'hello' in generated file
return Text(AppLocalizations.of(context)!.hello);
}
}
The test:
testWidgets('test localizations widget', (tester) async {
await tester.pumpWidget(
Localizations(
locale: const Locale('en'),
delegates: AppLocalizations.localizationsDelegates,
child: WidgetToTest(),
)
);
expect(find.text('hello'), findsOneWidget);
});
You can use Localizations widget if you just need to enable AppLocalizations to work inside your widgets.

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: How can I get list of device locales before building starts?

I want to internationalize my application. To ease translations, I want to use i18n_extension package.
I need to get the list of device locales (any device, even web) before any widget building process starts.
This is required to initially set the correct locale (which can be taken from a server) both for I18n and for Localizations (in WidgetsApp).
The only way I found to get the list of device locales is:
MaterialApp(
localeListResolutionCallback: (deviceLocales, appLocales) => myLocaleResolution(deviceLocales, appLocales),
locale: thisAppsLocale,
home: I18n(
child: MyHomePage(),
initialLocale: thisAppsLocale, ),
Method myLocaleResolution sets thisAppsLocale. Method is invoked once at startup and when the user changes the device locale. So thisAppsLocale is only available after the first build process and cannot be used to set locale and initialLocale. Using setState() within myLocaleResolution throws an exception on startup invocation.
Use window.locales:
import 'dart:ui';
import 'package:flutter/material.dart';
void main(){
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
locale: window.locale,
supportedLocales: window.locales,
home: Scaffold(),
);
}
}
The window singleton gives you access to the Window class.

How to write widget tree test to verify tree hierarchy

I am learning Flutter and I want to write one test for my simple MyAppBarWidget. Below is my widget
class MyAppBarWidget extends StatelessWidget{
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("My first widget"),
),
));
}
}
I want to test widget tree hierarchy as
MaterialApp has Scaffold property
Scaffold has AppBar property
AppBar has title property as Text
title is My first widget
Any suggestion what kind of test I should write
I tried below test
void main() {
testWidgets("verify app bar", (WidgetTester tester) async {
await tester.pumpWidget(MyAppBarWidget());
var byWidget = find.byType(MaterialApp);
var text = find.text("My first widget");
expect(byWidget, findsOneWidget);
expect(text, findsOneWidget);
});
}
But this test does not say that my text field is inside AppBar widget
Can someone help me how should I write test to verify this ?
Thanks
I suggest not to test widget hierarchy, you will change it often and always have to adjust the test without actually knowing anything when the test fails or succeeds. It is better to test functionality, the presence of something or the absence, tap events and interaction.
You can also look into golden (screenshot) tests to ensure that screens or pages don't change.
That being said, if you really want to do this you can use
find.ancestor(find.byType(AppBar), find.text("My first widget"));
EDIT
Or with newer versions of the test library, thanks Fred Grott:
find.ancestor(of: find.byType(AppBar), matching: find.text("My first widget"));

Is it normal that when I run my flutter code the first time it is rederized 2 times? (I only use one class )

I am new to flutter. Something worries me, I don't know if it is normal. I know the code will render if the widget is of type StateFulWidget. But in this case, I have a stateLessWidget and for some reason it renders 2 times. is this normal?
this is my code:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
print("main");
return MaterialApp(title: 'Provider Example', initialRoute: '/', routes: {
'/': (context) => Page2(),
});
}
}
class Page2 extends StatelessWidget {
Page2() {
print("page2");
}
#override
Widget build(BuildContext context) {
return Container();
}
}
this is the output:
Restarted application in 832ms.
I/flutter ( 4439): main
I/flutter ( 4439): page2 --> next two lines are the same, the widget is render again
I/flutter ( 4439): main
I/flutter ( 4439): page2
Flutter build method is what creates and return your rendered widget on the screen, so the build must be called everytime something change in the UI, and it need to rebuild. According to the Documentation
The build method will be called after:
After calling initState.
After calling didUpdateWidget.
After receiving a call to setState.
After a dependency of this State object changes (e.g., an InheritedWidget referenced by the previous build changes).
After calling deactivate and then reinserting the State object into the tree at another location.
So even if you have a Stateless Widget, flutter can and will rebuild multiple times, this why you should avoid putting logic handlers inside your Widgets, specially the build, let this method as simple as possible with only what it actually needs to build the widget.
Also, during animations, transitions... Your widget will be rebuild a lot of times in order to perfom the animation. If you want to avoid unnecessary builds there's some ways you can do it, by making using of the const widgets, if you that a certain Widget won't change during runtime like a Text('Hi') this kind of Widget just need to be build once, so you can use a const keyword to it.

how to make common Drawer for few screens in the app in flutter

Please suggest me a way how can i create common drawer for few screens in flutter.
I have tried this: Flutter: Setting up a Navigation Drawer with Multiple Fragments (Widgets). (this makes my whole app with drawer but i want it in on few screens only).
thanks in advance.
You just need to create a class for your drawer (A Stateful or Stateless Widget)
class CustomDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(...);
}
}
And then when using it in each specific page you want:
Scaffold(
drawer: CustomDrawer (...),
...
)