GotJimmy / accordion

Other
46 stars 43 forks source link

when updating Acordion section content its closes the opened section #42

Closed DevAloshe closed 1 year ago

GotJimmy commented 1 year ago

There have been a few cases like that before and each one had the entire Accordion wrapped in some sort of state management, which in turn rebuilt (and reset) the entire Accordion and all its sections. Please check your code for that.

abhilashsajeev commented 1 year ago

When using the GetX libraries, you may encounter an issue when trying to use the Obx widget inside the children of an Accordion widget. This is because the Accordion widget expects its children to be of type AccordionSection, and the Obx widget is not of this type.

It would be helpful to have support for using the Obx widget as a child of the Accordion widget. This would avoid the problem of having to rebuild the entire UI whenever there is a change in the state of the Obx widget, which can be inefficient and slow.

In summary, support for using the Obx widget as a child of the Accordion widget in the GetX libraries would be a useful feature that can improve the performance and efficiency of the UI.

@GotJimmy

GotJimmy commented 1 year ago

Don't wrap the AccordionSection with Obx but the individual widgets inside the AccordionSection that actually need updating. That solves your problem and is actually a bit more efficient because only specific widgets get updated and not the entire AccordionSection.

penguisnt commented 1 year ago

I am seeing the same things (not using Obx) with a very simple Accordion + AccordionSection

I tried to dive into the library but could not track down the issue. A few things I observed:

Every time the state is refreshed, it builds new controllers:

[GETX] Instance "ListController" has been created
[GETX] Instance "ListController" has been initialized
[GETX] Instance "ListController" has been created with tag "447629554"
[GETX] Instance "ListController" with tag "447629554" has been initialized

New updated event: Timestamp(seconds=1679511183, nanoseconds=162000000)
Here again
[GETX] Instance "ListController" has been created with tag "769490052"
[GETX] Instance "ListController" with tag "769490052" has been initialized

New updated event: Timestamp(seconds=1679511506, nanoseconds=356000000)
Here again
[GETX] Instance "ListController" has been created with tag "3741858"
[GETX] Instance "ListController" with tag "3741858" has been initialized

A few things that are suspicious:

1) Each time the parent widget is rebuilt, Accordion seems to build 2 copies of all AccordionSections? The accordion unwraps the child and then re-wraps them? But unfortunately, the keys are not preserved..

For example, for a simple accordion, where data is just a list of simple strings (dates), and in this case there is only a single item,

return Accordion(
          key: const ValueKey("MainAccordion"),
          accordionId: "myaccordion",
          maxOpenSections: 1,
          children: data
              .map((e) => AccordionSection(
                    key: ValueKey(e),
                    accordionId: "myaccordionsection",
                    header: const Text("Header"),
                    content: const Text("Content"),
                  ))
              .toList(),
        );

So, I put breakpoints on the Accordion and AccordionSection constructors.

When the first build occurs, we see in this order: 1) We see an AccordionSection being created, from the callstack of my build method with the corresponding keys image 2) The Accordion gets created, from the callstack of my build method with the corresponding keys image 3) Then we see another AccordionSection being rendered with the same content, but different keys image


2) It's a little suspicious that the accordionId in the Accordion constructor is not used.. The constructor uses the hashCode to create the listController, then sets the internal accordionId to the hashCode..

 String? accordionId,
  }) : super(key: key) {
    final listCtrl = Get.put(ListController(), tag: hashCode.toString());

    [...]
    this.accordionId = hashCode.toString();

I'm not a Flutter or Dart expert by any means, so I could easily have overlooked something on (2).

Please take a look, and I hope it helps.

penguisnt commented 1 year ago

Additionally, it seems there are lots of references to different ListController classes, because sometimes Get.put(ListController() uses the accordionId tag, and sometimes the hashCode tag.

Additionally++, there are some places that use uniqueKey, and some places that use key, causing the state lookups to not be correct, and occasionally the 'openSections' lookup seems to be wrong.

By using an accordionId, and making three changes to Accordion:

      }) : super(key: key) {
    final listCtrl =
        Get.put(ListController(), tag: accordionId ?? hashCode.toString());

[...]

    this.accordionId = accordionId ?? hashCode.toString();

[...]

  @override
  build(context) {
    final listCtrl = Get.put(ListController(), tag: accordionId );

The container now remembers which sections were open, however, on rebuild, it plays the 'opening' animation again..

Unfortunately I don't have the expertise to dig deeper, hope it can help resolve this.

penguisnt commented 1 year ago

Last thing before I have to move on to something else: Seems like animation issue lies in the SectionController not being persisted, since the internal hidden AccordionSections are recreated on each build.. maybe need to hook it up to Get

abhilashsajeev commented 1 year ago

@GotJimmy I need to display an accordion based on a particular value. Is there any alternative way to achieve this? other wise I have to keep accordions with empty titles

GotJimmy commented 1 year ago

@abhilashsajeev Not sure what you mean by "particular value". Please specify.