SearchDelegate text direction - flutter

how to change text direction to RTL in SearchDelegate
From the research, I found a way to change the color, but I did not find a way to change the direction of the text
thanks
class MySearchDelegte extends SearchDelegate {
#override
String get searchFieldLabel => 'ابحث حسب اسم المكتب';
.....
.....
#override
ThemeData appBarTheme(BuildContext context) {
return Theme.of(context).copyWith(
appBarTheme: Theme.of(context).appBarTheme.copyWith(
color: const Color(0xff202c3b),
),
);
}
}

You need to wrap the entire SearchDelegate widget in a Directionality widget and set the textDirection property to TextDirection.rtl.
Directionality(
textDirection: TextDirection.rtl,
child: MySearchDelegate(),
)
You can also set this property on an ancestor MaterialApp , which will propagate the text direction to all descendant widgets.
aterialApp(
home: MySearchDelegate(),
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('ar', 'AE'), // Arabic
],
locale: const Locale('ar', 'AE'),
)
Find more information in official flutter documentation
Directionality class
Globalization and Accessibility

Related

Internationalization with cubit

I'm working on a project and I've been asked to use cubit for internationalization, preferably using the lazy method. For that I have a LocalizationContainer as follows:
class LocalizationContainer extends BlocContainer {
final Widget child;
LocalizationContainer({required this.child});
#override
Widget build(BuildContext context) {
return BlocProvider<CurrentLocaleCubit>(
create: (context) => CurrentLocaleCubit(),
child: child,
);
}
}
class CurrentLocaleCubit extends Cubit<String> {
CurrentLocaleCubit() : super("pt-br");
CurrentLocaleCubit() : super("en-us");
}
In my main file I have the following:
MaterialApp(
title: 'Example',
theme: exampleTheme(context),
debugShowCheckedModeBanner: false,
home: LocalizationContainer(
child: InitialScreenContainer(),
),
);
In this example the child of LocalizationContainer is another container representing the screen. Each screen is structured into container, cubit and view:
The container for screen have the following structure:
class ExampleScreenContainer extends BlocContainer {
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => ExampleScreenCubit(),
child: I18NLoadingContainer(
language: BlocProvider.of<CurrentLocaleCubit>(context).state,
viewKey : "Example",
creator: (messages) => ExampleScreenView(ExampleScreenViewLazyI18N(messages)),
),
);
}
}
Everytime a new page needs to be opened, I do the following:
Navigator.of(blocContext).push(
MaterialPageRoute(
builder: (context) => BlocProvider.value(
value: BlocProvider.of<CurrentLocaleCubit>(blocContext),
child: NewScreenContainer(),
),
),
);
But whenever I try to hot reload a error pops up. It only works if I do the hot restart. Does somebody know how to solve this problem, or this internationalization method is wrong?
I did not really get the problem (I think if you put the error that's pop up I can help you more), but this way I do localizations (I use bloc).
first off all you need to add BlocProvider above MaterialApp so he become ancestor to every widget in context tree, so when ever you called BlocProvider.of(context)
you can get the instance of this bloc where ever you are in the tree (no need to do BlocProvider above every screen you are pushing).
now when ever you change language of your app and yield the new state the BlocBuilder will rebuild the whole app with the new language.
class AppProvider extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiBlocProvider(providers: [
BlocProvider<AppBloc>(
create: (_) => sl<AppBloc>()
//get app default language
..add(const AppEvent.initialEvent()),
),
], child: App());
}
}
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocBuilder<AppBloc, AppState>(
builder: (context, state) => MaterialApp(
debugShowCheckedModeBanner: false,
home: SplashScreen()),
locale: state.language == AppLanguageKeys.AR
? const Locale('ar', '')
: const Locale('en', ''),
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('en', ''), // English
const Locale('ar', ''), // Arabic
],
),
);
}
}

Flutter, How to make a unit test on a Widget that contain AppLocalization (multilingual)?

I am trying to make a simple test on my HistoryPage widget, here it is :
class HistoryPage extends StatefulWidget {
#override
_HistoryPageState createState() => _HistoryPageState();
}
class _HistoryPageState extends State<HistoryPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.historyPageTitle),
backgroundColor: Colors.green,
),
body: Column(
children: [
],
),
);
}
}
And here is the unit test for that widget :
group('UI TESTS', () {
Widget makeTesteableWidget({required Widget child}) {
return MaterialApp(
home: child,
);
}
testWidgets('History page', (WidgetTester tester) async {
// Create the widget by telling the tester to build it.
final widget = makeTesteableWidget(
child: HistoryPage(),
);
await tester.pumpWidget(widget);
expect(true, true);
});
});
Here is the exception when I run the test :
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following _CastError was thrown building HistoryPage(dirty, dependencies:
[_LocalizationsScope-[GlobalKey#ca13b]], state: _HistoryPageState#b1cad):
Null check operator used on a null value
If I replace the AppBar title like this :
appBar: AppBar(
title: Text('Simple title'),
backgroundColor: Colors.green,
),
The test is working... But I don't know how to manage this line to not throw this exception...
title: Text(AppLocalizations.of(context)!.historyPageTitle),
Thanks for your help !
Found the issue ! In the test, in the MaterialApp I just called (like in the main) the localizations, like this :
group('UI TESTS', () {
Widget makeTesteableWidget({required Widget child}) {
return MaterialApp(
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
const Locale('en', ''),
// English, no country code
const Locale('fr', ''),
// French, no country code
const Locale('de', ''),
// German, no country code
// const Locale('gsw', ''), // Swiss German Alemannic Alsatian, no country code
const Locale('it', ''),
// Italian, no country code
],
home: child,
);
}
testWidgets('History page', (WidgetTester tester) async {
// Create the widget by telling the tester to build it.
final widget = makeTesteableWidget(
child: LoginPage(),
);
await tester.pumpWidget(widget);
expect(true, true);
});
});

Why does flutter localization not work properly?

I want to add the tr tag to the Flutter localization section. I am
getting an error although there is support for Tr. The error is as
follows; error: The element type 'Locale (where Locale is defined in
C:\Users\StatTark\AppData\Roaming\Pub\Cache\hosted\pub.dartlang.org\intl-0.16.1\lib\src\locale.dart)'
can't be assigned to the list type 'Locale (where Locale is defined in
C:\flutter\bin\cache\pkg\sky_engine\lib\ui\window.dart)'.
error: Abstract classes can't be instantiated.
(instantiate_abstract_class at [ajanda] lib\pages\mainmenu.dart:36)
I do not understand if I am making a mistake in use, I will be glad if you help.
class MainMenu extends StatelessWidget {
MainMenu({Key key}) : super(key: key);
final _sdb = SettingsDbHelper();
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _sdb.getSettings(),
builder: (context, snapshot) {
if (snapshot.data == null) {
return MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
supportedLocales: [Locale('en','US'),Locale('tr','')], //The error is here
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: Text(proTranslate["Yükleniyor....."][Language.languageIndex]),
),
),
);
} else {
Language.languageIndex = snapshot.data[0].language;
return DynamicTheme(
defaultBrightness: Brightness.light,
data: (brightness) => ThemeData(
brightness: brightness,
fontFamily: snapshot.data[0].fontName,
floatingActionButtonTheme: FloatingActionButtonThemeData(
foregroundColor: Colors.green,
),
),
themedWidgetBuilder: (context, theme) {
return MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
supportedLocales: [Locale('en','US'),Locale('tr','')],
debugShowCheckedModeBanner: false,
theme: theme,
home: MainMenuBody(
warning: snapshot.data[0].warning,
),
// navigatorKey: navigatorKey,
);
});
}
},
);
}
}
class MainMenuBody extends StatefulWidget {....}
I'm coming a bit late but I just fixed this same issue I had.
I'm pretty sure your file is importing intl/locale.dart instead of flutter/material.dart as both define a Local type.
To fix it, just replace your import at the top of the file from:
import 'package:intl/locale.dart';
to
import 'package:flutter/material.dart';
and you should be OK.
Few suggestions you can try/test, see below.
Replace [Locale('en','US'),Locale('tr','')], with [Locale('en'),Locale('tr')],
init list of supported locales first and use it accordingly.
// init before build
final List<Locale> appSupportedLocales = [
Locale('en'),
Locale('tr')
];
// ...
// then use it like this
supportedLocales: appSupportedLocales,

Flutter: Change default textDirection on the whole app to RTL

I'm building an Arabic based app (RTL language) using flutter and I guess there is a way better than using Directionality in each page like this Directionality(textDirection: TextDirection.rtl, child: FacultyPage()) as I don't feel it's a clean approach and sometimes I forget to implement Directionality as parent widget especially in large apps with a lot of pages/screens.
So the default layout becomes RTL no need to redoing it by Directionality each screen/page.
The easiest way to set RTL configuration for the entire app is:
void main() {
runApp(
MaterialApp(
home: Directionality( // use this
textDirection: TextDirection.rtl, // set it to rtl
child: HomePage(),
),
),
);
}
The cleanest way to set directionality across the app without defining Directionality widget(s) is to use localizations delegate.
See example below.
my_localization_delegate.dart
class MyLocalizationDelegate
extends LocalizationsDelegate<WidgetsLocalizations> {
final MyLocalization mylocalization;
MyLocalizationDelegate(this.myLocalization);
#override
bool isSupported(Locale locale) => true;
#override
Future<WidgetsLocalizations> load(Locale locale) async => mylocalization
#override
bool shouldReload(MyLocalizationDelegate old) => false;
}
my_localization.dart
class MyLocalization implements WidgetsLocalizations {
#override
TextDirection get textDirection {
// logic to return the correct directionality
}
}
your_app.dart
final MyLocalization _myLocalization = Mylocalization();
final MyLocalizationDelegate _myLocalizationDelegate = MyLocalizationDelegate(_myLocalization);
...
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [_myLocalizationDelegate],
...
Use Localization and Internationalization
When you will specify Arabic locale as explained below in Main MyApp state MaterialApp it will change Directionality of each widget in the subtree of your app.
Explained very well in Flutter documentation Link
After Localization and Intl dependencies you can define and set Locale for Arabic like this in you myApp state MaterialApp like this.
I have used findAncestorStateOfType in Parent MyApp to setLocale in Child
Here is the code
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
static void setLocale(BuildContext context, Locale locale) {
_MyAppState? state = context.findAncestorStateOfType<_MyAppState>();
state!.setLocale(locale);
}
#override
_MyAppState createState() => _MyAppState();
}
// ignore: use_key_in_widget_constructors
class _MyAppState extends State<MyApp> {
// const MyApp({Key? key}) : super(key: key)
late Locale _locale;
void setLocale(Locale value) {
setState(() {
_locale = value;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: const [
AppLocalizations.delegate, // Add this line
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('en', ''), // English, no country code
Locale('ar', ''), // Spanish, no country code
],
locale: _locale,
themeMode: ThemeMode.light,
// themeMode: ThemeMode.dark,
// themeMode: ThemeMode.system,
theme: MyTheme.lightTheme(context),
darkTheme: MyTheme.darkTheme(context),
debugShowCheckedModeBanner: false,
initialRoute: "/",
routes: {
"/": (context) => LoginScreen1(), //LoginPage(),
MyRoutes.loginRoute: (context) => LoginScreen1(), //LoginPage(),
MyRoutes.signupRoute: (context) => const SignUpScreen1(),
MyRoutes.homeRoute: (context) => HomePage(),

how to get current layout direction in Flutter?

I'm working on an application that should also work with RTL layout direction (Arabic and Hebrew languages).
I also need to perform some changes in the layout in case the layout direction is
RTL.
How can I determine what is the current layout direction of the app?
You can get the current direction using Directionality.of.
final TextDirection currentDirection = Directionality.of(context);
final bool isRTL = currentDirection == TextDirection.rtl;
which determines `the direction of the selctedlanguage but if need you to set it manually, probably this can work for you.
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(),
home: Directionality(
textDirection: TextDirection.rtl,
child: Home(),
),
);
}
Add this method to your class :
bool isRTL() => Directionality.of(context).toString().contains(TextDirection.RTL.value.toLowerCase());
Usage in the build() method :
RichText(
textAlign: isRTL() ? TextAlign.right : TextAlign.left,
.....
),