Skip to content

[Desktop] Allow applications to continue frame rendering when hidden for real-time games and animation-driven audio #174356

Closed

Description

@tguerin

Use case

We are building a real-time multiplayer game that uses Rive animations with audio event synchronization. When players perform actions remotely, the game continues running in the background, and animations must maintain their timeline to ensure audio plays at the correct positions. However, when the app window is hidden (e.g., minimized, covered by other windows, or in mission control on macOS), frame rendering stops, causing animations to freeze and audio to not play.

Proposal

Proposed Solution

Add a forceFramesEnabled property to SchedulerBinding that allows developers to explicitly opt-in to continuous frame rendering when the app is hidden. This would be particularly useful for:

  • Real-time games that continue running in the background
  • Applications with animation-driven audio (like Rive)
  • Multiplayer applications where game logic continues server-side

I have already implemented this feature in PR #173862.

Current Workaround

We've implemented a hacky solution by intercepting the AppLifecycleState.hidden state and replacing it with AppLifecycleState.inactive in a custom DesktopWidgetsFlutterBinding:

@override
void handleAppLifecycleStateChanged(AppLifecycleState state) {
  if (state == AppLifecycleState.hidden) {
    _isHidden = true;
    super.handleAppLifecycleStateChanged(AppLifecycleState.inactive);
  } else {
    if (state == AppLifecycleState.resumed) {
      _isHidden = false;
    }
    super.handleAppLifecycleStateChanged(state);
  }
}

Activity

  1. added
    in triagePresently being triaged by the triage team
    c: new featureNothing broken; request for a new capability
    frameworkflutter/packages/flutter repository. See also f: labels.
    c: proposalA detailed proposal for a change to Flutter
    and removed
    in triagePresently being triaged by the triage team
    on Aug 25, 2025
  2. tirth-patel-nc commented on Aug 25, 2025

    @tirth-patel-nc
    Member

    @tguerin thanks for the report. Marking it as a new proposal.

  3. dkwingsmt commented on Aug 25, 2025

    @dkwingsmt
    Contributor
  4. dkwingsmt commented on Aug 25, 2025

    @dkwingsmt
    Contributor

    Thank you for the writeup!

    I've encountered some games that freezes when the window goes inactive or minimized, but yeah there are some that continues to play (maybe with reduced frames).

    If we move forward with this flag, I propose a negated name for the property such as pauseFramesWhenHidden, which seems clearer to me.

    Let's wait for a while for more feedbacks.

  5. added
    P2Important issues not at the top of the work list
    workaround availableThere is a workaround available to overcome the issue
    on Aug 26, 2025
  6. justinmc commented on Aug 26, 2025

    @justinmc
    Contributor

    I talked to @chinmaygarde and he says that the engine doesn't care and will happily continue rendering frames if we tell it to.

    So is eliminating the need for the workaround worth the danger of people turning on this flag when they shouldn't?

    @tguerin Are you sure you must continue rendering frames in order to play your audio at the right time? Can you explain that in more detail and maybe provide an example if possible?

  7. tguerin commented on Sep 8, 2025

    @tguerin
    ContributorAuthor

    Here is a sample project: https://github.com/tguerin/rive_sound_sample

    (you may need to run dart run rive_native:setup --clean --platform macos for it to work). I only added macOS, but it works on Windows as well.

    When the app is hidden, you no longer hear the robot’s sound because in Rive the audio is tied directly to the animation timeline. In a real-time game, remote actions don’t stop just because the window is hidden, so the audio must continue to play in sync.

    I understand this is a specific use case and mainly relevant on desktop. We could restrict the flag to desktop platforms only. I’m open to any suggestions or adaptations for the PR, but the current workaround is hacky and not a real fix.

  8. dkwingsmt commented on Sep 8, 2025

    @dkwingsmt
    Contributor

    I think at least, the logic for Flutter to disable frames when hidden is something "extra" and "smart", therefore it makes perfect sense to allow developers to disable them as long as we document clearly and the developers know what they're doing. My concern is mostly if there are anything better for your project, such as allowing the logic to run without actually rendering them, but I guess you'd be happy to at least have this and we should grant it. @justinmc What do you think?

  9. zeyus commented on Sep 12, 2025

    @zeyus

    I am going to add my use case to this: I am using flutter (and flame) for a research project that uses real-time communication and data streams. I often end up with things breaking because while the isolates continue consuming in the background, the main UI stops, and it can get into an inconsistent state (including a countdown timer) which causes all kinds of problems.

    It would be awesome to be able to have an option, at least for desktop, to continue rendering when an application loses focus. I understand that is not always desirable, but in some cases it very much is.

  10. loic-sharma commented on Sep 13, 2025

    @loic-sharma
    Member

    I believe forcing frames globally should already be possible today using SchedulerBinding.scheduleForcedFrame. Something like:

    SchedulerBinding.instance.addPersistentFrameCallback((_) {
      SchedulerBinding.instance.scheduleForcedFrame();
    });

    This seems better than a global flag like SchedulerBinding.forceFramesEnabled.


    However, it would be nice if Flutter's animation system let you force frames using granular app logic. Today, the TickerMode widget lets you configure tickers in the subtree. You can use this to mute animations, for example if the animation is running in a tab that isn't active:

    Widget build(BuildContext context) {
      return TickerMode(
        enabled: false, // Tickers in the subtree are muted
        child: ...,
      );
    }

    I would consider:

    1. Add a new Ticker.forceFrames API. If true, this causes Ticker.scheduleTick to call SchedulerBinding.scheduleForcedFrame instead of SchedulerBinding.scheduleFrame.

    2. Add a new TickerMode.forceFrames property. Something like:

      Widget build(BuildContext context) {
        // Tickers in the subtree, if any, are enabled and force frames if active.
        return TickerMode(
          enabled: true,
          forceFrames: true,
          child: ...,
        );
      }

      This would also likely require deprecating TickerMode.of and TickerMode.getNotifier in favor of new APIs that can return both enabled and forceFrames.

    3. Add a new TickerMode.merged constructor. This creates a TickerMode that overrides ticker mode properties, similar to DefaultTextStyle.merge:

      Widget build(BuildContext context) {
        // Tickers in the subtree, if any are active, force frames.
        // Tickers can be muted by a TickerMode higher in the widget tree.
        return TickerMode.merge(
          forceFrames: true,
          child: ...,
        );
      }
    4. Update SingleTickerProviderStateMixin and TickerProviderStateMixin here and here.

  11. github-actions commented on Nov 26, 2025

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  12. locked as resolved and limited conversation to collaborators on Nov 26, 2025
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

    P2Important issues not at the top of the work lista: animationAnimation APIsa: desktopRunning on desktopc: new featureNothing broken; request for a new capabilityc: proposalA detailed proposal for a change to Flutterframeworkflutter/packages/flutter repository. See also f: labels.r: fixedIssue is closed as already fixed in a newer versionteam-frameworkOwned by Framework teamtriaged-frameworkTriaged by Framework teamworkaround availableThere is a workaround available to overcome the issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions