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:

RTL:

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),
),
],
),
),
);
}
}
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),
),
],
),
),
);
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With