Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter scrollable positioned list support rtl direction?

Tags:

flutter

I use the scrollable positioned package in the following code to move between elements via the index, and it works fine in the case of LTR, but when converting to RTL, some problems occur and the transition is not correct!! I tried to find out the problem but couldn't.

How can this be solved?

LTR:

enter image description here

RTL:

enter image description here

The code:

class Home extends StatefulWidget {
  const Home({Key? key}) : super(key: key);

  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  int currentIndex = 0;
  TextDirection textDirection = TextDirection.ltr;

  late final ItemScrollController itemScrollController;

  @override
  void initState() {
    itemScrollController = ItemScrollController();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Directionality(
      textDirection: textDirection,
      child: Scaffold(
        appBar: AppBar(title: const Text('Scrollable positioned list')),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            SizedBox(
              height: 100,
              child: ScrollablePositionedList.separated(
                itemCount: 10,
                scrollDirection: Axis.horizontal,
                itemScrollController: itemScrollController,
                padding: const EdgeInsets.symmetric(horizontal: 16),
                separatorBuilder: (context, index) => const SizedBox(width: 10),
                itemBuilder: (context, index) => Container(
                  width: 100,
                  height: 50,
                  alignment: AlignmentDirectional.center,
                  decoration: BoxDecoration(
                    color: Colors.red,
                    border: currentIndex == index ? Border.all(color: Colors.black, width: 4) : null,
                  ),
                  child:
                      Text(index.toString(), style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w800)),
                ),
              ),
            ),
            const SizedBox(height: 40),
            ElevatedButton(
              onPressed: () {
                setState(() {
                  if (textDirection == TextDirection.ltr) {
                    textDirection = TextDirection.rtl;
                  } else {
                    textDirection = TextDirection.ltr;
                  }
                });
              },
              child: Text(textDirection.toString()),
            )
          ],
        ),
        floatingActionButton: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            FloatingActionButton(
              onPressed: () {
                setState(() {
                  if (currentIndex != 0) {
                    currentIndex--;
                    itemScrollController.scrollTo(index: currentIndex, duration: const Duration(microseconds: 500), alignment: 0.10);
                  }
                });
              },
              child: const Icon(Icons.arrow_back),
            ),
            FloatingActionButton(
              onPressed: () {
                setState(() {
                  if (currentIndex < 9) {
                    currentIndex++;
                    itemScrollController.scrollTo(index: currentIndex, duration: const Duration(microseconds: 500), alignment: 0.10);
                  }
                });
              },
              child: const Icon(Icons.arrow_forward),
            ),
          ],
        ),
      ),
    );
  }
}
like image 710
Mohammed Helewa Avatar asked Jul 03 '26 02:07

Mohammed Helewa


2 Answers

It seems this might be a bug either from scrollable positioned package library or in Directionality Widget. A simple workaround would be reversing the List Widget by toggling the reverse parameter according to direction.

Reverse property of List Widget only displays the data in reverse order but the functioning remains the same hence you need to change the button functionality accordingly.

import 'package:flutter/material.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';

void main() => runApp(
      const MyApp(), // Wrap your app
    );

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  int currentIndex = 0;
  TextDirection textDirection = TextDirection.ltr;

  late final ItemScrollController itemScrollController;

  @override
  void initState() {
    itemScrollController = ItemScrollController();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            SizedBox(
              height: 100,
              child: ScrollablePositionedList.separated(
                reverse: textDirection == TextDirection.ltr ? false : true, // reverse the list
                itemCount: 10,
                scrollDirection: Axis.horizontal,
                itemScrollController: itemScrollController,
                padding: const EdgeInsets.symmetric(horizontal: 16),
                separatorBuilder: (context, index) => const SizedBox(width: 10),
                itemBuilder: (context, index) => Container(
                  width: 100,
                  height: 50,
                  alignment: AlignmentDirectional.center,
                  decoration: BoxDecoration(
                    color: Colors.red,
                    border: currentIndex == index
                        ? Border.all(color: Colors.black, width: 4)
                        : null,
                  ),
                  child: Text(index.toString(),
                      style: const TextStyle(
                          color: Colors.white, fontWeight: FontWeight.w800)),
                ),
              ),
            ),
            const SizedBox(height: 40),
            ElevatedButton(
              onPressed: () {
                setState(() {
                  if (textDirection == TextDirection.ltr) {
                    textDirection = TextDirection.rtl;
                  } else {
                    textDirection = TextDirection.ltr;
                  }
                });
              },
              child: Text(textDirection.toString()),
            )
          ],
        ),
        floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
        floatingActionButton: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            FloatingActionButton(
              onPressed: () {
                setState(() {
                  if (textDirection == TextDirection.ltr) {
                    if (currentIndex != 0) {
                      currentIndex--;
                      itemScrollController.scrollTo(
                          index: currentIndex,
                          duration: const Duration(microseconds: 500),
                          alignment: 0.10);
                    }
                  } else {
                    if (currentIndex < 9) {
                      currentIndex++;
                      itemScrollController.scrollTo(
                          index: currentIndex,
                          duration: const Duration(microseconds: 500),
                          alignment: 0.10);
                    }
                  }
                });
              },
              child: const Icon(Icons.arrow_back),
            ),
            FloatingActionButton(
              onPressed: () {
                setState(() {
                  if (textDirection == TextDirection.ltr) {
                    if (currentIndex < 9) {
                      currentIndex++;
                      itemScrollController.scrollTo(
                          index: currentIndex,
                          duration: const Duration(microseconds: 500),
                          alignment: 0.10);
                    }
                  } else {
                    if (currentIndex != 0) {
                      currentIndex--;
                      itemScrollController.scrollTo(
                          index: currentIndex,
                          duration: const Duration(microseconds: 500),
                          alignment: 0.10);
                    }
                  }
                });
              },
              child: const Icon(Icons.arrow_forward),
            ),
          ],
        ),
      ),
    );
  }
}
like image 66
Ramji Avatar answered Jul 05 '26 00:07

Ramji


Jus to simplify the solution of @Ramji, here is how the workaround works:

bool isRTL = true; // <- Arabic: true, English: false

return Directionality(
  textDirection: TextDirection.ltr, // <- Always should be 'ltr'
  child: ScrollablePositionedList.separated(
    reverse: isRTL ? true : false,
    // ...
  ),
);

Just declare isRTL properly to detect the current language whether it is Right-To-Left: true or Left-To-Right: false.

like image 42
Osama Remlawi Avatar answered Jul 04 '26 23:07

Osama Remlawi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!