So, the problem is in my RawScrollBar. There's no problem to place it inside ListView, but I need it to be outside of all containers. I can place it outside like on picture, but it seems like there's no way to place it to bottom where I need it to be. So, there's any way to change position for my RawScrollBar?
Additional info: on picture numbers 1, 2 and 3 are containers, and ListView itself is a table inside a container. It's easily scrollable, and scrollbar does show scrolling for it. Problem is a position only. I underlined with red the position where RawScrollBar just stucks, but I need it to take question mark position instead.
Here's my short main code, I'll try to provide more if necessary:
Widget build(BuildContext context) {
return MyScrollbar (
controller: scrollCtrl,
child: GetBuilder<...>(
tag: controller!.tag,
init: controller!,
builder: (c) => Container(
margin: const EdgeInsets.all(24),
child: Column(
children: [
Container(...),
const SizedBox(height: 24),
Expanded(
child: _Body(..., scrollController), //another container inside this Body, and ListView inside its container
),
],
),
),
),
);
}
MyScrollbar widget:
class MyScrollbar extends StatelessWidget {
final ScrollController controller;
final Widget child;
const MyScrollbar ({
required this.controller,
required this.child,
});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(right: 6),
child: RawScrollbar(
controller: controller,
radius: const Radius.circular(12),
trackRadius: const Radius.circular(12),
thumbVisibility: true,
trackVisibility: true,
scrollbarOrientation: ScrollbarOrientation.right,
child: child,
);
}
}
You can override the ScrollConfiguration and set scrollBar to false. Then use NotificationListener
+ AnimatedBuilder
or CustomPaint for the scrollBar.
FIXME: currently I am falling to get the ratio of (MaxScrollExtent/viewPortHeight)
If I made any update in future, you can find it in gist 46f502e70de001e159b6dcf2e7a0a39a
class MyListView extends StatefulWidget {
const MyListView({super.key});
@override
State<MyListView> createState() => _MyListViewState();
}
class _MyListViewState extends State<MyListView> {
final ScrollController controller = ScrollController();
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: ScrollConfiguration(
behavior: ScrollBehavior().copyWith(scrollbars: false),
child: Column(
children: [
Container(
height: 200,
width: double.infinity,
color: Colors.cyan,
),
Expanded(
child: ListView.builder(
controller: controller,
itemCount: 23,
itemBuilder: (context, index) => ListTile(
tileColor: index.isEven ? Colors.red : Colors.white,
),
),
)
],
),
),
),
const SizedBox(width: 48),
SizedBox(
height: double.infinity,
width: 48,
child: CustomPaint(
painter: MyScrollBarPainter(controller),
),
)
],
);
}
}
class MyScrollBarPainter extends CustomPainter {
const MyScrollBarPainter(this.controller) : super(repaint: controller);
final ScrollController controller;
@override
void paint(Canvas canvas, Size size) {
if (controller.hasClients == false) return;
///FIXME: currently I am falling to get the ratio of (MaxScrollExtent/viewPortHeight)
final barHeight = controller.position.maxScrollExtent / 10;
final t = controller.offset / controller.position.maxScrollExtent;
final double top = lerpDouble(0, size.height - barHeight, t) ?? 1;
debugPrint("t $t height $barHeight");
final rrect = RRect.fromRectAndRadius(
Rect.fromLTWH(0, top, 10, barHeight),
Radius.circular(12),
);
canvas.drawRRect(rrect, Paint()..color = Colors.green);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}