'How to create a constant looping auto-scroll in Flutter?

I am seeking to create a constant scroll of a dynamic number of images across my screen (similar to a news ticker) in Flutter. I want this to be automatic and a constant speed, that also loops.

The simplest solution I have found is to use the Carousel Package which ticks almost all the boxes, except one. I am unable to get a constant scroll speed

A possible solution was to adjust autoPlayInterval to zero, but unfortunately, this paramater appears to need a value of around 50 or greater to run - therefore creating an even scroll.

Any idea on how to tweak it this with this package? Or another suitable solution?

Simplified code:

@override
  Widget build(BuildContext context) {
      return Container(
        child: CarouselSlider(
          items: DynamicImages.list
              .map(
                (e) => Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Image.asset('assets/images/$e.png'),
                ),
              )
              .toList(),
          options: CarouselOptions(
            autoPlay: true,
            autoPlayCurve: Curves.linear,
            autoPlayInterval: Duration(milliseconds: 0), /// carousel will not run if set to zero
            autoPlayAnimationDuration: Duration(milliseconds: 1000)
          ),
        ),
      );
  }
}


Solution 1:[1]

So, I've been working on this since posting and come up with a solution. I am posting my answer in case it helps any others in the future.

Basically, instead of using Carousel package, I used ListView.builder which then continuously grows as needed.

Of note, I needed to use WidgetsBinding.instance.addPostFrameCallback((timeStamp) {}); to get this to work.

It would still be great to see any other solutions, (as I am sure the below workaround could be improved).

import 'package:flutter/material.dart';

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

  @override
  _ScrollLoopState createState() => _ScrollLoopState();
}

class _ScrollLoopState extends State<ScrollLoop> {
  ScrollController _controller = ScrollController();

  /// [_list] is growable and holds the assets which will scroll.
  List<String> _list = [
    "assets/images/image1.png",
  /// etc...
  ];

  /// [_list2] holds duplicate data and is used to append to [_list].
  List<String> _list2 = [];

  /// [_listAppended] ensures [_list] is only appended once per cycle.
  bool _listAppended = false;

  @override
  void initState() {
    _list2.addAll(_list);

    /// To auto-start the animation when the screen loads.
    WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
      _startScroll();
    });

    /// The [_controller] will notify [_list] to be appended when the animation is near completion.
    _controller.addListener(
      () {
        if (_controller.position.pixels >
            _controller.position.maxScrollExtent * 0.90) {
          if (_listAppended == false) {
            _list.addAll(_list2);
            _listAppended = true;
          }
        }

        /// The [_controller] will listen for when the animation cycle completes,
        /// so this can immediately re-start from the completed position.
        if (_controller.position.pixels ==
            _controller.position.maxScrollExtent) {
          _listAppended = false;
          setState(() {});
          WidgetsBinding.instance!.addPostFrameCallback(
            (timeStamp) {
              _startScroll();
            },
          );
        }
      },
    );
    super.initState();
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
  }

  void _startScroll() {
    _controller.animateTo(_controller.position.maxScrollExtent,
        duration: Duration(milliseconds: 8000), curve: Curves.linear);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final _size = MediaQuery.of(context).size;
    return AbsorbPointer(
      child: Material(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Expanded(
                child: ListView.builder(
                  shrinkWrap: true,
                  controller: _controller,
                  scrollDirection: Axis.horizontal,
                  itemCount: _list.length,
                  itemBuilder: (context, index) {
                    return Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: Container(
                          width: _size.width / 4,
                          height: _size.height / 10,
                          child: Image.asset(_list[index]),
                        ),
                     );
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

enter image description here

Solution 2:[2]

you're right about Carousel Package. You try this plugin scroll_loop_auto_scroll

This Plugin automatically scrolls the custom child widget to an infinite loop.

You can adjust, interval for every round, scroll position, Duration. and other stuff.

Other features

  • Infinite Auto Scroll
  • Custom child widgets
  • Delay after every round
  • Custom scroll direction
  • Specifying custom durations and gap

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Kdon
Solution 2