Flutter:使剪切区域透明以滚动 ListView
Posted
技术标签:
【中文标题】Flutter:使剪切区域透明以滚动 ListView【英文标题】:Flutter: Making clipped area transparent for scrolling ListView 【发布时间】:2021-02-19 05:30:52 【问题描述】:我有一个ListView
,当它遇到另一个小部件的剪辑时,我想“消失”。
这是我的代码
import 'package:flutter/material.dart';
void main()
runApp(MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
home: MyHomePage(),
);
class MyHomePage extends StatelessWidget
@override
Widget build(BuildContext context)
return Scaffold(
body: Column(
children: [
TopWidget(),
Expanded(
child: ListView(
itemExtent: 100,
children: <Widget>[
Card(color: Colors.green,),
],
),
),
],
),
);
class TopWidget extends StatelessWidget
TopWidget();
@override
Widget build(BuildContext context)
return CustomPaint(
painter: ShadowPainter(),
child: ClipPath(
clipper: TopWidgetClipper(),
child: Container(
height: 370,
color: Colors.blue,
),
),
);
class TopWidgetClipper extends CustomClipper<Path>
@override
Path getClip(Size size)
Offset controllPoint1 = Offset(0, size.height - 100);
Offset endPoint1 = Offset(100, size.height - 100);
Offset controllPoint2 = Offset(size.width, size.height - 100);
Offset endPoint2 = Offset(size.width, size.height - 200);
Path path = Path()
..lineTo(0, size.height)
..quadraticBezierTo(
controllPoint1.dx, controllPoint1.dy, endPoint1.dx, endPoint1.dy)
..lineTo(size.width - 100, size.height - 100)
..quadraticBezierTo(
controllPoint2.dx, controllPoint2.dy, endPoint2.dx, endPoint2.dy)
..lineTo(size.width, 0);
return path;
@override
bool shouldReclip(CustomClipper<Path> oldClipper)
return true;
class ShadowPainter extends CustomPainter
@override
void paint(Canvas canvas, Size size)
Offset controllPoint1 = Offset(0, size.height - 100);
Offset endPoint1 = Offset(100, size.height - 100);
Offset controllPoint2 = Offset(size.width, size.height - 100);
Offset endPoint2 = Offset(size.width, size.height - 200);
Path path = Path()
..lineTo(0, size.height)
..quadraticBezierTo(
controllPoint1.dx, controllPoint1.dy, endPoint1.dx, endPoint1.dy)
..lineTo(size.width - 100, size.height - 100)
..quadraticBezierTo(
controllPoint2.dx, controllPoint2.dy, endPoint2.dx, endPoint2.dy)
..lineTo(size.width, 0);
canvas.drawShadow(path, Colors.grey[50], 3.0, false);
@override
bool shouldRepaint(CustomPainter oldDelegate)
return true;
到目前为止,当我向下滚动时,当列表(绿色框)到达我剪辑的TopWidget
的容器(黄色边框)底部时,它就看不见了。但我希望列表只有在到达我的剪辑边缘时才会平滑消失(即蓝色区域 - 就像在第二个屏幕截图中一样)。
有什么想法可以做到这一点吗?谢谢!
【问题讨论】:
【参考方案1】:正如我从 @pskink 那里学到的(谢谢),在这样的用例中,您需要小部件实际调整其边界(剧透:形状),您应该使用不同小部件的 shape
属性和在扩展ShapeBorder
的自定义类中使用您用于此示例的Path
。最简单的方法是:
Container(
height: 370,
decoration: ShapeDecoration(
color: Colors.blue,
shape: AppBarBorder(),
/// You can also specify some neat shadows to cast on widgets scrolling under this one
shadows: [
BoxShadow(
color: Colors.black.withOpacity(0.7),
blurRadius: 18.0,
spreadRadius: 2.0,
),
],
),
),
还有自定义类:
class AppBarBorder extends ShapeBorder
@override
Path getOuterPath(Rect rect, TextDirection textDirection)
Offset controllPoint1 = Offset(0, rect.size.height - 100);
Offset endPoint1 = Offset(100, rect.size.height - 100);
Offset controllPoint2 = Offset(rect.size.width, rect.size.height - 100);
Offset endPoint2 = Offset(rect.size.width, rect.size.height - 200);
return Path()
..lineTo(0, rect.size.height)
..quadraticBezierTo(
controllPoint1.dx, controllPoint1.dy, endPoint1.dx, endPoint1.dy)
..lineTo(rect.size.width - 100, rect.size.height - 100)
..quadraticBezierTo(
controllPoint2.dx, controllPoint2.dy, endPoint2.dx, endPoint2.dy)
..lineTo(rect.size.width, 0);
@override
EdgeInsetsGeometry get dimensions => EdgeInsets.only(bottom: 0);
@override
Path getInnerPath(Rect rect, TextDirection textDirection) => null;
@override
void paint(Canvas canvas, Rect rect, TextDirection textDirection)
@override
ShapeBorder scale(double t) => this;
声明CustomClipper
或CustomPainter
的方法几乎相同,因为您不需要实现大多数这些方法,基本上只需要关心getOuterPath
。
最后,我们需要重新构建布局本身,因为目前您有一个 Column
和这个自定义的 Container
形状和下面的 ListView
。由于Container
不是ListView
的一部分,它不能滚动到下面或其他地方。最简单的方法是使用Stack
:
Stack(
children: [
Expanded(
child: ListView(
padding: EdgeInsets.only(top: 370.0),
itemExtent: 100,
children: <Widget>[
Card(
color: Colors.green,
),
],
),
),
Container(
height: 370,
decoration: ShapeDecoration(
color: Colors.blue,
shape: AppBarBorder(),
shadows: [
BoxShadow(
color: Colors.black.withOpacity(0.7),
blurRadius: 18.0,
spreadRadius: 2.0,
),
],
),
),
],
),
【讨论】:
太棒了!太感谢了!我只需要删除 ListView 周围的 Expanded 小部件,因为它引发了异常......否则它可以完美运行!以上是关于Flutter:使剪切区域透明以滚动 ListView的主要内容,如果未能解决你的问题,请参考以下文章
如何让 Flutter 应用在 android 导航栏后面绘制并使导航栏完全透明?
Flutter监听滚动动作 控制组件 透明度渐变 ( 移除顶部状态栏空白 | 帧布局组件 | 透明度组件 | 监听滚动组件 )