'Flutter Horizontal List View Dot Indicator

I have multiple images each with their own redirect link. Currently this works fine at displaying using a list view build to display the images inside a gesture detector.

However, I’d like to add a dot indicator to show which image is being viewed. How can I get the index of the image being displayed? Or increase / decrease a counter when swiping left or right.

I’ve looked into carousels but they don’t seem to allow each image to redirect to somewhere else.



Solution 1:[1]

If I understand you clear. with package https://pub.dev/packages/carousel_slider swipe image to left or right can get current page from onPageChanged event
and use InkWell wrap image, you can navigate to other page. In my demo just print click message

code snippet

final List child = map<Widget>(
  imgList,
  (index, i) {
    return Container(
      margin: EdgeInsets.all(5.0),
      child: ClipRRect(
        borderRadius: BorderRadius.all(Radius.circular(5.0)),
        child: Stack(children: <Widget>[
          InkWell(
              onTap: () { print("you click  ${index} "); },
              child: Image.network(i, fit: BoxFit.cover, width: 1000.0)),
          Positioned(
            bottom: 0.0,
            left: 0.0,
            right: 0.0,
            child: Container(
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  colors: [Color.fromARGB(200, 0, 0, 0), Color.fromARGB(0, 0, 0, 0)],
                  begin: Alignment.bottomCenter,
                  end: Alignment.topCenter,
                ),
              ),
              padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
              child: Text(
                'No. $index image',
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 20.0,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
          ),
        ]),
      ),
    );
  },
).toList();

...

class _CarouselWithIndicatorState extends State<CarouselWithIndicator> {
  int _current = 0;

  @override
  Widget build(BuildContext context) {
    return Column(children: [
      CarouselSlider(
        items: child,
        autoPlay: false,
        enlargeCenterPage: true,
        aspectRatio: 2.0,
        onPageChanged: (index) {
          setState(() {
            _current = index;
            print("${_current}");
          });
        },
      ),
      Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: map<Widget>(
          imgList,
          (index, url) {
            return Container(
              width: 8.0,
              height: 8.0,
              margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0),
              decoration: BoxDecoration(
                  shape: BoxShape.circle,
                  color: _current == index
                      ? Color.fromRGBO(0, 0, 0, 0.9)
                      : Color.fromRGBO(0, 0, 0, 0.4)),
            );
          },
        ),
      ),
    ]);
  }
}

enter image description here

Solution 2:[2]

As per example pointed to by @chunhunghan above in the comments, you need to replace map<Widget> ( (image, url) {..} with imgList.map((image) {int index = imgList.indexOf(image); ..}

I was able to get it to work with the above change. Revised code snippet for the build method:

  @override
  Widget build(BuildContext context) {
    return Column(children: [
      CarouselSlider(
        items: child,
        autoPlay: false,
        enlargeCenterPage: true,
        aspectRatio: 2.0,
        onPageChanged: (index) {
          setState(() {
            _current = index;
            print("${_current}");
          });
        },
      ),
      Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: imgList.map((image) {       //these two lines 
            int index=imgList.indexOf(image); //are changed
            return Container(
              width: 8.0,
              height: 8.0,
              margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0),
              decoration: BoxDecoration(
                  shape: BoxShape.circle,
                  color: _current == index
                      ? Color.fromRGBO(0, 0, 0, 0.9)
                      : Color.fromRGBO(0, 0, 0, 0.4)),
            );
          },
        ),
      ),
    ]);
  }

Solution 3:[3]

The solution I found, besides change map to use imgList, was to put .toList() at the imgList.map function return as follows:

@override
   Widget build(BuildContext context) {
    return Column(children: [
      CarouselSlider(
        items: child,
        autoPlay: false,
        enlargeCenterPage: true,
        aspectRatio: 2.0,
        onPageChanged: (index) {
          setState(() {
            _current = index;
            print("${_current}");
          });
        },
      ),
      Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: imgList.map((image) {     
            int index=imgList.indexOf(image);
            return Container(
              width: 8.0,
              height: 8.0,
              margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0),
              decoration: BoxDecoration(
                  shape: BoxShape.circle,
                  color: _current == index
                      ? Color.fromRGBO(0, 0, 0, 0.9)
                      : Color.fromRGBO(0, 0, 0, 0.4)
              ),
            );
          },
        ).toList(), // this was the part the I had to add
      ),
    ]);
  }

Solution 4:[4]

Vertical Indicators On Slider or Flutter Carousel


class VerticalSlider extends StatefulWidget {
  @override
  _VerticalSliderState createState() => _VerticalSliderState();
}
class _VerticalSliderState extends State<VerticalSlider> {

  List imageSliders=[
//add your items here
];
  int _current = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Vertical sliding carousel ')),
      body: Stack(

        children: [
          Container(
            child: CarouselSlider(
              options: CarouselOptions(
                viewportFraction: .6,
                aspectRatio: 1.2,
                enlargeCenterPage: true,
                scrollDirection: Axis.vertical,
                autoPlay: true,
                 onPageChanged: (index, reason) {
                setState(() {
                  _current = index;
                });
              }
              ),
              items: imageSliders,
            )
          ),
           Positioned(
            top: 50,
            right: 10,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: imgList.map((url) {
                int index = imgList.indexOf(url);
                return Container(
                  width: 8.0,
                  height: 8.0,
                  margin: EdgeInsets.symmetric(vertical: 4.0, horizontal: 2.0),
                  decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    color: _current == index
                      ? Colors.white60
                      : Color.fromRGBO(0, 0, 0, 0.4),
                  ),
                );
              }).toList(),
            ),
          ),
        ],
      ),
    );
  }
}

And you may add custom indicators too.

Solution 5:[5]

The easiest way is to use CarouselSlider with the AnimatedSmoothIndicator widget

 int _current = 0;

  @override
  Widget build(BuildContext context) {
    Column(children: [
      CarouselSlider(
        options: CarouselOptions(
            height: MediaQuery.of(context).size.height / 2,
            onPageChanged: (i, r) {
              setState(() {
                _current = i;
              });
            }),
        items: imageList.map((e) => Container(
                  child: Image.network(e, fit: BoxFit.cover),
                ))
            .toList(),
      ),
      Container(
        margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0),
        child: AnimatedSmoothIndicator(
          activeIndex: _current,
          count: imageList.length,
          effect: ExpandingDotsEffect(
              activeDotColor: Color.fromRGBO(0, 0, 0, 0.9),
              dotWidth: 8,
              dotHeight: 8),
        ),
      )
    ]);

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 chunhunghan
Solution 2 Balaji Rathakrishnan
Solution 3 Vinicius Alcantara
Solution 4 FEELIX
Solution 5 Krishna Acharya