flutter_bidi_text
A Flutter package that provides text widgets with automatic bidirectional (RTL/LTR) text direction detection. This package is ideal for multilingual applications that need to handle both right-to-left and left-to-right text seamlessly.

Features
- Automatic Direction Detection: Text direction is automatically determined based on content.
- Direction Change Callbacks: Be notified when text direction changes.
- Text Widget Types:
BidiText
: A drop-in replacement for Flutter'sText
widgetBidiRichText
: A drop-in replacement for Flutter'sRichText
widget withTextSpan
supportBidiTextField
: An input field with automatic direction detectionBidiTextFormField
: A form field with automatic direction detection
- Support for Mixed Content: Handles mixed RTL/LTR content intelligently.
- Configurable Sampling: Control how much text is analyzed for direction detection.
- Complete Drop-in Replacement: All widgets inherit from Flutter's standard widgets, so they support all the same properties and can be used as direct replacements.
Why Use flutter_bidi_text?
If your app supports multiple languages with different writing directions, you've likely encountered these challenges:
- Text direction needs to be manually set based on language
- Mixed language content displays incorrectly
- Direction changes require manual intervention
This package solves all these problems by automatically detecting and setting the appropriate text direction in real-time.
Technical Background
Bidirectional text (often abbreviated as BiDi) refers to text that contains both right-to-left (RTL) and left-to-right (LTR) text directionalities. This is common in multilingual applications where, for example, English text (LTR) is mixed with Arabic or Hebrew text (RTL).
Understanding the Unicode Bidirectional Algorithm
The Unicode Standard provides a sophisticated algorithm for handling bidirectional text. Unicode characters are categorized into different types:
- Strong Characters: Characters with a definite direction (e.g., Latin letters for LTR, Arabic/Hebrew letters for RTL)
- Weak Characters: Characters with vague direction (e.g., numbers, arithmetic symbols)
- Neutral Characters: Characters with indeterminate direction (e.g., whitespace, common punctuation)
- Explicit Formatting: Special Unicode control characters that override default behavior
How This Package Works
This package implements a practical text direction detection algorithm that:
- Analyzes Character Content: Scans the text for strong directional characters
- Counts Directional Runs: Groups consecutive characters with the same directionality
- Applies Threshold Logic: Determines overall direction based on the ratio of RTL to LTR content
- Handles Mixed Content: Makes intelligent decisions when text contains both directions
The algorithm uses a threshold-based approach: if RTL characters exceed approximately 40% of strongly directional characters, the text is considered RTL; otherwise, it defaults to LTR. This provides robust handling of real-world multilingual content.
Real-World Example
Consider the text: "Hello مرحبا 123"
(English + Arabic + numbers)
- "Hello" → Strong LTR characters
- "مرحبا" → Strong RTL characters
- "123" → Weak characters (inherit direction from context)
The package analyzes the ratio of strong characters and determines the appropriate text direction automatically.
Further Reading
For a deeper understanding of bidirectional text handling, see:
- Bidirectional text on Wikipedia
- Unicode Bidirectional Algorithm (UAX #9)
- W3C Guidelines on Bidirectional Text
Installation
Add this dependency to your pubspec.yaml
file:
dependencies:
flutter_bidi_text: ^1.1.0
Then run:
flutter pub get
Usage
Drop-in Replacement
The widgets in this package are designed as direct replacements for Flutter's standard text widgets. Simply add "Bidi" before the original widget name:
Flutter Widget | Bidirectional Replacement |
---|---|
Text |
BidiText |
RichText |
BidiRichText |
TextField |
BidiTextField |
TextFormField |
BidiTextFormField |
Since our widgets inherit from Flutter's standard widgets, they support all the same properties and can be used as direct replacements with minimal code changes.
BidiText
A simple drop-in replacement for the standard Flutter Text
widget:
import 'package:flutter_bidi_text/flutter_bidi_text.dart';
// Automatically displays in LTR direction
BidiText('Hello world')
// Automatically displays in RTL direction
BidiText('مرحبا بالعالم')
// Configure how much text to sample for direction detection
BidiText(
'مرحبا بالعالم Hello world',
sampleLength: 10, // Only analyze first 10 characters
)
// Use null to analyze the entire text
BidiText(
longText,
sampleLength: null,
)

BidiRichText
A drop-in replacement for Flutter's RichText
widget with automatic direction detection for styled text using TextSpan
:
import 'package:flutter_bidi_text/flutter_bidi_text.dart';
// English text with different styles (LTR)
BidiRichText(
text: TextSpan(
style: TextStyle(fontSize: 16, color: Colors.black),
children: [
TextSpan(text: 'This is '),
TextSpan(
text: 'bold',
style: TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(text: ' and this is '),
TextSpan(
text: 'italic',
style: TextStyle(fontStyle: FontStyle.italic),
),
TextSpan(text: ' text.'),
],
),
)
// Arabic text with different styles (RTL)
BidiRichText(
text: TextSpan(
style: TextStyle(fontSize: 16, color: Colors.black),
children: [
TextSpan(text: 'هذا نص '),
TextSpan(
text: 'عريض',
style: TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(text: ' وهذا نص '),
TextSpan(
text: 'مائل',
style: TextStyle(fontStyle: FontStyle.italic),
),
],
),
)
// Mixed content with colors
BidiRichText(
text: TextSpan(
style: TextStyle(fontSize: 16, color: Colors.black),
children: [
TextSpan(
text: 'English text',
style: TextStyle(color: Colors.blue),
),
TextSpan(text: ' with '),
TextSpan(
text: 'نص عربي',
style: TextStyle(color: Colors.red),
),
],
),
)
// With icons using WidgetSpan
BidiRichText(
text: TextSpan(
style: TextStyle(fontSize: 16, color: Colors.black),
children: [
TextSpan(text: 'Rate us '),
WidgetSpan(
child: Icon(Icons.star, size: 18, color: Colors.amber),
),
TextSpan(text: ' on the app store!'),
],
),
)
The BidiRichText
widget analyzes all text content in the TextSpan
tree (including nested children) to determine the overall text direction, while ignoring WidgetSpan
elements.

BidiTextField
A text input that automatically adjusts its direction based on content:
import 'package:flutter_bidi_text/flutter_bidi_text.dart';
BidiTextField(
decoration: InputDecoration(hintText: 'Enter text'),
onDirectionChanged: (TextDirection? direction) {
print('Text direction changed to: $direction');
},
// sampleLength: 50, // Analyze first 50 characters (default)
)

BidiTextFormField
A form field that automatically adjusts text direction:
import 'package:flutter_bidi_text/flutter_bidi_text.dart';
BidiTextFormField(
decoration: InputDecoration(
labelText: 'Message',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onDirectionChanged: (TextDirection? direction) {
print('Text direction changed to: $direction');
},
// sampleLength: 50,
)

Advanced Usage
Utility Functions
The package also provides utility functions for direction detection in a convenient BidiHelper
class:
import 'package:flutter_bidi_text/flutter_bidi_text.dart';
// Using the BidiHelper class
TextDirection? direction = BidiHelper.estimateDirectionOfText('مرحبا بالعالم');
// Returns TextDirection.rtl
bool isRtl = BidiHelper.detectRtlDirectionality('Hello world');
// Returns false
// You can also use the standalone functions if preferred
TextDirection? direction2 = estimateDirectionOfText('مرحبا بالعالم');
bool isRtl2 = detectRtlDirectionality('Hello world');
Custom Controllers
You can use your own TextEditingController with both input widgets:
final controller = TextEditingController(text: 'Initial text');
BidiTextField(
controller: controller,
onDirectionChanged: (direction) {
// Handle direction change
},
)
Performance Optimization
For very long text, you can control how much text is sampled to determine direction:
// Only analyze first 20 characters (faster)
BidiText(
veryLongText,
sampleLength: 20,
)
// Analyze entire text (more accurate but potentially slower)
BidiText(
mixedDirectionText,
sampleLength: null,
)
Real-World Examples
Multilingual Chat Application
BidiTextField(
controller: messageController,
decoration: InputDecoration(
hintText: 'Type a message...',
suffixIcon: IconButton(
icon: Icon(Icons.send),
onPressed: sendMessage,
),
),
onDirectionChanged: (direction) {
// Align the send button based on text direction
setState(() {
sendButtonAlignment = direction == TextDirection.rtl
? Alignment.centerLeft
: Alignment.centerRight;
});
},
)
Multilingual Form
Form(
key: _formKey,
child: Column(
children: [
BidiTextFormField(
decoration: InputDecoration(labelText: 'Name / الاسم'),
validator: (value) => value!.isEmpty ? 'Required' : null,
),
BidiTextFormField(
decoration: InputDecoration(labelText: 'Address / العنوان'),
validator: (value) => value!.isEmpty ? 'Required' : null,
maxLines: 3,
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
// Submit form
}
},
child: Text('Submit'),
),
],
),
)
Contributing
Contributions are welcome! If you'd like to contribute, please feel free to submit a PR or open an issue.
Setting Up Development Environment
-
Clone the repository
git clone https://github.com/Mohammed-Al-Zubiri/flutter_bidi_text.git
-
Install dependencies
flutter pub get
-
Run tests
flutter test
Frequently Asked Questions
Q: How does direction detection work?
A: The package analyzes the text content to detect RTL characters. If the proportion of RTL characters exceeds a threshold, the text is considered RTL. For optimization, you can control how much text is sampled.
Q: Can I use this with Flutter's Directionality
widget?
A: Yes! You can wrap your widgets in Directionality
for a default direction, and the BidiText widgets will still override as needed based on content.
Q: How does this handle numbers and special characters?
A: Numbers and most special characters are considered neutral in terms of directionality and don't affect the direction detection.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Libraries
- bidi_rich_text
- bidi_text
- bidi_text_field
- bidi_text_form_field
- flutter_bidi_text
- Flutter package for text widgets with automatic bidirectional text direction detection
- helpers/bidi_helper