serenader2014 / flutter_carousel_slider

A flutter carousel widget, support infinite scroll, and custom child widget.
https://pub.dev/packages/carousel_slider
MIT License
1.56k stars 523 forks source link

Prevent items for re-creating #187

Open scaraux opened 3 years ago

scaraux commented 3 years ago

I am passing a List of items to the Carousel Slider.

However I can notice that when moving from one cell to another, and going back and forth, the Widgets are being initState-ed and dispose-ed all the time. Is there a way to prevent this reloading behavior ?

jacksonw765 commented 3 years ago

Are they Stateful or Stateless widgets?

DirtyNative commented 3 years ago

I am having the same issue. I am using a Stateless Widget and the stacked plugin to implement it the MVVM way.

class LoginRegisterView extends StatelessWidget {
  final List<Widget> _views = <Widget>[
    LoginView(),
    RegisterView(),
  ];

  @override
  Widget build(BuildContext context) {
    return ViewModelBuilder<LoginRegisterViewModel>.reactive(
      viewModelBuilder: () => new LoginRegisterViewModel(),
      builder: (context, model, child) => Scaffold(
        body: Stack(
          children: <Widget>[
            Container(
              child: CarouselSlider(
                items: _views,
                options: CarouselOptions(
                  initialPage: 0,
                  height: double.infinity,
                ),
              ),
            ),
...
class LoginView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ViewModelBuilder<LoginViewModel>.reactive(
      viewModelBuilder: () => new LoginViewModel(
        dialogWrapper: GetIt.I.get(),
        navigationService: GetIt.I.get(),
        localAuthService: GetIt.I.get(),
        loginExecutor: GetIt.I.get(),
        employeesExecutor: GetIt.I.get(),
        localStorageService: GetIt.I.get(),
        secureStorageService: GetIt.I.get(),
      ),
      builder: (context, model, child) => Scaffold( ...

Every time I swipe to an other view, it rebuilds, and with it, a new instance of the ViewModel is created. I think there is no need to rebuild it.

Also if I create the ViewModel as a variable and assign it inside the ViewModelBuilder, I get an exception, that the model is already disposed and cannot be assigned

shangl commented 3 years ago

I have the same issue. The widget tree below contains a stateful widget. I also tried to set a key in the top level widget, however, the state is erased on sliding back and fourth. How can I preserve the state?

shangl commented 3 years ago

In case other people run into the same problem, here is the solution:

You can indicate to flutter to keep the state by adding "with AutomaticKeepAliveClientMixin" to the state you want to preserve. Additionally, you need to tell flutter explicitly to keep a certain state instance by overriding the method

@override bool get wantKeepAlive => true;

Unfortunately this will not lead to the state being preserved for infinite swiping, but swiping back will work perfectly fine.

simo9900 commented 3 years ago

In case other people run into the same problem, here is the solution:

You can indicate to flutter to keep the state by adding "with AutomaticKeepAliveClientMixin" to the state you want to preserve. Additionally, you need to tell flutter explicitly to keep a certain state instance by overriding the method

@override bool get wantKeepAlive => true;

Unfortunately this will not lead to the state being preserved for infinite swiping, but swiping back will work perfectly fine.

Could you provide us the full code?

I tried it but it not worked.

Thanks

tyfoo1603 commented 3 years ago

I am having the issue. @shangl can you provide the code as reference?

alibasta commented 2 years ago

Thanks @shangl for providing this workaround. After infinite swipe the widget is recreated and the state is gone. Do you have a workaround for this?

This bug is very annoying inside flutter_carousel_slider. There are other open issues reporting the same behavior (e.g. https://github.com/serenader2014/flutter_carousel_slider/issues/315 )

iEricKoh commented 2 years ago

i am having the same issue, all components on my screen was rebuilding due to this package TAT

andreasgarvik commented 1 year ago

For me the problem was that I was calling setState inside onPageChanged in CarouselOptions, I refactored the state outside using the provider pattern.

Merculiar commented 9 months ago

In case other people run into the same problem, here is the solution:

You can indicate to flutter to keep the state by adding "with AutomaticKeepAliveClientMixin" to the state you want to preserve. Additionally, you need to tell flutter explicitly to keep a certain state instance by overriding the method

@OverRide bool get wantKeepAlive => true;

Unfortunately this will not lead to the state being preserved for infinite swiping, but swiping back will work perfectly fine.

There is also workaround if you want InfiniteScroll, but in that case you need to do all custom: set autoplay to false, set enableInfiniteScroll to false, customTimer for autoplay and use this method instead of just carouselController.nextPage: void customNextPage() { if (_pageIndex == carouselWidgets.length) { carouselController.animateToPage( 0, curve: Curves.fastOutSlowIn, ); } else { carouselController.nextPage( curve: Curves.fastOutSlowIn, ); } }

kobars commented 4 months ago

@simo9900 @tyfoo1603

I'm not sure if you all are still experiencing this issue, but I also found it quite challenging. Here is my solution utilizing AutomaticKeepAliveClientMixin and wantKeepAlive as @shangl suggested:

class CustomScreen extends StatelessWidget {
  const CustomScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return const CustomCarousel(
      carouselItems: [
        CarouselItem(name: 'Text 1'),
        CarouselItem(name: 'Text 2'),
        CarouselItem(name: 'Text 3'),
      ],
    );
  }
}

class CustomCarousel extends StatelessWidget {
  const CustomCarousel({
    super.key,
    required this.carouselItems,
  });

  final List<Widget> carouselItems;

  @override
  Widget build(BuildContext context) {
    return CarouselSlider.builder(
      options: CarouselOptions(
        initialPage: 0,
        scrollDirection: Axis.horizontal,
        onPageChanged: (index, reason) {},
      ),
      itemCount: carouselItems.length,
      itemBuilder: (context, index, realIndex) => carouselItems[index],
    );
  }
}

class CarouselItem extends StatefulWidget {
  const CarouselItem({super.key, required this.name});

  final String name;

  @override
  State<StatefulWidget> createState() => _CarouselItemState();
}

class _CarouselItemState extends State<CarouselItem> with AutomaticKeepAliveClientMixin {
  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Center(
      child: Text(widget.name),
    );
  }

  @override
  bool get wantKeepAlive => true; // Ensures the state is kept alive.
}