Skip to content

Conversation

@jonahwilliams
Copy link
Contributor

@jonahwilliams jonahwilliams commented Oct 21, 2024

The backdrop key functionality allows multiple backdrop filters to share the same input filter, dramatically improving raster performance. This is only supported on the Impeller backend.

The backdrop key class allocates a new int from a static and passes this to the engine layer. with 64 bit integers, we can allocate many backdrop filter ids per frame and never run out.

See also: #156455

import 'dart:math';
import 'dart:ui';

import 'package:flutter/material.dart';

final _random = Random();

void main() => runApp(const BackdropFilterDemo());

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

  static final listKey = BackdropKey();
  static final overlayKey = BackdropKey();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.white,
        body: Stack(
          children: [
            ListView.builder(
              itemCount: 120, // 60 pairs of red and blue containers
              itemBuilder: (context, index) {
                return Container(
                  height: 100,
                  color: index % 2 == 0 ? Colors.red : Colors.blue,
                );
              },
            ),
            Center(
              child: Container(
                width: 400,
                height: 400,
                decoration: BoxDecoration(
                  border: Border.all(color: Colors.black),
                ),
                child: Image.network('https://picsum.photos/400'),
              ),
            ),
            ListView.separated(
              separatorBuilder: (_, __) => const SizedBox(height: 8),
              itemBuilder: (context, index) => BlurEffect(
                backdropKey: listKey,
                child: SizedBox(
                  height: 50,
                  child: Center(
                    child: Text(index.toString(),
                        style: const TextStyle(color: Colors.white)),
                  ),
                ),
              ),
              itemCount: 200,
            ),
            Positioned.fill(
              bottom: null,
              child: BlurEffect(
                backdropKey: overlayKey,
                child: Padding(
                  padding: EdgeInsets.only(
                    top: MediaQuery.of(context).viewPadding.top,
                  ),
                  child: const SizedBox(height: 45),
                ),
              ),
            ),
            Positioned.fill(
              top: null,
              child: BlurEffect(
                backdropKey: overlayKey,
                child: Padding(
                  padding: EdgeInsets.only(
                    top: MediaQuery.of(context).viewPadding.bottom,
                  ),
                  child: const SizedBox(height: 50),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class BlurEffect extends StatelessWidget {
  final Widget child;

  const BlurEffect({
    required this.child,
    required this.backdropKey,
    super.key,
  });

  final BackdropKey backdropKey;

  @override
  Widget build(BuildContext context) {
    return ClipRect(
      child: BackdropFilter(
        backdropKey: backdropKey,
        filter: ImageFilter.blur(
          sigmaX: 40,
          sigmaY: 40,
          // tileMode: TileMode.mirror,
        ),
        child: DecoratedBox(
          decoration: BoxDecoration(color: Colors.black.withOpacity(.65)),
          child: child,
        ),
      ),
    );
  }
}

Skia

Impeller

@github-actions github-actions bot added the framework flutter/packages/flutter repository. See also f: labels. label Oct 21, 2024
@jonahwilliams jonahwilliams changed the title Support backdrop id in flutter framework. Support backdrop key in flutter framework. Oct 21, 2024
@jonahwilliams jonahwilliams marked this pull request as ready for review October 21, 2024 19:04
@gaaclarke
Copy link
Member

I thought we were going to make this scoped with a parent widget instead of having an explicit key?

@jonahwilliams
Copy link
Contributor Author

There will still be a key type. Scoping would be done with an inherited widget, which exists above the render object/layer types.

Do you want that in this PR?

@gaaclarke
Copy link
Member

There will still be a key type. Scoping would be done with an inherited widget, which exists above the render object/layer types.

Do you want that in this PR?

Ah okay. I think it would be nice to have the full picture defined somewhere before signing off on the details. It can be a separate pr based on this one or a small design doc.

@jonahwilliams
Copy link
Contributor Author

The design is fairly simple. its "just" an inherited widget and a special constructor.

// new inherited widget type, introduces scoped backdrop. internally caches
// backdrop id.
class BackdropProvider { ... }

// new backdrop filter contructor. internally grabs backdrop id.
BackdropFilter(..., parent:  BackdropProvider.of(context))

@gaaclarke
Copy link
Member

Why do we need to specify the parent explicitly? That information exists in the tree, can't we just crawl up to the widget tree to get the closest ancestor of that type?

@jonahwilliams
Copy link
Contributor Author

If you want to hide the lookup that could be deferred to the body itself. What would you call this new backdrop filter constructor? I'm intentionally trying not to change the behavior of the existing filter. You don't want to opt in to using backdrop ids accidentally because it can be very breaking with overlaps.

BackdropFilter.fromParent(filter: ..., child: ...); // grabs BackdropProvider.of(context)

@gaaclarke
Copy link
Member

I mentioned it here: flutter/engine#55701 (comment)

I'll fill it out more to show what I was thinking:

Widget build(BuildContext context) {
  return BackdropGroup(
    children:[
      ClipRect(
        child: BackdropFilter(
          filter: ui.ImageFilter.blur(
            sigmaX: 5.0,
            sigmaY: 5.0,
          ),
          child: Container(
            alignment: Alignment.center,
            width: 200.0,
            height: 200.0,
            child: const Text('Hello World'),
          ),
        )
      ),
      ClipRect(
        child: BackdropFilter(
          filter: ui.ImageFilter.blur(
            sigmaX: 5.0,
            sigmaY: 5.0,
          ),
          child: Container(
            alignment: Alignment.center,
            width: 200.0,
            height: 200.0,
            child: const Text('Hello World'),
          ),
        )
      ),
    ]
  );
}

There is no explicit key, there is no new constructor for the backdrop filters, there is no breakage since code without BackdropGroup will behave as it always has, and there is no duplication of information that can be inferred contextually.

@jonahwilliams
Copy link
Contributor Author

That design doesn't allow you to nest backdrop filters that don't use the shared BackdropGroup. Opting in to sharing must be explicit otherwise you limit the usefullness of BackdropGroup

@jonahwilliams
Copy link
Contributor Author

jonahwilliams commented Oct 21, 2024

This is important for widgets that incidentally use backdrop filters. For example, imagine a button that used a backdrop filter in an interna saveLayer layer, but nestled in the app I show above. You don't want it to accidentally start reading from the same backdrop as the list items.

@gaaclarke
Copy link
Member

That design doesn't allow you to nest backdrop filters that don't use the shared BackdropGroup. Opting in to sharing must be explicit otherwise you limit the usefullness of BackdropGroup

I would say opting out of a group which you are defined in is an exceptional case. The design of the API should be the smoothest/least amount of typing for the most common case.

This is important for widgets that incidentally use backdrop filters. For example, imagine a button that used a backdrop filter in an interna saveLayer layer, but nestled in the app I show above. You don't want it to accidentally start reading from the same backdrop as the list items.

What have our other APIs done? I believe the scenario you're laying out. I'm skeptical that it's that likely or problematic to justify making the API more complicated.

@gaaclarke
Copy link
Member

Here's the compromise that leaves us in a better place. Not specifying the group means that it takes the closest ancestor group by default, you can also specify it explicitly.

@jonahwilliams
Copy link
Contributor Author

That isn't a reasonable compromise, as its going to make an opt in performance improvement into an opt out breaking change.

@jonahwilliams
Copy link
Contributor Author

I'ved added the BackdropGroup, the BackdropFilter.shared constructor, and updated the sample code to use the backdrop group instead.

@gaaclarke
Copy link
Member

That isn't a reasonable compromise, as its going to make an opt in performance improvement into an opt out breaking change.

It's not a breaking change because the behavior won't change unless a BackdropGroup is added to the widget tree. Are you thinking there would be one group for the whole tree if one isn't supplied above? We should keep the old behavior that they are all independent.

The scenario you are describing would play out like this:
Aaron: Oh man, my client isn't happy because the performance on this scroll view. Time to start profiling...
Narrator: 1 day later...
Aaron: Hmm, looks like it's related to blur performance. I remember hearing about BackdropFilterGroups, that might help. I'll throw everything into one backdrop filter. That's got to be the best peformance.
Narrator: 15 minutes...tick tock
Aaron: Hmm well that is much faster, but why does that one blur look odd. The docstring for BackdropFilterGroups warned that there can be interference between backdrop filters. I'll try opting him out of the group like it said.
Aaron: Bingo!

@gaaclarke
Copy link
Member

Or better yet, there will be a whole class of problem scenarios where they could be solved by just breaking up the BackdropFilterGroup, no need to explicitly opt out.

@jonahwilliams
Copy link
Contributor Author

lets GVC about it, easier to explain.

@Piinks Piinks requested a review from dkwingsmt October 22, 2024 22:17
Copy link
Member

@gaaclarke gaaclarke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. I still think that having the groups be defined solely by the parent widget is slightly better since it gives the option to opt-in 3rd party code into the BackdropGroup. It does however require a negation scope to cover all cases. It is safer to opt-out by default versus opt-in so this tradeoff makes sense plus negation requires knowing exactly what 3rd party code is doing in all cases which is probably not ideal. So, considering all that LGTM.

/// When multiple backdrop filters share the same key, the Flutter engine can
/// more efficiently perform the backdrop operations.
///
/// For more information, see `BackdropFilter`.
Copy link
Member

@gaaclarke gaaclarke Oct 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This says "done" but it wasn't changed yet.

Copy link
Contributor

@dkwingsmt dkwingsmt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@jonahwilliams jonahwilliams added the autosubmit Merge PR when tree becomes green via auto submit App label Oct 25, 2024
@auto-submit auto-submit bot merged commit c4c5452 into flutter:master Oct 25, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Oct 25, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Oct 25, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Oct 25, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Oct 26, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Oct 26, 2024
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Oct 26, 2024
Roll Flutter from 4faa4a4 to 5a11904 (67 revisions)

flutter/flutter@4faa4a4...5a11904

2024-10-26 30870216+gaaclarke@users.noreply.github.com Relands "Wide gamut framework gradient test (#153976)" (flutter/flutter#157643)
2024-10-26 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7c5c5fe5c84d to c9b8ac96f6ce (3 revisions) (flutter/flutter#157662)
2024-10-26 32538273+ValentinVignal@users.noreply.github.com Add test for `navigator_state.restorable_push_and_remove_until.0.dart` (flutter/flutter#157595)
2024-10-26 matanlurey@users.noreply.github.com Tighten up `throwToolExit`, explain when to use it. (flutter/flutter#157561)
2024-10-26 matanlurey@users.noreply.github.com Remove extraneous `throw`. (flutter/flutter#157658)
2024-10-26 32538273+ValentinVignal@users.noreply.github.com Add tests for `navigator.restorable_push.0.dart` (flutter/flutter#157492)
2024-10-25 engine-flutter-autoroll@skia.org Roll Flutter Engine from 43e4d9a30666 to 7c5c5fe5c84d (1 revision) (flutter/flutter#157644)
2024-10-25 engine-flutter-autoroll@skia.org Roll Flutter Engine from 5061358e255f to 43e4d9a30666 (1 revision) (flutter/flutter#157637)
2024-10-25 engine-flutter-autoroll@skia.org Roll Flutter Engine from eb867e055790 to 5061358e255f (2 revisions) (flutter/flutter#157623)
2024-10-25 polinach@google.com Create flutter specific leak troubleshooting guidance. (flutter/flutter#157396)
2024-10-25 katelovett@google.com Update CupertinoNavigationBar to support large layout (flutter/flutter#157133)
2024-10-25 engine-flutter-autoroll@skia.org Roll Flutter Engine from 38e9be1f74fa to eb867e055790 (3 revisions) (flutter/flutter#157613)
2024-10-25 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Wide gamut framework gradient test (#153976)" (flutter/flutter#157615)
2024-10-25 30870216+gaaclarke@users.noreply.github.com Wide gamut framework gradient test (flutter/flutter#153976)
2024-10-25 engine-flutter-autoroll@skia.org Roll Flutter Engine from b413d9996c86 to 38e9be1f74fa (2 revisions) (flutter/flutter#157604)
2024-10-25 engine-flutter-autoroll@skia.org Roll Packages from a556f0f to e0c4f55 (2 revisions) (flutter/flutter#157605)
2024-10-25 jonahwilliams@google.com Support backdrop key in flutter framework. (flutter/flutter#157278)
2024-10-25 reidbaker@google.com Add 3.24.4 changelog to master (flutter/flutter#157600)
2024-10-25 mohellebiabdessalem@gmail.com Update flutter.groovy to catch unknown task exception when finding api task (flutter/flutter#157282)
2024-10-25 engine-flutter-autoroll@skia.org Roll Flutter Engine from c4b0184c8783 to b413d9996c86 (1 revision) (flutter/flutter#157580)
2024-10-25 engine-flutter-autoroll@skia.org Roll Flutter Engine from b1c2ba8c4d52 to c4b0184c8783 (1 revision) (flutter/flutter#157578)
2024-10-25 32538273+ValentinVignal@users.noreply.github.com Add test for `build_owner.0.dart` (flutter/flutter#157499)
2024-10-25 32538273+ValentinVignal@users.noreply.github.com Add tests  for `focusable_action_detector.0.dart` (flutter/flutter#157575)
2024-10-25 32538273+ValentinVignal@users.noreply.github.com Add test for `navigator.restorable_push_and_remove_until.0.dart` (flutter/flutter#157487)
2024-10-25 engine-flutter-autoroll@skia.org Roll Flutter Engine from 29440ed1e225 to b1c2ba8c4d52 (1 revision) (flutter/flutter#157572)
2024-10-25 engine-flutter-autoroll@skia.org Roll Flutter Engine from 88716d804aef to 29440ed1e225 (1 revision) (flutter/flutter#157569)
2024-10-25 engine-flutter-autoroll@skia.org Roll Flutter Engine from b8b28c80a737 to 88716d804aef (2 revisions) (flutter/flutter#157567)
2024-10-24 engine-flutter-autoroll@skia.org Roll Flutter Engine from 48ff670d256b to b8b28c80a737 (2 revisions) (flutter/flutter#157564)
2024-10-24 matanlurey@users.noreply.github.com Use discenrable characters (replace `' � � '` in error logs) (flutter/flutter#157548)
2024-10-24 matanlurey@users.noreply.github.com Remove unused `PubDependenciesProjectValidator`. (flutter/flutter#157516)
2024-10-24 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Upgrade tests to AGP 8.7/Gradle 8.10.2/Kotlin 1.8.10 (#157032)" (flutter/flutter#157559)
2024-10-24 jonahwilliams@google.com Mark mac impeller as bringup. (flutter/flutter#157551)
2024-10-24 tessertaha@gmail.com Deprecate `ThemeData.dialogBackgroundColor` in favor of `DialogThemeData.backgroundColor` (flutter/flutter#155072)
2024-10-24 34871572+gmackall@users.noreply.github.com Upgrade tests to AGP 8.7/Gradle 8.10.2/Kotlin 1.8.10 (flutter/flutter#157032)
2024-10-24 engine-flutter-autoroll@skia.org Roll Flutter Engine from 246344f26edc to 48ff670d256b (2 revisions) (flutter/flutter#157544)
2024-10-24 matanlurey@users.noreply.github.com Allow opting out of `.flutter-plugins`, opt-out in `refreshPluginsList`. (flutter/flutter#157527)
2024-10-24 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Reverts "Added a warning if `flutter.groovy` uses a `.flutter-plugins` file. (#157388)" (#157541)" (flutter/flutter#157549)
2024-10-24 50643541+Mairramer@users.noreply.github.com Changes the offset computation to first item for RenderSliverMainAxisGroup (flutter/flutter#154688)
2024-10-24 engine-flutter-autoroll@skia.org Roll Packages from 5e03bb1 to a556f0f (7 revisions) (flutter/flutter#157539)
2024-10-24 737941+loic-sharma@users.noreply.github.com Add partial test for flutter build ios-framework on non-module (flutter/flutter#157482)
2024-10-24 737941+loic-sharma@users.noreply.github.com Add example to SafeArea docs (flutter/flutter#157228)
2024-10-24 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Added a warning if `flutter.groovy` uses a `.flutter-plugins` file. (#157388)" (flutter/flutter#157541)
2024-10-24 matanlurey@users.noreply.github.com Added a warning if `flutter.groovy` uses a `.flutter-plugins` file. (flutter/flutter#157388)
2024-10-24 engine-flutter-autoroll@skia.org Roll Flutter Engine from be56084344d1 to 246344f26edc (2 revisions) (flutter/flutter#157504)
2024-10-24 104349824+huycozy@users.noreply.github.com Add ability to disable CupertinoSegmentedControl (flutter/flutter#152813)
2024-10-24 tessertaha@gmail.com Update `Tab.height` parameter doc for tab height lower than default (flutter/flutter#157443)
...
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 12, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 13, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 13, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 6, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autosubmit Merge PR when tree becomes green via auto submit App framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants