Hakim Hüseyin
Hakim Hüseyin

Reputation: 109

Flutter: Text.rich widget is revers the WidgetSpan if you add arabic text

the green container is should be in the red and there is in the green.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

Answers (3)

M Atheer
M Atheer

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

Hakim H&#252;seyin
Hakim H&#252;seyin

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

Abdallah A. Odeh
Abdallah A. Odeh

Reputation: 1792

That's because you are WidgetSpans between TextSpans 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

Related Questions