GotJimmy / accordion

Other
46 stars 43 forks source link

Accordion closes/rebuilds when on state change when it shouldn't #47

Closed SexyBeast007 closed 1 year ago

SexyBeast007 commented 1 year ago

This is a copy/paste from my SO post. Trying to get this issue sorted, any help is appreciated. https://stackoverflow.com/questions/76696945/accordion-widget-closes-automatically-is-rebuilt-on-state-change-how-to-avoi

I have a few different situations where state changes in Accordion child widgets are causing entire Accordion widget rebuilds which in turn causes accordions to close on state change. My example below is highly simplified. The CustomRadioButton enforcedValue is the state change.

For example, a radio button is tapped, the state changes and the accordion closes. Upon reopening the accordion the state is updated.

Of course this is undesired, the entire accordion parent should not rebuild. I'm using Riverpod, the whole point of riverpod is to enact only rebuilds of child widgets that require it and not rebuild larger parents.

I have tried messing around with keys but I don't have any conclusive good changes to report along this line.

What am I doing wrong here? Is there something I can change while maintaining widget reusability as I currently have?

class Page extends ConsumerWidget {
  const Page({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // Exemplary boolean visibility values
    bool one = true;
    bool two = true;
    bool three = true;

    return SingleChildScrollView(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisSize: MainAxisSize.min,
        children: [
          pulldownExample(context, ref, null),
          Visibility(visible: one, child: pulldownExample(context, ref, 0)),
          Visibility(visible: two, child: pulldownExample(context, ref, 1)),
          Visibility(visible: three, child: pulldownExample(context, ref, 2)),
        ],
      ),
    );
  }

  // FP magnitude notification pulldown
  Widget pulldownExample(
    BuildContext context,
    WidgetRef ref,
    int? index,
  ) {
    return Accordion(
      scaleWhenAnimating: false,
      openAndCloseAnimation: true,
      flipRightIconIfOpen: true,
      disableScrolling: true,
      children: [
        AccordionSection(
          header: Text('DYNAMIC HEADER TEXT'),
          content: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  CustomRadioButton(
                    enforcedValue: ref.watch(exampleState).valueOne,
                    onTap: (_) => onTapLogic(),
                  ),
                  const SizedBox(width: 8),
                  Text('ONE'),
                ],
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  CustomRadioButton(
                    enforcedValue: ref.watch(exampleState).valueTwo,
                    onTap: (_) => onTapLogic(),
                  ),
                  const SizedBox(width: 8),
                  Text('TWO'),
                ],
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  CustomRadioButton(
                    enforcedValue: ref.watch(exampleState).valueThree,
                    onTap: (_) => onTapLogic(),
                  ),
                  const SizedBox(width: 8),
                  Text('THREE'),
                ],
              ),
            ],
          ),
        ),
      ],
    );
  }
}
GotJimmy commented 1 year ago

1) I wouldn't put Accordion in a separate reusable widget since it already is just that 2) I don't see the point of the Column widget either 3) don't know what onTapLogic does specifically, but do you want to open/close an AccordionSection? If so, you should use the isOpen parameter on the AccordionSection widget. 4) if the entire Accordion still rebuilds then it must be that your state forces a rebuild. Then you need to find what causes the rebuild outside of Accordion. This happened a number of times before and every time it was the state causing the rebuild.

SexyBeast007 commented 1 year ago

1, 2 & 3 are moot as far as helping this issue.

4 however helped me suck it up and rebuild this part of my widgets so things worked well and are optimized properly.

What ended up fixing the issue is NOT passing a WidgetRef via the Accordion widget instance but instead to create a separate ConsumerWidget which has access to the WidgetRef. This was not only a fix but also a valuable lesson in Flutter state management.

Thank for your help.

GotJimmy commented 1 year ago

Glad it worked out for you!