如何遮盖重叠部分,通过 NestedScrollView 中的“半透明标题条”可见?

问题描述 投票:0回答:3

以下代码生成一个可滚动列表以及“半透明固定条标题”。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: NestedScrollView(
          headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
            return [
              SliverPersistentHeader(
                delegate: _SliverPersistentHeaderDelegate(),
                pinned: true,
              ),
            ];
          },
          body: ListView.builder(
            itemBuilder: (context, index) {
              return ListTile(
                title: Container(
                  color: Colors.amber.withOpacity(0.3),
                  child: Text('Item $index'),
                ),
              );
            },
          ),
        ),
      ),
    );
  }
}

class _SliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    return Container(
      color: Colors.blue.withOpacity(0.75),
      child: Placeholder(),
    );
  }

  @override double get maxExtent => 300;
  @override double get minExtent => 200;
  @override bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}

一切都好;除了,我需要“标题”是透明的,但是让它半透明,会导致下面的列表项被显示(如下面的屏幕截图所示)。

那么,如何“屏蔽”通过“半透明标题”可见的“列表项”呢?

The items are visible through the

flutter mask flutter-sliver
3个回答
1
投票

使用

CustomClipper
作为 List 本身怎么样?由于列表高度在滚动期间是动态的,因此必须动态计算剪辑高度。所以我将 ClipHeight 传递到自定义剪辑器中。

为了获取 ClipHeight,我使用

MediaQuery.of(context).size.height
- 标题高度。所以我创建另一个类来获取这个值。

      ...
      body: CustomWidget (
        child: ListView.builder(
        ...


class CustomWidget extends StatelessWidget {

 final Widget child;

 CustomWidget({this.child,Key key}):super(key:key);

  @override
  Widget build(BuildContext context) {
    return ClipRect(
      clipper: MyCustomClipper(clipHeight: MediaQuery.of(context).size.height-200),
      child: child,
    );
  }
}

class MyCustomClipper extends CustomClipper<Rect>{

  final double clipHeight;

  MyCustomClipper({this.clipHeight});

  @override
  getClip(Size size) {
    double top = math.max(size.height - clipHeight,0) ;
    Rect rect = Rect.fromLTRB(0.0, top, size.width, size.height);
    return rect;
  }

  @override
  bool shouldReclip(CustomClipper oldClipper) {
    return false;
  }
}

0
投票

固定

SliverPersistentHeader
的工作方式类似于 “CSS
position: absolute
。 所以你的身体小部件不知道上面有什么东西。 其中一种选择是不使用
SliverPersistentHeader

enter image description here

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

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
  ScrollController controller;

  @override
  void initState() {
    currentHeight = _maxExtent;
    controller = ScrollController();
    controller.addListener(() {
      _updateHeaderHeight();
    });
    super.initState();
  }

  _updateHeaderHeight() {
    double offset = controller.offset;
    if (offset <= _maxExtent - _minExtent) {
      setState(() {
        currentHeight = math.max(_maxExtent - offset, _minExtent);
      });
    }
  }

  double currentHeight;
  final double _maxExtent = 300;
  final double _minExtent = 200;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: DecoratedBox(
          // only to prove transparency
          decoration: BoxDecoration(
            image: DecorationImage(
              colorFilter: ColorFilter.mode(Colors.white, BlendMode.color),
              image: NetworkImage(
                'https://picsum.photos/720/1280',
              ),
              fit: BoxFit.cover,
            ),
          ),
          child: Stack(
            children: [
              Header(currentHeight: currentHeight),
              Padding(
                padding: EdgeInsets.only(top: currentHeight),
                child: Container(
                  decoration: BoxDecoration(
                    border: Border.all(color: Colors.blueAccent),
                  ),
                  child: ListView.builder(
                    controller: controller,
                    itemBuilder: (context, index) {
                      return ListTile(
                        title: Container(
                          color: Colors.amber.withOpacity(0.3),
                          child: Text('Item $index'),
                        ),
                      );
                    },
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class Header extends StatelessWidget {
  const Header({Key key, this.currentHeight}) : super(key: key);

  final double currentHeight;

  @override
  Widget build(BuildContext context) {
    return Container(
      height: currentHeight,
      color: Colors.blue.withOpacity(0.75),
      child: Placeholder(),
    );
  }
}

0
投票

实现此目的的正确方法是使用 SliverOverlapAbsorber 和 SliverOverlapInjector,(有关更多信息,请参阅文档),但此注入器只能在 customScrollView 内部使用,它为我们提供了 SliverOverlapAbsorber 吸收的高度。 我假设您不想在 NestedScrollView 中使用 CustomScrollView,而且在您的情况下也没有必要,因为我们知道标头的 minExtent。

一般来说,SliverOverlapAbsorber 会吸收 NestedScrollview 主体的 minExtent 高度,我们会将这个高度注入到分配给主体的小部件中。


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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: NestedScrollView(
          headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
            return [
              SliverOverlapAbsorber(
                handle:
                    NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                sliver: SliverPersistentHeader(
                  delegate: _SliverPersistentHeaderDelegate(),
                  pinned: true,
                ),
              ),
            ];
          },
          body: Column(
            children: [
              SizedBox(
                height: 200, //minExtent of SliverHeaderDelegate
              ),
              Expanded(child: ListView.builder(
                itemBuilder: (context, index) {
                  return ListTile(
                    title: Container(
                      color: Colors.amber.withOpacity(0.3),
                      child: Text('Item $index'),
                    ),
                  );
                },
              ))
            ],
          ),
        ),
      ),
    );
  }
}

class _SliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return Container(
      color: Colors.blue.withOpacity(0.75),
      child: Placeholder(),
    );
  }

  @override
  double get maxExtent => 300;

  @override
  double get minExtent => 200;

  @override
  bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}

© www.soinside.com 2019 - 2024. All rights reserved.