在Flutter中,如何创建一个带有毛玻璃效果的SliverAppBar?

In Flutter, how to create a SliverAppBar with frosted glass effect?

我想创建一个类似于 Apple News 应用程序中的顶部应用程序栏。

很难在屏幕截图中看到模糊,因为条太细了,但你明白了。

我希望栏通过滚动来扩展和收缩,并在收缩时固定在顶部,就像在屏幕截图中一样,SliverAppBar 完成了所有这些,只是我不能将它包裹在 ClipRect、BackdropFilter 和 Opacity 中创建磨砂玻璃效果,因为 CustomScrollView 仅采用 RenderSliv​​er child 类.

我的测试代码:

Widget build(BuildContext context) {
  return CustomScrollView(
    slivers: <Widget>[
      SliverAppBar(
        title: Text('SliverAppBar'),
        elevation: 0,
        floating: true,
        pinned: true,
        backgroundColor: Colors.grey[50],
        expandedHeight: 200.0,
        flexibleSpace: FlexibleSpaceBar(
            background: Image.network("https://i.imgur.com/cFzxleh.jpg", fit: BoxFit.cover)
        ),
      )
      ,
      SliverFixedExtentList(
        itemExtent: 150.0,
        delegate: SliverChildListDelegate(
          [
            Container(color: Colors.red),
            Container(color: Colors.purple),
            Container(color: Colors.green),
            Container(color: Colors.orange),
            Container(color: Colors.yellow),
            Container(color: Colors.pink,
              child: Image.network("https://i.imgur.com/cFzxleh.jpg", fit: BoxFit.cover)
            ),
          ],
        ),
      ),
    ],
  );
}

有没有办法实现我想要的?

我设法通过将 AppBar 包裹在 SliverPersistentHeader 中来让它工作(这基本上就是 SliverAppBar 所做的)。

忽略未模糊的边缘这是一个 iOS 模拟器错误。

这是一个概念验证代码示例:

class TranslucentSliverAppBar extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SliverPersistentHeader(
        floating: true,
        pinned: true,
        delegate: _TranslucentSliverAppBarDelegate(
            MediaQuery.of(context).padding,
        )
    );
  }
}

class _TranslucentSliverAppBarDelegate extends SliverPersistentHeaderDelegate {

  /// This is required to calculate the height of the bar
  final EdgeInsets safeAreaPadding;

  _TranslucentSliverAppBarDelegate(this.safeAreaPadding);

  @override
  double get minExtent => safeAreaPadding.top;

  @override
  double get maxExtent => minExtent + kToolbarHeight;

  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    return ClipRect(child: BackdropFilter(
      filter: ImageFilter.blur(sigmaX: 16, sigmaY: 16),
      child: Opacity(
          opacity: 0.93,
          child: Container(
              // Don't wrap this in any SafeArea widgets, use padding instead
              padding: EdgeInsets.only(top: safeAreaPadding.top),
              height: maxExtent,
              color: Colors.white,
              // Use Stack and Positioned to create the toolbar slide up effect when scrolled up
              child: Stack(
                  overflow: Overflow.clip,
                  children: <Widget>[
                    Positioned(
                      bottom: 0, left: 0, right: 0,
                      child: AppBar(
                          primary: false,
                          elevation: 0,
                          backgroundColor: Colors.transparent,
                          title: Text("Translucent App Bar"),
                      ),
                    )
                  ],
              )
          )
      )
    ));
  }

  @override
  bool shouldRebuild(_TranslucentSliverAppBarDelegate old) {
    return maxExtent != old.maxExtent || minExtent != old.minExtent ||
        safeAreaPadding != old.safeAreaPadding;
  }
}