0

I have a call that call this carousel and based on the widget clicked, it shows different content (through the contentUrls argument). The content shows up fine but I tried including the DotsIndicator widget and the position (activePage) variable is not updating. It takes its value from the ref. For instance, if on a widget I moved to image 5, when I go to a different carousel, it starts on image 5 rather than on image 0. I am not understanding how the activePage variable works through the setState.

import 'package:activo/widgets/video_player.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'package:dots_indicator/dots_indicator.dart';

class Carousel extends StatefulWidget {
  const Carousel({super.key, required this.contentUrls});
  final String contentUrls;

  @override
  State<Carousel> createState() => _CarouselState();
}

class _CarouselState extends State<Carousel> {
  double activePage = 0.0;
  final GlobalKey _key = GlobalKey();

  @override
  Widget build(BuildContext context) {
    List<String> contentUrls = widget.contentUrls.split(' , ');

    return Column(
      children: [
        CarouselSlider(
          key: _key,
          options: CarouselOptions(
            height: 300.0,
            onPageChanged: (val, _) {
              setState(() {
                activePage = val * 1.0;
              });
            },
          ),
          items: contentUrls.map(
            (currentContent) {
              return Builder(
                builder: (BuildContext context) {
                  return Container(
                    decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(15),
                      border: Border.all(
                        color: Colors.white,
                      ),
                    ),
                    width: MediaQuery.of(context).size.width,
                    margin: const EdgeInsets.symmetric(horizontal: 5.0),
                    child: ClipRRect(
                      borderRadius: BorderRadius.circular(15),
                      child: currentContent.contains('mp4')
                          ? VideoPlayerWidget(
                              videoUrl: currentContent,
                            )
                          : Image.network(
                              currentContent,
                              // will need to change it based on pictures for events
                              fit: BoxFit.fill,
                            ),
                    ),
                  );
                },
              );
            },
          ).toList(),
        ),
        Text('$activePage'),
        DotsIndicator(
          dotsCount: contentUrls.length,
          position: activePage,
        ),
      ],
    );
  }
}

Thanks!

I am expecting that when I load this widget, the activePage's value is 0 or at least the last value for that specific carousel (as I have multiple) rather than the last values from some other widget.

Oer
  • 447
  • 5
  • 8

1 Answers1

0

Here is your basic code adjusted to work with an index using setState((){}).


import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';

class Carousel extends StatefulWidget {
  const Carousel({super.key, required this.contentUrls});

  final List<String> contentUrls;

  @override
  State<Carousel> createState() => _CarouselState();
}

class _CarouselState extends State<Carousel> {
  int activePage = 1;
  final GlobalKey _key = GlobalKey();

  _onPageChanged(int index) {
    activePage = index;
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        CarouselSlider(
            key: _key,
            options: CarouselOptions(
              height: 300.0,
              onPageChanged: (index, _) {
                _onPageChanged(index);
              },
            ),
            items: widget.contentUrls
                .map((e) => Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Container(
                        color: Colors.blue,
                        child: Center(child: Text(e)),
                      ),
                    ))
                .toList()),
      ],
    );
  }
}

Also, as you mentioned at the end, you may want the last carousel card to show up when you load the page again. For that you can save the page index using a package to manage the app state like Provider.

Here is the example using Provider (also with GIF images fetched from online).

import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => CarouselNotifier()),
      ],
      child: const MaterialApp(
        home:
        Material(child: Carousel(contentUrls: ['https://media.giphy.com/media/wW95fEq09hOI8/giphy.gif',
          'https://media.giphy.com/media/SggILpMXO7Xt6/giphy.gif','https://media.giphy.com/media/KHJw9NRFDMom487qyo/giphy.gif'
        ])),
      ),
    ),
  );
}

class Carousel extends StatelessWidget {
  const Carousel({super.key, required this.contentUrls});

  final List<String> contentUrls;

  @override
  Widget build(BuildContext context) {
    return Consumer(
      builder: (_, notifier, __) =>
          CarouselSlider(
              key: key,
              options: CarouselOptions(
                height: 300.0,
                onPageChanged: (index, _) {
                  Provider.of<CarouselNotifier>(context, listen: false)
                      .setIndex(index);
                },
              ),
              items: contentUrls
                  .map((e) =>
                  Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Container(
                      color: Colors.blue,
                      // display images from urls etc
                      child: Column(
                        children: [
                          Expanded(
                              flex: 3,
                              child: Center(child: Image.network(e))),
                          Expanded(child: Text('Page index ${contentUrls.indexOf(e)}'))
                        ],
                      ),
                    ),
                  ))
                  .toList()),
    );
  }
}

class CarouselNotifier extends ChangeNotifier {
  int activePage = 0;

  setIndex(int index) {
    activePage = index;
    notifyListeners();
  }
}

enter image description here

Kdon
  • 892
  • 6
  • 19
  • Thanks for the explanation! There is something weird happening, I tried your code, and it works by itself, but when I load my images and videos (or text like in your example), it stops working. I am calling the different Carousel from a ScallPanel under a StreamBuilder. Do you think the parent widget has something to do with it? – Oer Feb 02 '23 at 03:59
  • OK, it could be the case. Perhaps try breaking down and splitting the parent widgets up to identify the error cause? Note, To confirm, I used Image.network() also to check if works and it does still. I updated my answer (the second example) if it helps... – Kdon Feb 02 '23 at 06:02