Skip to content

[animations] OpenContainer should have a onOpen callback that is executed only once #74111

Description

@barangungor

I'm using openContainer in my app. I want call a function on OpenContainer's openBuilder page opened.

My Code Like this but the function runs 2-3 times onOpen and onClose. I want call only 1 time after page opened:

openBuilder: (BuildContext c,
                                                VoidCallback action) {
                                              _supRepo.getProductStore(context,
                                                  _userRepo.activePage['id'],
                                                  campaign: _userRepo
                                                              .campaignObject.values
                                                              .elementAt(
                                                                  campaignCatIndex)[
                                                          'campaigns']
                                                      [campaignIndex]['id']);
                                              return ProductPage(
                                                showAppBar: true,
                                              );
                                            },

Activity

  1. iapicca commented on Jan 16, 2021

    @iapicca

    @barangungor
    can you provide your flutter doctor -v and a complete minimal reproducible code sample (not a snippet from production)

  2. barangungor commented on Jan 16, 2021

    @barangungor
    Author

    flutter doctor -v output:

    flutter doctor -v
    [√] Flutter (Channel beta, 1.25.0-8.2.pre, on Microsoft Windows [Version 10.0.19042.746], locale tr-TR)
        • Flutter version 1.25.0-8.2.pre at C:\src\flutter
        • Framework revision b0a2299859 (11 days ago), 2021-01-05 12:34:13 -0800
        • Engine revision 92ae191c17
        • Dart version 2.12.0 (build 2.12.0-133.2.beta)
    
    [!] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
        • Android SDK at C:\Users\bbara\AppData\Local\Android\Sdk
        • Platform android-30, build-tools 30.0.2
        • ANDROID_SDK_ROOT = C:\Users\bbara\AppData\Local\Android\Sdk
        • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java      
        • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)
        X Android license status unknown.
          Run `flutter doctor --android-licenses` to accept the SDK licenses.
          See https://flutter.dev/docs/get-started/install/windows#android-setup for more details.
    
    [√] Android Studio (version 4.1.0)
        • Android Studio at C:\Program Files\Android\Android Studio
        • Flutter plugin can be installed from:
           https://plugins.jetbrains.com/plugin/9212-flutter
        • Dart plugin can be installed from:
           https://plugins.jetbrains.com/plugin/6351-dart
        • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)
    
    [!] Connected device
        ! No devices available
    
    ! Doctor found issues in 2 categories.

    And this is my OpenContainer Code :

    Code
    OpenContainer(                                           transitionDuration:
                                                    Duration(milliseconds: 300),
                                                closedBuilder: (BuildContext c,
                                                    VoidCallback action) {
                                                  return Container(
                                                      margin: EdgeInsets.only(
                                                        top: MediaQuery.of(context)
                                                                .size
                                                                .height /
                                                            150,
                                                      ),
                                                      child: Image.network(
                                                          IMAGE_ORIGINAL +
                                                              _userRepo
                                                                  .campaignObject
                                                                  .values
                                                                  .elementAt(campaignCatIndex)[
                                                                      'campaigns'][
                                                                      campaignIndex]
                                                                      ['image']
                                                                  .toString()));
                                                },
                                                openBuilder: (BuildContext c,
                                                    VoidCallback action) {
                                                  _supRepo.getProductStore(context,
                                                      _userRepo.activePage['id'],
                                                      campaign: _userRepo
                                                                  .campaignObject.values
                                                                  .elementAt(
                                                                      campaignCatIndex)[
                                                              'campaigns']
                                                          [campaignIndex]['id']);
                                                  return ProductPage(
                                                    showAppBar: true,
                                                  );
                                                },
                                                tappable: true,
                                              );
  3. iapicca commented on Jan 16, 2021

    @iapicca

    [...] and a complete minimal reproducible code sample (not a snippet from production)

    OpenContainer( transitionDuration: Duration(milliseconds: 300), closedBuilder: (BuildContext c, VoidCallback action) { return Container( margin: EdgeInsets.only( top: MediaQuery.of(context) .size .height / 150, ), child: Image.network( IMAGE_ORIGINAL + _userRepo .campaignObject .values .elementAt(campaignCatIndex)[ 'campaigns'][ campaignIndex] ['image'] .toString())); }, openBuilder: (BuildContext c, VoidCallback action) { _supRepo.getProductStore(context, _userRepo.activePage['id'], campaign: _userRepo .campaignObject.values .elementAt( campaignCatIndex)[ 'campaigns'] [campaignIndex]['id']); return ProductPage( showAppBar: true, ); }, tappable: true, );

    @barangungor
    this seems a snippet from production code rather than a minimal reproducible code sample
    in this link you can find a reference to what this definition implies

  4. barangungor commented on Jan 16, 2021

    @barangungor
    Author

    @iapicca
    I had to do this to explain my problem. I think it's pretty understandable. I want to run a function only once when it is opened. Instead of the code before "return" in the code block I provided, print ('TEST'); you can write and try. When opening my page, the function works 2 or 3 times and I don't want that to happen.

  5. pedromassangocode commented on Jan 18, 2021

    @pedromassangocode

    Considering this as a proposal based on the use-case. I suggest to add an onOpen callback (similar to onClosed) which should be called only once: when the container is being opened.

    Thank you

  6. changed the title [-]OpenContainer onOpened function?[/-] [+][animations] OpenContainer should have a onOpen callback that is executed only once[/+] on Jan 18, 2021
  7. barangungor commented on Jan 18, 2021

    @barangungor
    Author
  8. pedromassangocode commented on Jan 18, 2021

    @pedromassangocode
  9. The-Redhat commented on Feb 7, 2021

    @The-Redhat
    Contributor

    Hey @pedromassangocode can I create a pr for this?

  10. pedromassangocode commented on Feb 8, 2021

    @pedromassangocode

    Hey @pedromassangocode can I create a pr for this?

    Yes you can.

  11. 11 remaining items

  12. adrianbuqs commented on Jun 9, 2022

    @adrianbuqs

    In case someone still needs a solution. Here is my work around.

          return OpenContainer(
              tappable: false,
              transitionDuration: Duration(milliseconds: 700),
              closedBuilder: (context, openContainer) {
                return GestureDetector(
                  onTap: () {
                    // Your onOpen codes
                    openContainer(); //this function triggers and executes the openBuilder property
                  },
                  child: Container(
                    padding: const EdgeInsets.all(10),
                    color: Colors.blue,
                    child: Text('Open Container'),
                  ),
                );
              },
              openBuilder: (context, closeContainer) {
                return Container(
                  color: Colors.red,
                );
              },
            );
    
  13. SniffyMcTasty commented on Aug 11, 2022

    @SniffyMcTasty

    This feature really is a must have. +1 from me and I'll be sure to follow whatever news in this thread.

  14. mirkancal commented on Nov 30, 2022

    @mirkancal

    I also need this, openBuilder returns a page but before the return, I'm logging with firebase analytics. That logging call gets triggered 5-10 times on every opening.

  15. The-Redhat commented on Dec 3, 2022

    @The-Redhat
    Contributor

    You can use the workaround proposed in the comment above: #74111 (comment)

  16. kinex commented on Feb 4, 2023

    @kinex

    I have been struggling with poor animation performance and also other strange behavior in my app until I noticed that the openBuilder gets called multiple times. All the issues I had were caused by this. The widget I build in openBuilder is a very complex page so it definitely must not be created multiple times. This was quite unexpected behavior and unfortunately the suggested workaround does not help in my case for some reason. The openBuilder gets still called twice.

    So I implemented another workaround which is to save the widget created in openBuilder and in subsequent calls return the saved widget. One challenge is that where to reset the saved instance (you can't do it in closeBuilder as it also gets called "randomly" multiple times). Here is a simplified sample of my solution if it helps anyone:

    class AddEventFloatingActionButton extends StatefulWidget {
      const AddEventFloatingActionButton({super.key});
    
      @override
      State<AddEventFloatingActionButton> createState() =>
          _AddEventFloatingActionButtonState();
    }
    
    class _AddEventFloatingActionButtonState
        extends State<AddEventFloatingActionButton> {
    
      Widget? _openBuilderWidget;
    
      final _isMenuOpen = ValueNotifier<bool>(false);
      @override
      Widget build(BuildContext context) {
        return SpeedDial(
          onOpen: () => _openBuilderWidget = null,
          isOpenOnStart: _isMenuOpen.value,
          openCloseDial: _isMenuOpen,
          children: <AddEventCommand>[
            AddEventCommand(EventTypes.a),
            ...
          ].map(
            (c) {
              return SpeedDialChild(
                child: OpenContainer(
                  openBuilder: (BuildContext context, VoidCallback _) {
                    scheduleMicrotask(() {
                      _isMenuOpen.value = false;
                    });
    
                    if (_openBuilderWidget != null) {
                      return _openBuilderWidget!;
                    }
    
                    _openBuilderWidget = EditEventPage(newEventType: c.eventType);
    
                    return _openBuilderWidget!;
                  },
                  closedBuilder: (context, openContainer) {
                    return Container(...);
                  },
                ),
              );
            },
          ).toList(),
        );
      }
    }
    
  17. added
    team-designOwned by Design Languages team
    triaged-designTriaged by Design Languages team
    and removed on Aug 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Issues that are less important to the Flutter projectc: proposalA detailed proposal for a change to Flutterp: animationsThe animations packagepackageflutter/packages repository. See also p: labels.team-designOwned by Design Languages teamtriaged-designTriaged by Design Languages team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions