flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
165.75k stars 27.38k forks source link

`BackdropFilter` does not work when compiling with --wasm #152026

Open dxvid-pts opened 3 months ago

dxvid-pts commented 3 months ago

Steps to reproduce

Use the provided AppBar. When targetting CanvasKit everything works as expected. When compiling using --wasm blur is not applied.

Expected results (CanvasKit)

Screenshot 2024-07-19 at 14 29 19

Actual results (WASM + skwasm)

Screenshot 2024-07-19 at 14 28 39

Code sample

Code sample ```dart AppBar( forceMaterialTransparency: true, flexibleSpace: ClipRect( child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), ), ), ); ```

Screenshots or Video

Screenshots / Video demonstration [Upload media here]

Logs

Logs ```console [Paste your logs here] ```

Flutter Doctor output

Doctor output ```console [✓] Flutter (Channel master, 3.24.0-1.0.pre.197, on macOS 14.5 23F79 darwin-arm64, locale en-DE) [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) [✓] Xcode - develop for iOS and macOS (Xcode 15.4) [✓] Chrome - develop for the web [✓] Android Studio (version 2023.3) [✓] Android Studio (version 2021.3) [✓] Android Studio (version 2022.2) [✓] VS Code (version 1.91.1) [✓] Connected device (3 available) [✓] Network resources • No issues found! ```
huycozy commented 3 months ago

Do you use extendBodyBehindAppBar on Scaffold? Can you share a complete sample code in a main.dart file so that we can run and confirm this issue?

dxvid-pts commented 3 months ago

Here is a full code sample:

import 'dart:ui';

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      home: const MyHomePage(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBodyBehindAppBar: true,
      appBar: AppBar(
        forceMaterialTransparency: true,
        flexibleSpace: ClipRect(
          child: BackdropFilter(
            filter: ImageFilter.blur(sigmaX: 30, sigmaY: 30),
            child: AnimatedContainer(
            decoration: BoxDecoration(
              border: Border(
                bottom: BorderSide(color: Colors.black.withOpacity(0.09)),
              ),
              color: Colors.white10,
            ),
            duration: const Duration(milliseconds: 300),
          ),
          ),
        ),
        title: Text("Title"),
      ),
      body: SizedBox(
        height: double.infinity,
        width: double.infinity,
        child: Image.network(
            "https://fastly.picsum.photos/id/428/700/700.jpg?hmac=uGO0h-whSs6ScNzRZ4co4JfKP6otpf16kDrZ0qtEer8",
         fit: BoxFit.cover,
        ),
      ),
    );
  }
}
darshankawar commented 3 months ago

Thanks for the update. It is replicable as reported on latest master version.

master flutter doctor -v ``` [!] Flutter (Channel master, 3.24.0-1.0.pre.197, on macOS 12.2.1 21D62 darwin-x64, locale en-GB) • Flutter version 3.24.0-1.0.pre.197 on channel master at /Users/dhs/documents/fluttersdk/flutter ! Warning: `flutter` on your path resolves to /Users/dhs/Documents/Fluttersdk/flutter/bin/flutter, which is not inside your current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter. Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path. ! Warning: `dart` on your path resolves to /Users/dhs/Documents/Fluttersdk/flutter/bin/dart, which is not inside your current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter. Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path. • Upstream repository https://github.com/flutter/flutter.git • Framework revision 0bac2be379 (76 minutes ago), 2024-07-19 00:15:26 -0400 • Engine revision ea1e53a4e8 • Dart version 3.6.0 (build 3.6.0-55.0.dev) • DevTools version 2.37.1 • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades. [!] Android toolchain - develop for Android devices (Android SDK version 30.0.3) • Android SDK at /Users/dhs/Library/Android/sdk ✗ cmdline-tools component is missing Run `path/to/sdkmanager --install "cmdline-tools;latest"` See https://developer.android.com/studio/command-line for more details. ✗ Android license status unknown. Run `flutter doctor --android-licenses` to accept the SDK licenses. See https://flutter.dev/docs/get-started/install/macos#android-setup for more details. [✓] Xcode - develop for iOS and macOS (Xcode 13.2.1) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 13C100 • CocoaPods version 1.11.2 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] IntelliJ IDEA Ultimate Edition (version 2021.3.2) • IntelliJ at /Applications/IntelliJ IDEA.app • Flutter plugin version 65.1.4 • Dart plugin version 213.7228 [✓] VS Code (version 1.62.0) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.29.0 [✓] Connected device (3 available) • Darshan's iphone (mobile) • 21150b119064aecc249dfcfe05e259197461ce23 • ios • iOS 15.3.1 19D52 • macOS (desktop) • macos • darwin-x64 • macOS 12.2.1 21D62 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 109.0.5414.119 [✓] Network resources • All expected network resources are available. ! Doctor found issues in 1 category. [!] Xcode - develop for iOS and macOS (Xcode 12.3) • Xcode at /Applications/Xcode.app/Contents/Developer ! Flutter recommends a minimum Xcode version of 13. Download the latest version or update via the Mac App Store. • CocoaPods version 1.11.2 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] VS Code (version 1.62.0) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.21.0 [✓] Connected device (5 available) • SM G975F (mobile) • RZ8M802WY0X • android-arm64 • Android 11 (API 30) • Darshan's iphone (mobile) • 21150b119064aecc249dfcfe05e259197461ce23 • ios • iOS 14.4.1 18D61 • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729 • ios • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator) • macOS (desktop) • macos • darwin-x64 • Mac OS X 10.15.4 19E2269 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 98.0.4758.80 [✓] HTTP Host Availability • All required HTTP hosts are available ! Doctor found issues in 1 category. ```
davidhicks980 commented 2 months ago

Also ran into this issue. @dxvid-pts I think your example is actually working on my Chrome, but I'm on 3.24.0-1.0.pre.542. That said, here's another test case that demonstrates:

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return _FilterTest(Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        DecoratedBox(
          decoration: BoxDecoration(
            border: Border.all(
                color: Colors.red,
                width: 2,
                strokeAlign: BorderSide.strokeAlignOutside),
          ),
          child: ClipRect(
            child: BackdropFilter(
              filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
              // ignore: sized_box_for_whitespace
              child: Container(
                height: 200,
                width: 200,
              ),
            ),
          ),
        ),
        const SizedBox(width: 8),
        // Using a transparent color works
        DecoratedBox(
          decoration: BoxDecoration(
            border: Border.all(
              color: Colors.blue,
              width: 2,
              strokeAlign: BorderSide.strokeAlignOutside,
            ),
          ),
          child: ClipRect(
            child: BackdropFilter(
              filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
              child: Container(
                color: Colors.transparent,
                height: 200,
                width: 200,
              ),
            ),
          ),
        ),
      ],
    ));
  }
}

// This is just a colorful backdrop
class _FilterTest extends StatelessWidget {
  const _FilterTest(Widget child, {this.brightness = Brightness.light})
      : _child = child;
  final Brightness brightness;
  final Widget _child;

  @override
  Widget build(BuildContext context) {
    final Size size = MediaQuery.sizeOf(context);
    final double tileHeight = size.height / 4;
    final double tileWidth = size.width / 8;
    return CupertinoApp(
      home: Stack(fit: StackFit.expand, children: <Widget>[
        // 512 color tiles
        // 4 alpha levels (0.416, 0.25, 0.5, 0.75)
        for (int a = 0; a < 4; a++)
          for (int h = 0; h < 8; h++) // 8 hues
            for (int s = 0; s < 4; s++) // 4 saturation levels
              for (int b = 0; b < 4; b++) // 4 brightness levels
                Positioned(
                  left: h * tileWidth + b * tileWidth / 4,
                  top: a * tileHeight + s * tileHeight / 4,
                  height: tileHeight,
                  width: tileWidth,
                  child: ColoredBox(
                    color: HSVColor.fromAHSV(
                      0.5 + a / 8,
                      h * 45,
                      0.5 + s / 8,
                      0.5 + b / 8,
                    ).toColor(),
                  ),
                ),
        Padding(
          padding: const EdgeInsets.all(32),
          child: CupertinoTheme(
            data: CupertinoThemeData(brightness: brightness),
            child: _child,
          ),
        ),
      ]),
    );
  }
}

This only occurs when the BackdropFilter does not have a child that is painted. I'm not sure why this is.

eyebrowsoffire commented 2 months ago

I did some investigation on this, and it turns out there are two things causing this: 1) If the saveLayer operation has no pictures within it, we skip it entirely: https://github.com/flutter/engine/blob/4b7306dc7fd42463787b508dc23a14b17f13c53d/lib/web_ui/lib/src/engine/layers.dart#L774 We shouldn't do that, the operation should be applied anyway. 2) There is a skia issue where if the actual drawn contents of the layer that go on top of the backdrop filter are clipped out of the region of the canvas, the backdrop filter does not apply. I filed https://g-issues.skia.org/issues/362552959. Essentially the reason this happens in Skwasm and not CanvasKit is because Skwasm uses a SkBBHFactory to let skia accurately calculate the bounds of the picture. In this case the picture is "empty" in that there is nothing drawn over the backdrop, but we still want the backdrop filter to apply. There are more nitty gritty details and minimal examples in the linked skia issue.

eyebrowsoffire commented 2 months ago

I fixed (1) here but the issue is still occurring until (2) is fixed (the skia issue).

dkwingsmt commented 1 month ago

Related: https://github.com/flutter/flutter/issues/154698 , which should be fixable once this issue is addressed.