flutterpositionscrollbar

How to align scroll bar outside of its ListView?


enter image description here

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,
          );
  }
}

Solution

  • 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;
    }