Flutter:如何将 BackdropFilter 添加到 SliverAppBar

Flutter: How to add BackdropFilter to SliverAppBar

我想添加一个 BackdropFilter() 到一个 SliverAppbar()

我希望它看起来像 iOS 应用程序库应用栏:https://cln.sh/eP8wfY

Header sliver not floating over the list in a NestedScrollView 这样做,但只针对 header,我希望 titleactions 在背景模糊时可见。

谢谢!

编辑

页面的外观:https://cln.sh/vcCY4j

Github 我的代码要点:https://gist.github.com/HadyMash/21e7bd2f7e202de02837505e1c7363e9.

注意:即使在花费数小时之后,也会在颜色上遇到困难。

  • 你需要改变颜色
  • 你们中的一些人发现一些区域问题可能是因为 safeArea 或 CupertinoNavBar。
  • 你可以 remove/change 给阴影上色,我给的太多是为了测试目的。
  • 所有你必须玩的颜色和 LinearGradient

OutPut


这是我的概念:

Stack
    - backgroundImage
    - Container with white.3
         - CustomScrollView
              - SliverToBoxAdapter 2x kToolbarHeight for extra height for GridList, 
              - SliverGrid
     - LinearGradient 2xkToolbarHeight for fadeEffect on upper scroll
     - our widget TextField or anything

演示


class Body extends StatelessWidget {
  const Body({Key? key}) : super(key: key);

  Widget build(BuildContext context) {
    return Scaffold(
      body: LayoutBuilder(
        builder: (context, constraints) => Stack(
          children: [
            Container(
              decoration: BoxDecoration(
                // color: Colors.white.withOpacity(.3),
                image: DecorationImage(
                  image: AssetImage("assets/me.jpg"),
                  fit: BoxFit.cover,
                ),
              ),
              child: Container(),
            ),
            Container(
              decoration: BoxDecoration(
                color: Colors.white.withOpacity(.3),
              ),
              child: CustomScrollView(
                slivers: [
                  SliverToBoxAdapter(
                    child: SizedBox(
                      height: kToolbarHeight * 2,
                    ),
                  ),
                  SliverPadding(
                    padding: EdgeInsets.all(20),
                    sliver: SliverGrid.count(
                      crossAxisCount: 2,
                      mainAxisSpacing: 20,
                      crossAxisSpacing: 20,
                      children: [
                        ...List.generate(
                            12,
                            (index) => Container(
                                  decoration: BoxDecoration(
                                    color: index % 3 == 0
                                        ? Colors.deepPurple
                                        : index % 3 == 1
                                            ? Colors.deepOrange
                                            : Colors.amberAccent,
                                    borderRadius: BorderRadius.circular(12),
                                  ),
                                ))
                      ],
                    ),
                  )
                ],
              ),
            ),
            Align(
              alignment: Alignment.topCenter,
              child: Container(
                height: kToolbarHeight * 2,
                width: constraints.maxWidth,
                decoration: BoxDecoration(
                  gradient: LinearGradient(
                    begin: Alignment.topCenter,
                    end: Alignment.bottomCenter,
                    colors: [
                      Colors.grey,
                      Colors.white.withOpacity(.7),
                    ],
                  ),
                ),
                child: Text(""),
              ),
            ),
            Positioned(
              top: kTextTabBarHeight * 1.122,

              /// need to tweek
              left: 20,
              right: 20,
              child: Container(
                height: kToolbarHeight,
                alignment: Alignment.center,
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(12),
                  color: Colors.white70,
                  boxShadow: [
                    BoxShadow(
                        blurRadius: 12,
                        spreadRadius: 6,
                        color: Colors.black54,
                        offset: Offset(0, 12))
                  ],
                ),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    GestureDetector(
                        onTap: () {
                          print("boosm");
                        },
                        child: Text("Tap")),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

TL;DR 我使用普通 AppBar() 解决了这个问题,因为我不需要 SliverAppBar()。我制作了一个自定义应用程序栏来解决问题(请参阅问题末尾的代码)。


我意识到我不需要 SilverAppBar(),因为它只会保留 floatingpinned。这让我的生活变得轻松多了,因为我可以使用 AppBar() 并在 Scaffold() 中将 extendBodyBehindAppBar 设置为 true。这使得我不必制作自定义的条子小部件,因为我不熟悉制作它们。

我的解决方案是定制 AppBar()。我会有一个 Stack(),然后将模糊效果和 AppBar() 放在它上面。

https://github.com/flutter/flutter/issues/48212 表明您不能将 ShaderMasks()BackdropFilter() 一起使用。为了解决这个问题,我制作了一个包含一堆 BackdropFilter() 的专栏。他们会降低 sigma 值来创建我正在寻找的渐变效果。然而,这不是很好的性能,并且在较重的应用程序中效果不佳。使每个块具有单个逻辑像素的长度太重,所以我将其设为 2 个逻辑像素。

它也可以很容易地扩展,例如,像我一样添加淡入淡出效果。

结果如下。

这是解决方案的代码:

import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';

class BlurredAppBar extends StatelessWidget implements PreferredSizeWidget {
  final String title;
  final List<Widget>? actions;

  /// An `AppBar()` which has a blur effect behind it which fades in to hide it
  /// until content appears behind it. This has a similar effect to the iOS 14
  /// App Library app bar. It also has the possibility of having a fade effect to
  /// redude the opacity of widgets behind the `BlurredAppBar()` using a `LinearGradient()`.
  const BlurredAppBar({required this.title, this.actions, Key? key})
      : super(key: key);

  /// The height of the `AppBar()`
  final double height = 56;

  /// Returns a `List<Widget>` of `BackdropFilter()`s which have decreasing blur values.
  /// This will create the illusion of a gradient blur effect as if a `ShaderMask()` was used.
  List<Widget> _makeBlurGradient(double height, MediaQueryData mediaQuery) {
    List<Widget> widgets = [];
    double length = height + mediaQuery.padding.top;

    for (int i = 1; i <= (length / 2); i++) {
      widgets.add(
        ClipRRect(
          child: BackdropFilter(
            filter: ImageFilter.blur(
              sigmaX: max(((length / 2) - i.toDouble()) / 2, 0),
              sigmaY: min(5, max(((length / 2) - i.toDouble()) / 2, 0)),
            ),
            child: SizedBox(
              height: 2,
              width: mediaQuery.size.width,
            ),
          ),
        ),
      );
    }

    return widgets;
  }

  @override
  Widget build(BuildContext context) {
    final MediaQueryData mediaQuery = MediaQuery.of(context);

    return Stack(
      children: [
        // BackdropFilters
        SizedBox(
          height: height + mediaQuery.padding.top,
          child: Column(
            children: _makeBlurGradient(height, mediaQuery),
          ),
        ),
        // Fade effect.
        Container(
          decoration: BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
              stops: [0.5, 1],
              colors: [
                Colors.white.withOpacity(0.8),
                Colors.white.withOpacity(0),
              ],
            ),
          ),
        ),

        // AppBar
        AppBar(
          title: Text(
            title,
            style: Theme.of(context).textTheme.headline3,
          ),
          automaticallyImplyLeading: false,
          actions: actions,
        ),
      ],
    );
  }

  @override
  Size get preferredSize => Size.fromHeight(height);
}