Reputation: 109
please can any one help me?
Text.rich widget does not work successfully with arabic text. there are a problem with the text direction.
lets give an example
when i run the app the order of the container in the below code is come reverse
that is a big problem for me
Text.rich(
TextSpan(
children: [
TextSpan(text: 'بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ'),
WidgetSpan(
child: Container(
width: 30,
height: 30,
color: Colors.green,child:Text('1'),
),
),
TextSpan(text: 'ٱلۡحَمۡدُ لِلَّهِ رَبِّ ٱلۡعَٰلَمِينَ'),
WidgetSpan(
child: Container(
width: 30,
height: 30,
color: Colors.blue,child:Text('2'),
),
),
TextSpan(text: 'ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ'),
WidgetSpan(
child: Container(
width: 30,
height: 30,
color: Colors.red,child:Text('3'),
),
),
],
),
textDirection: TextDirection.rtl,
)
Upvotes: 4
Views: 1531
Reputation: 392
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
void main() => runApp(const App());
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Widget Test',
home: TestWidgetSpans(),
);
}
}
double _getYOffsetOf(GlobalKey key) {
final box = key.currentContext!.findRenderObject() as RenderBox;
return box.localToGlobal(Offset.zero).dy;
}
double _getXOffsetOf(GlobalKey key) {
final box = key.currentContext!.findRenderObject() as RenderBox;
return box.localToGlobal(Offset.zero).dx;
}
void _resolveSameRow(List<GlobalKey<_WidgetSpanWrapperState>> keys) {
var middle = (keys.length / 2.0).floor();
for (int i = 0; i < middle; i++) {
var a = keys[i];
var b = keys[keys.length - i - 1];
var left = _getXOffsetOf(a);
var right = _getXOffsetOf(b);
a.currentState!.updateXOffset(right - left);
b.currentState!.updateXOffset(left - right);
}
}
class TestWidgetSpans extends StatelessWidget {
const TestWidgetSpans({super.key});
@override
Widget build(BuildContext context) {
final keys = <GlobalKey<_WidgetSpanWrapperState>>[];
nextKey() {
var key = GlobalKey<_WidgetSpanWrapperState>();
keys.add(key);
return key;
}
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
List<GlobalKey<_WidgetSpanWrapperState>>? sameRow;
GlobalKey<_WidgetSpanWrapperState> prev = keys.removeAt(0);
for (var key in keys) {
if (_getYOffsetOf(key) == _getYOffsetOf(prev)) {
sameRow ??= [prev];
sameRow.add(key);
} else if (sameRow != null) {
_resolveSameRow(sameRow);
sameRow = null;
}
prev = key;
}
if (sameRow != null) {
_resolveSameRow(sameRow);
}
});
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
body: Center(
child: Text.rich(
TextSpan(
text: 'هذا اختبار',
style: TextStyle(
backgroundColor: Colors.grey.withOpacity(0.5),
fontSize: 30,
),
children: [
WidgetSpan(
child: WidgetSpanWrapper(
key: nextKey(),
child: const TestWidgetSpan(color: Colors.red, order: 1),
),
),
const TextSpan(text: ' و '),
WidgetSpan(
child: WidgetSpanWrapper(
key: nextKey(),
child: const TestWidgetSpan(color: Colors.orange, order: 2),
),
),
const TextSpan(text: ' ثم '),
WidgetSpan(
child: WidgetSpanWrapper(
key: nextKey(),
child: const TestWidgetSpan(color: Colors.yellow, order: 3),
),
),
const TextSpan(text: ' ، لكنه معطل'),
WidgetSpan(
child: WidgetSpanWrapper(
key: nextKey(),
child: const TestWidgetSpan(color: Colors.green, order: 4),
),
),
const TextSpan(text: ' اختبارات '),
WidgetSpan(
child: WidgetSpanWrapper(
key: nextKey(),
child: const TestWidgetSpan(color: Colors.blue, order: 5),
),
),
const TextSpan(text: ' اختبارات '),
WidgetSpan(
child: WidgetSpanWrapper(
key: nextKey(),
child: const TestWidgetSpan(color: Colors.purple, order: 6),
),
),
const TextSpan(text: ' اختبارات '),
WidgetSpan(
child: WidgetSpanWrapper(
key: nextKey(),
child: const TestWidgetSpan(color: Colors.pink, order: 7),
),
),
const TextSpan(text: ' اختبارات '),
WidgetSpan(
child: WidgetSpanWrapper(
key: nextKey(),
child: const TestWidgetSpan(color: Colors.lime, order: 8),
),
),
const TextSpan(text: ' اختبارات '),
WidgetSpan(
child: WidgetSpanWrapper(
key: nextKey(),
child: const TestWidgetSpan(color: Colors.teal, order: 9),
),
),
const TextSpan(text: ' اختبارات '),
],
),
),
),
),
);
}
}
class WidgetSpanWrapper extends StatefulWidget {
const WidgetSpanWrapper({super.key, required this.child});
final Widget child;
@override
State<WidgetSpanWrapper> createState() => _WidgetSpanWrapperState();
}
class _WidgetSpanWrapperState extends State<WidgetSpanWrapper> {
Offset offset = Offset.zero;
void updateXOffset(double xOffset) {
setState(() {
offset = Offset(xOffset, 0);
});
}
@override
Widget build(BuildContext context) {
return Transform.translate(
offset: offset,
child: widget.child,
);
}
}
class TestWidgetSpan extends StatelessWidget {
final Color color;
final int order;
const TestWidgetSpan({super.key, required this.color, required this.order});
@override
Widget build(BuildContext context) {
return Container(
color: color.withOpacity(0.5),
width: 40,
child: Center(child: Text(order.toString())),
);
}
}
Upvotes: 0
Reputation: 109
Sometimes the most difficult problems can be solved by very simple things. There may be many long and difficult ways to solve this problem.
[https://github.com/flutter/flutter/issues/54400#issuecomment-662558160][1]
But the simplest way that i found is
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Quran',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Quran Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({
Key? key,
required this.title,
}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Widget> wrapList(List m) {
List<Widget> myList = [];
for (var i in m) {
String a = i['text'] as String;
int b = i['num'];
List l2 = a.split(' ');
myList.addAll(List.generate(l2.length, (e) {
return Text(
e == l2.length - 1 ? '${l2[e]}' : '${l2[e]} ',
style: const TextStyle(fontSize: 18),
);
}));
myList.add(
Container(
alignment: Alignment.center,
width: 20,
height: 20,
child: Text("$b"),
decoration: const BoxDecoration(
color: Colors.green,
),
),
);
}
return myList;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Wrap(
children: wrapList([
{'text': 'بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ', 'num': 1},
{'text': 'ٱلۡحَمۡدُ لِلَّهِ رَبِّ ٱلۡعَٰلَمِينَ', 'num': 2},
{'text': 'ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ', 'num': 3},
{'text': 'مَٰلِكِ يَوۡمِ ٱلدِّينِ', 'num': 4},
{'text': 'إِيَّاكَ نَعۡبُدُ وَإِيَّاكَ نَسۡتَعِينُ', 'num': 5},
{'text': 'ٱهۡدِنَا ٱلصِّرَٰطَ ٱلۡمُسۡتَقِيمَ', 'num': 6},
{
'text':
'صِرَٰطَ ٱلَّذِينَ أَنۡعَمۡتَ عَلَيۡهِمۡ غَيۡرِ ٱلۡمَغۡضُوبِ عَلَيۡهِمۡ وَلَا ٱلضَّآلِّينَ',
'num': 7
},
]),
textDirection: TextDirection.rtl,
crossAxisAlignment: WrapCrossAlignment.center,
),
),
);
}
}
Upvotes: 2
Reputation: 1792
That's because you are WidgetSpan
s between TextSpan
s which ruins the TextDirection
because WidgetSpan
does not follow the directionality,
you can replace the WidgetSpan
with a TextSpan
and it will work I tested id
Text.rich(
TextSpan(
children: [
TextSpan(text: 'بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ'),
TextSpan(text: ' 1 ', style: TextStyle(
backgroundColor: Colors.green,
)),
TextSpan(text: 'ٱلۡحَمۡدُ لِلَّهِ رَبِّ ٱلۡعَٰلَمِينَ'),
TextSpan(text: ' 2 ', style: TextStyle(
backgroundColor: Colors.blue,
)),
TextSpan(text: 'ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ'),
TextSpan(text: ' 3 ', style: TextStyle(
backgroundColor: Colors.red,
)),
],
),
style: TextStyle(
fontFamily: 'UthmanicHafs1'
),
textDirection: TextDirection.rtl,
),
Upvotes: 1