Flutter: Change default textDirection on the whole app to RTL - flutter

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(),

Related

Preserve state and prevent initState of been called more than once

I have 3 page (all statefull widgets) :
Home page
Weather page
Setting page
The things is when i'm going from home page to weather page with a "Navigator.pushNamed" and going from the weather page to home page with a "Navigator.pop", the next time i'm trying to go to the weather page from the home page, initState method is called again...
How i can manage to make it call only the first time and not been called every time i push into the weather page ?
Here my app.dart code :
import 'package:exomind/src/core/views/home_view.dart';
import 'package:exomind/src/features/weather/presentation/views/weather_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import '../injection_container.dart';
import 'core/styles/colors.dart';
import 'features/settings/presentation/bloc/settings_bloc.dart';
import 'features/settings/presentation/views/settings_view.dart';
import 'features/weather/presentation/bloc/weather_bloc.dart';
/// The Widget that configures your application.
class MyApp extends StatelessWidget {
const MyApp({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
// Glue the SettingsController to the MaterialApp.
//
// The AnimatedBuilder Widget listens to the SettingsController for changes.
// Whenever the user updates their settings, the MaterialApp is rebuilt.
return MultiBlocProvider(
providers: [
BlocProvider<WeatherBloc>(
create: (_) => serviceLocator<WeatherBloc>()),
BlocProvider<SettingsBloc>(
create: (_) => serviceLocator<SettingsBloc>()
..add(
const SettingsLoaded(),
)),
],
child:
BlocBuilder<SettingsBloc, SettingsState>(builder: (context, state) {
return MaterialApp(
debugShowCheckedModeBanner: false,
// Providing a restorationScopeId allows the Navigator built by the
// MaterialApp to restore the navigation stack when a user leaves and
// returns to the app after it has been killed while running in the
// background.
restorationScopeId: 'app',
// Provide the generated AppLocalizations to the MaterialApp. This
// allows descendant Widgets to display the correct translations
// depending on the user's locale.
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('en', ''), // English, no country code
],
// Use AppLocalizations to configure the correct application title
// depending on the user's locale.
//
// The appTitle is defined in .arb files found in the localization
// directory.
onGenerateTitle: (BuildContext context) =>
AppLocalizations.of(context)!.appTitle,
// Define a light and dark color theme. Then, read the user's
// preferred ThemeMode (light, dark, or system default) from the
// SettingsController to display the correct theme.
theme:
ThemeData(fontFamily: 'Circular', primaryColor: kPrimaryColor),
darkTheme: ThemeData.dark(),
themeMode: state.themeMode,
// Define a function to handle named routes in order to support
// Flutter web url navigation and deep linking.
onGenerateRoute: (RouteSettings routeSettings) {
return MaterialPageRoute<void>(
settings: routeSettings,
builder: (BuildContext context) {
switch (routeSettings.name) {
case SettingsView.routeName:
return const SettingsView();
case WeatherView.routeName:
return const WeatherView();
case HomeView.routeName:
return const HomeView();
default:
return const HomeView();
}
},
);
},
);
}));
}
}
Here my home_view.dart code :
import 'package:flutter/material.dart';
import '../../features/weather/presentation/views/weather_view.dart';
class HomeView extends StatefulWidget {
const HomeView({Key? key}) : super(key: key);
static const routeName = '/home';
#override
State<HomeView> createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView>
with SingleTickerProviderStateMixin {
late AnimationController rotationController;
#override
void initState() {
rotationController =
AnimationController(duration: const Duration(seconds: 1), vsync: this)
..repeat();
super.initState();
}
#override
void dispose() {
rotationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final double height = MediaQuery.of(context).size.height;
final double width = MediaQuery.of(context).size.width;
return Scaffold(
body: Stack(
alignment: Alignment.center,
children: [
Positioned(
top: (height / 2),
child: RotationTransition(
turns: Tween(begin: 0.0, end: 1.0).animate(rotationController),
child: IconButton(
icon: const Icon(Icons.wb_sunny),
color: Colors.yellow,
iconSize: (width * 0.2),
onPressed: () {
Navigator.of(context).pushNamed(WeatherView.routeName);
},
),
),
)
],
),
);
}
}
Here my weather_view.dart code :
import 'dart:async';
import 'package:exomind/src/features/weather/presentation/bloc/weather_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:percent_indicator/percent_indicator.dart';
class WeatherView extends StatefulWidget {
const WeatherView({Key? key}) : super(key: key);
static const routeName = '/weather';
#override
State<WeatherView> createState() => _WeatherViewState();
}
class _WeatherViewState extends State<WeatherView>
with SingleTickerProviderStateMixin {
#override
void initState() {
print("initcalled")
super.initState();
}
#override
void dispose() {
rotationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
super.build(context);
final double width = MediaQuery.of(context).size.width;
final double height = MediaQuery.of(context).size.height;
return Scaffold();
}
}
Any help and explanation would be appreciate :)
I can't think of a "clean" way of not executing the initState in _WeatherViewState. Are you trying to avoid the same city added to the WeatherBloc more than once? If so, I'd check for the existence of 'city' in the WeatherBloc before adding.
In your onGenerateRoute you call the WeatherView constructor each time:
case WeatherView.routeName:
return const WeatherView();
This in turn will call initState. What you need to do is create the WeatherView page widget once and use it in the onGenerateRoute:
final _weatherView = const WeatherView();
In your onGenerateRoute:
case WeatherView.routeName:
return _weatherView;
As #RoslanAmir said there is no way to prevent initstate of been called each time we push into a statefulwidget.
So to prevent my event of being added into my bloc each time we push into the stateful widget i add a bool variable to each state to know if the event should be added or not again.
For those who want a precise answer don't hesitate.
Just add a parameter to the Weather page: a boolean that specifies if the rebuild is true or false. (If true, it will call the initState())
This code works fine.
class WeatherView extends StatefulWidget {
final bool rebuild;
static const routeName = '/weather';
WeatherView({
Key? key,
required this.rebuild,
}) : super(key: key);
#override
State<WeatherView> createState() => _WeatherViewState();
}
and the WeatherViewState's initState() will be:
#override
void initState() {
if (widget.rebuild) {
print("initcalled");
super.initState();
} else {
print("Not called");
}
}
So, in your app.dart you should now route to the page by doing
case WeatherView.routeName:
return const WeatherView(rebuild: true); //Choose if rebuild or not by true and false

when i change the language of app i want clipboard text (Copy, Paste etc.,) to english only in Flutter?

*this is my main.dart my problem is when I switch the language of the app I want the (Copy, Paste, etc.,) to be only English. for example, when I change it to the Arabic language I want the clipboard text (Copy, Paste, etc.,) to remain in the English language. i triend alot but not found any solution *
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:pir_zakat/screens/AboutUsScreen.dart';
import 'package:pir_zakat/screens/SettingScreen.dart';
import 'package:pir_zakat/screens/loading_screen.dart';
import 'localization/zakat_localization.dart';
import 'screens/first_part_screen.dart';
import 'screens/loading_screen.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:pir_zakat/Utilites/constants.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
static void setLocal(BuildContext context, Locale locale) {
_MyAppState state = context.findAncestorStateOfType<_MyAppState>();
state.setLocal(locale);
}
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Locale _locale = Locale('fa', 'IR');
void setLocal(Locale newLocal) {
setState(() {
_locale = newLocal;
print('new local is ${newLocal.languageCode}');
});
}
#override
void didChangeDependencies() {
getLocale().then((local) {
setState(() {
this._locale = local;
});
});
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
if (_locale == null) {
return Container(
child: Center(
child: CircularProgressIndicator(),
),
);
} else {
return MaterialApp(
theme: ThemeData.dark().copyWith(
primaryColor: Color(0xFF0A0E21),
scaffoldBackgroundColor: Color(0xFF0A0E21),
),
debugShowCheckedModeBanner: false,
locale: Locale('de'),
localizationsDelegates: [
// ... app-specific localization delegate[s] here
ZakatLocalization.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
Locale('fa', 'IR'), // Hebrew
Locale('en', 'US'), // English
Locale('ar', 'SA'), // Hebrew
// ... other locales the app supports
],
localeResolutionCallback: (deviceLocale, supportedLocales) {
for (var local in supportedLocales) {
if (local.languageCode == deviceLocale.languageCode &&
local.countryCode == deviceLocale.countryCode) {
return deviceLocale;
}
}
return supportedLocales.first;
},
initialRoute: LoadingScreen.id,
routes: {
LoadingScreen.id: (context) => LoadingScreen(),
FirstPartScreen.id: (context) => FirstPartScreen(),
SettingString.id: (context) => SettingString(),
AboutUsScreen.id: (context) => AboutUsScreen(),
},
);
}
}
}
I prefer resting from all this effort and use ready made plugin like localize_and_translate
anyway, what you need is to use key for the main widget class and request a new one or something so you restart you app for everything to take effect in place and you need to add iOS delegates also
take a look at how the plugin source code manage things, and you should get it, it creates LocalizedApp class which includes restart method for requesting new key for main widget class within LocalizedApp and that gets the magic done

Could not find the correct Provider inconsistent behaviour

Hi I'm new to Flutter and Provider and can't make sense of this error. Basically I'm using MultiProvider to manage the states like below, and this works really great for one of them (Auth) but not other(User) even though I'm using them in the same way.
I get this error.
Note there are actually more providers, but simplified it for sake of simpler code example
Error
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following ProviderNotFoundException was thrown building StartPage(dirty, dependencies: [_InheritedProviderScope<Auth>, _InheritedProviderScope<UserLocation>, _InheritedProviderScope<UserState>, _InheritedProviderScope<BottomNavigationBarProvider>]):
Error: Could not find the correct Provider<User> above this StartPage Widget
To fix, please:
* Ensure the Provider<User> is an ancestor to this StartPage Widget
* Provide types to Provider<User>
* Provide types to Consumer<User>
* Provide types to Provider.of<User>()
* Ensure the correct `context` is being used.
If none of these solutions work, please file a bug at:
Main.dart
class SchoolApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Auth()),
ChangeNotifierProvider(create: (_) => User()),
],
child: HomePage(),
));
}
}
class HomePage extends StatefulWidget {
#override
State<StatefulWidget> createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
UserState _userState = Provider.of<UserState>(context);
switch (_userState.status) {
case UserStatus.UnAuthenticated:
return LoginScreen();
case UserStatus.Authenticated:
return StartPage();
}
}
}
StartPage.dart
class StartPage extends StatelessWidget with ChangeNotifier {
Timer timer;
#override
Widget build(BuildContext context) {
final _auth = Provider.of<Auth>(context);
final _user = Provider.of<User>(context, listen: true);
...
User.dart
class User extends ChangeNotifier{
Firestore db = Firestore.instance;
String _userId, _firstName, _lastName, _school, _email, _description, _employer, _title, _name;
List<String> _ideologies, _interests, _religions;
UserType _userType;
...
Auth.dart
class Auth extends ChangeNotifier {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
Future<String> signInWithEmailAndPassword(String email, String password) async {
final AuthResult authResult = await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
notifyListeners();
return authResult.user.uid.toString();
}
...
class SchoolApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<Auth>(create: (_) => Auth()),
ChangeNotifierProvider<User>(create: (_) => User()),
],
child: MaterialApp(
title: 'Flutter Demo',
home: HomePage(),
));
}
}
Wrap your MaterialApp with MultiProvider. I might have made some bracket issue in my code above
Try to wrap with Consumer the widget that requires to get date from provider:
Foo(
child: Consumer<Auth, User>(
builder: (context, auth, user, child) {
return YourWidget(a: yourProviderData, child: child);
},
child: Baz(),
),
)
You can try also wrap your code with the widget Builder(builder: (context) => ), to have access to the nearest context reference.
Answering my question for visibility. So found the answer to my question here. https://github.com/rrousselGit/provider/issues/422#issuecomment-632030627
Basically I imported the package in the wrong way, I did
import 'path/file.dart'
Instead of specifying the full path
import 'package:/path/file.dart'

how can i refresh application current screen after changing language in flutter? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
class ChangeLanguage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Change Language'),
),
body: Center(
child: RaisedButton(
child: Text('Change'),
onPressed: () {
navigator.pop(context);
},
),
),
);
}
I want to refresh previous screen according to the new language after pressing on raised button and calling navigator pop(context).
I am using easy_localization 1.3.1 and when i navigate back to the previous screen the layout doesn't reversed
If you want to change app language without restarting the app and also without any plugin, you can follow the bellow steps:
In main file of the application, in StatefullWedget for example MyHomePage create a static method setLocal as follow
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
static void setLocale(BuildContext context, Locale newLocale) async {
_MyHomePageState state =
context.findAncestorStateOfType<_MyHomePageState>();());
state.changeLanguage(newLocale);
}
#override
_MyHomePageState createState() => _MyHomePageState();
}
where _MyHomePageState is the state of your MyHomePage widget
In your state create a static method changeLanguage:
class _MyHomePageState extends State<MyHomePage> {
Locale _locale;
changeLanguage(Locale locale) {
setState(() {
_locale = locale;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Afghanistan',
theme: ThemeData(primaryColor: Colors.blue[800]),
supportedLocales: [
Locale('fa', 'IR'),
Locale('en', 'US'),
Locale('ps', 'AFG'),
],
locale: _locale,
localizationsDelegates: [
AppLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
localeResolutionCallback: (locale, supportedLocales) {
for (var supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale.languageCode &&
supportedLocale.countryCode == locale.countryCode) {
return supportedLocale;
}
}
return supportedLocales.first;
},
initialRoute: splashRoute,
onGenerateRoute: Router.generatedRoute,
);
}
}
Now from pages of your application you can change the language by calling the setLocal method and pass a new Locale as follow:
Locale newLocale = Locale('ps', 'AFG');
MyHomePage.setLocale(context, newLocale);
Please remember you need to create a LocalizationDelegate,
Here is the link to the Written Tutorial and Demo Application

"not found" when reload page on flutter web

On flutter web when I reload a page on Chrome I get the text "not found". How can I fix it? this is my code of the main.dart. I also noticed that to get directly to a page I have to insert an hash symbol (#) in the url like this: "http://127.0.0.1:8080/#/homepage". Is there a way to remove it?
class MyApp extends StatefulWidget {
const MyApp({Key key}): super(key: key);
#override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
// This widget is the root of your application.
#override
void initState() {
html.window.history.pushState(null, "Home", "/");
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
initialRoute: "/",
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: 'GoogleSansRegular'
),
routes: {
"/": (context) => HomePage(),
"/homepage": (context) => HomePage(),
"/secondPage": (context) => SecondPage()
},
);
}
}
to remove the # in the URL you have to switch to set the UrlStrategy, like it´s descriped here: https://docs.flutter.dev/development/ui/navigation/url-strategies
Long Story short: Add this package (https://pub.dev/packages/url_strategy) to pubspec.yaml and call setPathUrlStrategy() in your main method:
import 'package:url_strategy/url_strategy.dart';
void main() {
// Here we set the URL strategy for our web app.
// It is safe to call this function when running on mobile or desktop as well.
setPathUrlStrategy();
runApp(MyApp());
}
Maybe it also solves your other problem. If not, then i think it´s a good idea to use the AutoRoute package: https://pub.dev/packages/auto_route