How to create a carrousel (sliding animation) with PageView in Flutter?

bilal picture bilal · Dec 20, 2018 · Viewed 7.1k times · Source

I want to create slider animation with Images and also want to allow user to use swipe gesture to move back and forth. Another requirement is Page indicator. For this purpose, I used

page_indicator: ^0.1.3

Currently I am able to slide between images using swipe gesture with page indicator and now I want to animate slides repeatedly with x amount of duration. My code is below.

final PageController controller = new PageController();

@override
Widget build(BuildContext context) {
List<Widget> list = new List<Widget>();
list.add(new SliderBox(image: 'assets/shirt.png'));
list.add(new SliderBox(image: 'assets/laptops.png'));
list.add(new SliderBox(image: 'assets/bags.png'));

PageIndicatorContainer container = new PageIndicatorContainer(
  pageView: new PageView(
    children: list,
    controller: controller,
  ),
  length: 3,
  padding: EdgeInsets.fromLTRB(10, 40, 10, 10),
  indicatorSpace: 10,
  indicatorColor: Colors.grey[350],
  indicatorSelectorColor: Colors.grey,
);

return Stack(children: <Widget>[
  Container(color: Colors.grey[100], height: double.infinity),
  Container(
      color: Colors.white,
      child: container,
      margin: EdgeInsets.only(bottom: 50)),Text('$moveToPage')
]);


class SliderBox extends StatelessWidget {
final image;

const SliderBox({Key key, this.image}) : super(key: key);

@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
    padding: EdgeInsets.all(10),
    child: Image.asset(
      image,
      height: 300,
      fit: BoxFit.fill,
    ));
}
}

Answer

Miguel Ruivo picture Miguel Ruivo · Dec 20, 2018

I've modified your widget a little in order to provide you a complete example. You can do this in multiple ways, with AnimationController by itself or even combined with a custom Animation or, you can go the fastest way for what it seems that you want to achieve: using a recursive method that waits a x duration (time stand on a single page) and then animates with some new duration to the new page. For that you can for example:

  1. Make your List available within the state itself in order to retrieve its length.

  2. Create the recursive method that will handle the animation itself.

  3. Make sure that you call it after the first frame is rendered on the screen to prevent the PageController of being accessed before having a PageView rendered on the screen, which you probably don't want. For that you take advantage of the WidgetsBinding.instance.addPostFrameCallback.

demo

class Carousel extends StatefulWidget {
  _CarouselState createState() => _CarouselState();
}

class _CarouselState extends State<Carousel> with SingleTickerProviderStateMixin {
  final PageController _controller = PageController();

  List<Widget> _list = [
    SliderBox(
        child: FlutterLogo(
      colors: Colors.red,
    )),
    SliderBox(
        child: FlutterLogo(
      colors: Colors.green,
    )),
    SliderBox(
        child: FlutterLogo(
      colors: Colors.blue,
    ))
  ];

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) => _animateSlider());
  }

  void _animateSlider() {
    Future.delayed(Duration(seconds: 2)).then((_) {
      int nextPage = _controller.page.round() + 1;

      if (nextPage == _list.length) {
        nextPage = 0;
      }

      _controller
          .animateToPage(nextPage, duration: Duration(seconds: 1), curve: Curves.linear)
          .then((_) => _animateSlider());
    });
  }

  @override
  Widget build(BuildContext context) {
    PageIndicatorContainer container = new PageIndicatorContainer(
      pageView: new PageView(
        children: _list,
        controller: _controller,
      ),
      length: _list.length,
      padding: EdgeInsets.fromLTRB(10, 40, 10, 10),
      indicatorSpace: 10,
      indicatorColor: Colors.grey[350],
      indicatorSelectorColor: Colors.grey,
    );

    return Stack(
      children: <Widget>[
        Container(color: Colors.grey[100], height: double.infinity),
        Container(color: Colors.white, child: container, margin: EdgeInsets.only(bottom: 50)),
      ],
    );
  }
}

class SliderBox extends StatelessWidget {
  final Widget child;
  const SliderBox({Key key, this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(padding: EdgeInsets.all(10), child: child);
  }
}