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,
.....
),
Related
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
I want to use MediaQuery to build widgets based on screen height and width. The problem, also referenced in #26004, is that I only want to query the size data once, for example in initState. MediaQuery documentation states
Querying the current media using MediaQuery.of will cause your widget to rebuild automatically whenever the MediaQueryData changes (e.g., if the user rotates their device).
, but that causes unnecessary rebuilds in my application. Specifically, it causes rebuild of widgets if there are changes to insets or padding (such as when keyboard is displayed).
Is there an alternative to MediaQuery which wouldn't cause rebuilds when MediaQueryData changes?
I had this issue as well and initially thought that the MediaQuery is causing unnecessary rebuilds, but if you think about it you do want the widgets to rebuild (in cases of device rotation, keyboard popup) for the app to have a responsive design.
You could do something like this:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Builder(builder: (context) {
ResponsiveApp.setMq(context);
return MyHomePage(title: 'Flutter Demo Home Page');
}),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Flex(
direction:
ResponsiveApp().mq.size.width > ResponsiveApp().mq.size.height
? Axis.horizontal
: Axis.vertical,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class ResponsiveApp {
static MediaQueryData _mediaQueryData;
MediaQueryData get mq => _mediaQueryData;
static void setMq(BuildContext context) {
_mediaQueryData = MediaQuery.of(context);
}
}
I set the mediaQueryData at the beginning with ResponsiveApp.setMq(context) and I used the Builder because you can only use the MediaQuery one context below the MaterialApp widget. After the _mediaQueryData is set you can get it whenever you want to build widgets based on the screen size.
In this code I just change the Axis direction when the device is rotated and the widget needs to rebuild to show the changed direction.
You could also have something like :
if (_mediaQueryData.size.shortestSide < 400)
//phone layout
else if(_mediaQueryData.size.shortestSide >= 400 && _mediaQueryData.size.shortestSide < 600)
//tablet layout
else
//web layout
and resizing the window in web will cause the widgets to rebuild multiple times and display the desired layout.
But if you don't want to use MediaQuery at all, you can check the Window class from dart:ui.
LayoutBuilder seems preferable over every use of MediaQuery for sizing a viewport (either the whole screen, or the space left in a column or other layout).
LayoutBuilder also works hard to avoid rebuilding its child if the size doesn't change and the parents haven't had to re-layout.
The builder function is called in the following situations:
The first time the widget is laid out.
When the parent widget passes different layout constraints.
When the parent widget updates this widget.
When the dependencies that the builder function subscribes to change.
The builder function is not called during layout if the parent passes
the same constraints repeatedly.
And you don't have to think about "the height of the appbar" ever again, because you're getting the space left, not the total space on the screen.
Check it out: https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html
In my case, the problem was happening because I was controlling the focus manually using:
onEditingComplete: () {
FocusScope.of(context).nextFocus();
}
The context used was the Parent's context, and it was causing the rebuilding. Not sure why it happened, but it stopped once I've wrap the TextFormField with a Builder and started using its context instead.
Note: I'm also using MediaQuery.of(context).size.height normally (without the rebuild side effect) to set the Widget's Parent height 🤔
I don't get any errors upon running, I just get a blank page. I guess it has to do with the layout arrangement, but that's just a guess.
I switched the class to statful in the scaffold body, so I can use setState(){} under the flatButton. I don't know why nothing is showing on the screen though, because I would assume at least the AppBar would be on the screen. Can the body extend over the AppBar?
import 'package:flutter/material.dart';
import 'dart:math';
int colorNumber = 1;
void main()=> runApp(StructureBuild());
class StructureBuild extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text('Random Color Generator',
style: TextStyle(color: Colors.white)),
backgroundColor: Colors.blue,
),
body: BodyBuild()
));
}
}
class BodyBuild extends StatefulWidget {
#override
_BodyBuildState createState() => _BodyBuildState();
}
class _BodyBuildState extends State<BodyBuild> {
int changeColor(int colorNumber) {
return colorNumber = Random().nextInt(245) + 1;
}
#override
Widget build(BuildContext context) {
return Expanded(child:
FlatButton(color: Color(colorNumber),
onPressed: () {
setState(() {
changeColor(1);
});
}, child: Text('press anywhere'),));
}
}
First of all, I recommend checking the documentation of Flutter for using color const. Color const has color from the lower 32 bits of an int. 1-255 values are very rare values for Color. I suggest you find a hexadecimal value by examining the link I gave. https://api.flutter.dev/flutter/dart-ui/Color-class.html .
Also, you can give an integer value. But it should be a huge number to change the color of the Flatbutton. For Example, 4123423412341242342 would change to color kind of turquoise blue.
I need my font sizes to change depending on the width of the user's screen. I am using a file, size_config.dart, for this, which contains the method getProportionateScreenWidth() to retrieve the user screen width and do some calculations.
Because I want a light and dark UI I am creating two ThemeData classes, and have decided to do all TextStyles in it. My issue right now is that when I attempt to call the method to set the font size I get a NoSuchMethodError: The method 'toDouble' was called on null. Presumably the reason is that the ThemeData is calculated before the app startup? Thus there is no screen width to work with so I get an error. Is there an easy fix for this?
//size_config.dart
class SizeConfig {
static MediaQueryData _mediaQueryData;
static double screenWidth;
static double screenHeight;
static double defaultSize;
static Orientation orientation;
void init(BuildContext context) {
_mediaQueryData = MediaQuery.of(context);
screenWidth = _mediaQueryData.size.width;
screenHeight = _mediaQueryData.size.height;
orientation = _mediaQueryData.orientation;
}
}
double getProportionateScreenWidth(double inputWidth) {
double screenWidth = SizeConfig.screenWidth;
// 375 is the layout width that designer use
return (inputWidth / 375.0) * screenWidth;
}
//themes.dart
ThemeData lightTheme() {
return ThemeData (
textTheme: lightTextTheme()
//and other theme stuff
);
}
TextTheme textTheme() {
return TextTheme(
headline1: TextStyle(
color: Color(0xFF000000),
fontSize: getProportionateScreenWidth(25), //problem is with here i believe
fontWeight: FontWeight.normal),
);
}
//main.dart
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: lightTheme(), //instantiating theme
initialRoute: '/onboarding',
routes: {
'/onboarding': (context) => OnboardingScreen(),
'/home': (context) => HomeScreen(),
'/assistant': (context) => AssistantScreen(),
},
);
}
}
//body.dart (where headline1 is being used) don't believe problem is here as this is not even rendered in upon start of the app (an onBoarding screen comes before it named routing is used to navigate the separate screens)
Text(
'Hello,',
textAlign: TextAlign.left,
style: Theme.of(context).textTheme.headline1,
),
//Error message
════════ Exception caught by widgets library ═══════════════════════════════════
The following NoSuchMethodError was thrown building MyApp(dirty):
The method 'toDouble' was called on null.
Receiver: null
Tried calling: toDouble()
The relevant error-causing widget was
MyApp
package:dash/main.dart:17
When the exception was thrown, this was the stack
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
#1 double.* (dart:core-patch/double.dart:36:23)
#2 getProportionateScreenWidth
package:dash/size_config.dart:29
#3 textTheme
package:dash/themes.dart:35
#4 lightTheme
package:dash/themes.dart:11
...
════════════════════════════════════════════════════════════════════════════════
Help is greatly appreciated!
I use media query for this Using MediaQuery.of(context).size.height/100 * 5, I first convert the device height into percentage and multiply it by some no. you can do that too and can create a separate class for dimensions. My dimensions class looks like this.
class Dimensions {
static double boxWidth;
static double boxHeight;
Dimensions(context) {
boxHeight = MediaQuery.of(context).size.height / 100;
boxWidth = MediaQuery.of(context).size.width / 100;
}
}
then I can call Dimensions.box height * (someone) to set any dynamically changing constraints, But remember to initialize this dimension class before any build method.
Just like below.
#override
Widget build(BuildContext context) {
Dimensions(context); //initializing context
return Material(
child: Center(
child: Text(
"This is Splash Screen",
style: TextStyle(fontSize: Dimensions.boxHeight * 5), //Using dynamic sizing.
),
),
);
}
you can also check this package flutter_screenutil
A flutter plugin for adapting screen and font size.Guaranteed to look good on different models
A flutter plugin for adapting screen and font size.Let your UI display a reasonable layout on different screen sizes!
They are still continuously developing it and it is very easy to use.
//main.dart
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
SizeConfig().init(context); // add this line for initialization
return MaterialApp(
title: 'Flutter Demo',
theme: lightTheme(), //instantiating theme
initialRoute: '/onboarding',
routes: {
'/onboarding': (context) => OnboardingScreen(),
'/home': (context) => HomeScreen(),
'/assistant': (context) => AssistantScreen(),
},
);
}
}
I'm using TextField with some initial value. But when I want to add more text like append after that initial value, but always first time I type "space" the TextField gets emptied. Why is this so?
Another annoying thing is why first time tap to TextField the cursor always get to the left not after initial value?
var textEditingController = TextEditingController();
textEditingController.text = 'some_initial_values';
TextField(
decoration: InputDecoration(
),
controller: textEditingController,
)
Edit:
I get the initial values from StreamBuilder via ListView.Builder not a static text.
looking at the source code of TextField, might best answer this question.
if (widget.decoration != null) {
child = AnimatedBuilder(
animation: Listenable.merge(<Listenable>[ focusNode, controller ]),
builder: (BuildContext context, Widget child) {
return InputDecorator(
decoration: _getEffectiveDecoration(),
baseStyle: widget.style,
textAlign: widget.textAlign,
isFocused: focusNode.hasFocus,
isEmpty: controller.value.text.isEmpty,
child: child,
);
},
child: child,
);
}
The InputDecorator class has a textAlign and a isEmpty property that I thinks causing the behaviour you are referring to. Leaving out decoration: The default behaviour is the one you desire.
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
var textEditingController = TextEditingController();
textEditingController.text = 'some_initial_values';
return new MaterialApp(
title: 'Transition Animation Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Center(
child: TextField(
controller: textEditingController,
),
),
),
);
}
}