Flutter ImageSlideshow click through

662

This feature isn't built into that package but when you run into that scenario you can copy the package code and modify it. The main difference is that you have to create your own PageController and pass it into the ImageSlideShow. Then wrap each of the children in a GestureDetector and manually tell the _pageController to animate to the next page in the onTap.

Add this to your stateful widget where you have the ImageSlideShow.

 PageController _pageController;

  @override
  void initState() {
    _pageController = PageController(
      initialPage: 0,
    );
    super.initState();
  }

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

Create a new file and add this modified version of the package code. The only change is that it's using the PageController that's passed in vs one created internally.

import 'dart:async';

import 'package:flutter/material.dart';

class MyImageSlideshow extends StatefulWidget {
  MyImageSlideshow({
    @required this.children,
    @required this.pageController,
    this.width = double.infinity,
    this.height = 200,
    this.initialPage = 0,
    this.indicatorColor = Colors.blue,
    this.indicatorBackgroundColor = Colors.grey,
    this.onPageChanged,
  });
  final List<Widget> children;
  final double width;
  final double height;
  final int initialPage;
  final Color indicatorColor;
  final Color indicatorBackgroundColor;
  final ValueChanged<int> onPageChanged;
  final PageController pageController;

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

class _MyImageSlideshowState extends State<MyImageSlideshow> {
  final StreamController<int> _pageStreamController =
      StreamController<int>.broadcast();

  void _onPageChanged(int value) {
    _pageStreamController.sink.add(value);
    if (widget.onPageChanged != null) {
      widget.onPageChanged(value);
    }
  }

  Widget _indicator(BuildContext context) {
    return Wrap(
      spacing: 4,
      runSpacing: 4,
      alignment: WrapAlignment.center,
      children: List<Widget>.generate(
        widget.children.length,
        (index) {
          return StreamBuilder<int>(
            initialData: widget.pageController.initialPage,
            stream: _pageStreamController.stream.where(
              (pageIndex) {
                return index >= pageIndex - 1 && index <= pageIndex + 1;
              },
            ),
            builder: (_, AsyncSnapshot<int> snapshot) {
              return Container(
                width: 6,
                height: 6,
                decoration: ShapeDecoration(
                  shape: CircleBorder(),
                  color: snapshot.data == index
                      ? widget.indicatorColor
                      : widget.indicatorBackgroundColor,
                ),
              );
            },
          );
        },
      ),
    );
  }

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

  @override
  void dispose() {
    _pageStreamController.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: widget.width,
      height: widget.height,
      child: Stack(
        children: [
          PageView.builder(
            onPageChanged: _onPageChanged,
            itemCount: widget.children.length,
            controller: widget.pageController,
            itemBuilder: (context, index) {
              return widget.children[index];
            },
          ),
          Positioned(
            left: 0.0,
            right: 0.0,
            bottom: 10.0,
            child: _indicator(context),
          ),
        ],
      ),
    );
  }
}

Then on your page use the new custom MyImageSlideshow instead with the added GestureDetectors.

MyImageSlideshow(
      pageController: _pageController,
      width: 400,
      height: 800,
      initialPage: 0,
      indicatorColor: HexColor("#ff9900"),
      indicatorBackgroundColor: Colors.grey,
      children: [
        GestureDetector(
          // this is where you're animating to the next picture
          onTap: () {
            if (_pageController.hasClients) {
              _pageController.animateToPage(
                1,
                duration: const Duration(milliseconds: 400),
                curve: Curves.easeInOut,
              );
            }
          },
          child: Image.asset(
            'third.png',
            fit: BoxFit.cover,
          ),
        ),
        GestureDetector(
          onTap: () {
            if (_pageController.hasClients) {
              _pageController.animateToPage(
                2,
                duration: const Duration(milliseconds: 400),
                curve: Curves.easeInOut,
              );
            }
          },
          child: Image.asset(
            'second.png',
            fit: BoxFit.cover,
          ),
        ),
        GestureDetector(
          onTap: () {
            if (_pageController.hasClients) {
              _pageController.animateToPage(
                0, // going back to first picture
                duration: const Duration(milliseconds: 400),
                curve: Curves.easeInOut,
              );
            }
          },
          child: Image.asset(
            'first.png',
            fit: BoxFit.cover,
          ),
        ),
      ],
      onPageChanged: (value) {
        print('Page changed: $value');
      },
    )

That should do it for 'ya.

Share:
662
Charles Down
Author by

Charles Down

Updated on December 27, 2022

Comments

  • Charles Down
    Charles Down over 1 year

    I want to click trough my ImageSlideshow in Flutter web. is this possible and how, now i only can swipe through it.

    This is the Code

    ImageSlideshow(
                      width: 400,
                      height: 800,
                      initialPage: 0,
                      indicatorColor: HexColor("#ff9900"),
                      indicatorBackgroundColor: Colors.grey,
                      children: [
                        Image.asset(
                          'third.png',
                          fit: BoxFit.cover,
                        ),
                        Image.asset(
                          'second.png',
                          fit: BoxFit.cover,
                        ),
                        Image.asset(
                          'first.png',
                          fit: BoxFit.cover,
                        ),
                      ],
                      onPageChanged: (value) {
                        print('Page changed: $value');
                      },
                    ),
    

    This is the Stateless part where the error was born.

    class Information extends StatelessWidget {
    
    PageController _pageController;
    
      @override
      void initState() {
        _pageController = PageController(
      initialPage: 0,
        );
        super.initState();
      }
    
      @override
      void dispose() {
        _pageController.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
    

    Thanks for any answer.

  • Charles Down
    Charles Down about 3 years
    I get to new errors: Error: Superclass has no method named 'initState'. and Superclass has no method named 'dispose'.
  • Loren.A
    Loren.A about 3 years
    You need to convert that Information to a StatefulWidget. Stateless widgets don't have initState.