Flutter之Sliver组件扩展

Sliver主要用于处理嵌套滑动、合并多个List的情况,像GridView、ListView自身是支持滑动的,这个时候如果再在外层嵌套一个滑动的容器组件的话,如果说GridView、ListView的滑动方向与外层可滑动容器的滑动方向一致的时候,必然会引发滑动冲突的问题,这个时候我们的外层可滑动容器可以采用CustomScrollView,并配合SliverGrid、SliverList实现,查看ListView、GridView的源码可以知道,SliverGrid、SliverList相当于是GridView、ListView去除了滚动特性,即:

SliverGrid + Scrollable = GridView

SliverList + Scrollable = ListView

接下来看一个官方的GridView的实例:

CustomScrollView(primary: false,slivers: <Widget>[SliverPadding(padding: const EdgeInsets.all(20),sliver: SliverGrid.count(crossAxisSpacing: 10,mainAxisSpacing: 10,crossAxisCount: 2,children: <Widget>[Container(padding: const EdgeInsets.all(8),child: const Text("He'd have you all unravel at the"),color: Colors.green[100],),Container(padding: const EdgeInsets.all(8),child: const Text('Heed not the rabble'),color: Colors.green[200],),Container(padding: const EdgeInsets.all(8),child: const Text('Sound of screams but the'),color: Colors.green[300],),Container(padding: const EdgeInsets.all(8),child: const Text('Who scream'),color: Colors.green[400],),Container(padding: const EdgeInsets.all(8),child: const Text('Revolution is coming...'),color: Colors.green[500],),Container(padding: const EdgeInsets.all(8),child: const Text('Revolution, they...'),color: Colors.green[600],),],),),],
)

现在我们想单独为这个SliverGrid设置圆角和背景怎么办,实现下面的效果怎么办呢:

首先我们会想到给SliverGrid套一个Container,然后为Container指定decoration来实现,这样行不行呢,试试看就知道了

CustomScrollView(primary: false,slivers: <Widget>[SliverPadding(padding: const EdgeInsets.all(20),sliver: Container(decoration: BoxDecoration(color: Color(0xffff0000),borderRadius: BorderRadius.all(Radius.circular(20))),child: SliverGrid.count(crossAxisSpacing: 10,mainAxisSpacing: 10,crossAxisCount: 2,children: <Widget>[Container(padding: const EdgeInsets.all(8),child: const Text("He'd have you all unravel at the"),color: Colors.green[100],),Container(padding: const EdgeInsets.all(8),child: const Text('Heed not the rabble'),color: Colors.green[200],),Container(padding: const EdgeInsets.all(8),child: const Text('Sound of screams but the'),color: Colors.green[300],),Container(padding: const EdgeInsets.all(8),child: const Text('Who scream'),color: Colors.green[400],),Container(padding: const EdgeInsets.all(8),child: const Text('Revolution is coming...'),color: Colors.green[500],),Container(padding: const EdgeInsets.all(8),child: const Text('Revolution, they...'),color: Colors.green[600],),],),),),],
),

很明显这样是不行的,这是因为一个支持Sliver的组件他的子组件对应的RenderObject必须要是一个RenderSliver对象,而Container内部在处理decoration属性的时候是通过DecoratedBox来处理的,而DecoratedBox本身对应的RenderObject是一个RenderBox对象。

然而google官方并没有为我们提供一个支持Sliver的DecoratedBox组件,而默认支持Sliver的组件包含SliverPadding、SliverOpacity、SliverOffstage并不能满足我们的要求,所以我们可以通过扩展一个Sliver组件,实现我们想要的效果,接下来我们参考DecoratedBox的源码来实现一个SliverDecoratedBox,从而实现图2中的效果:

1.编写SliverDecoratedBox widget

直接拷贝DecoratedBox的源码在他的基础上做改造:

1.接收子元素的成员变量名有child变成了Sliver

2.createRenderObject返回对象由RenderDecoratedBox变成RenderSliverDecoratedBox

3.updateRenderObject参数中的RenderDecoratedBox变成RenderSliverDecoratedBox

class SliverDecoratedBox extends SingleChildRenderObjectWidget {const SliverDecoratedBox({Key? key,required this.decoration,this.position = DecorationPosition.background,Widget? sliver,}) : assert(decoration != null),assert(position != null),super(key: key, child: sliver);final Decoration decoration;final DecorationPosition position;@overrideRenderSliverDecoratedBox createRenderObject(BuildContext context) {return RenderSliverDecoratedBox(decoration: decoration,position: position,configuration: createLocalImageConfiguration(context),);}@overridevoid updateRenderObject(BuildContext context, RenderSliverDecoratedBox renderObject) {renderObject..decoration = decoration..configuration = createLocalImageConfiguration(context)..position = position;}@overridevoid debugFillProperties(DiagnosticPropertiesBuilder properties) {super.debugFillProperties(properties);final String label;switch (position) {case DecorationPosition.background:label = 'bg';break;case DecorationPosition.foreground:label = 'fg';break;}properties.add(EnumProperty<DecorationPosition>('position', position, level: DiagnosticLevel.hidden));properties.add(DiagnosticsProperty<Decoration>(label, decoration));}
}

2.实现RenderSliverDecoratedBox RenderSliver

直接拷贝RenderDecoratedBox的源码在他的基础上做改造:

1.父类由RenderProxyBox变为RenderProxySliver

2.接收子元素的成员变量类型由RenderBox变为RenderSliver

3.获取组件的大小的Size对象,不在通过从RenderBox继承的size属性获取,而是通过从RenderSliver继承的getAbsoluteSize方法获取

class RenderSliverDecoratedBox extends RenderProxySliver {RenderSliverDecoratedBox({required Decoration decoration,DecorationPosition position = DecorationPosition.background,ImageConfiguration configuration = ImageConfiguration.empty,RenderSliver? sliver,}) : assert(decoration != null),assert(position != null),assert(configuration != null),_decoration = decoration,_position = position,_configuration = configuration {child = sliver;}BoxPainter? _painter;Decoration get decoration => _decoration;Decoration _decoration;set decoration(Decoration value) {assert(value != null);if (value == _decoration)return;_painter?.dispose();_painter = null;_decoration = value;markNeedsPaint();}DecorationPosition get position => _position;DecorationPosition _position;set position(DecorationPosition value) {assert(value != null);if (value == _position)return;_position = value;markNeedsPaint();}ImageConfiguration get configuration => _configuration;ImageConfiguration _configuration;set configuration(ImageConfiguration value) {assert(value != null);if (value == _configuration)return;_configuration = value;markNeedsPaint();}@overridevoid detach() {_painter?.dispose();_painter = null;super.detach();markNeedsPaint();}@overridevoid paint(PaintingContext context, Offset offset) {Size size = getAbsoluteSize();assert(size.width != null);assert(size.height != null);_painter ??= _decoration.createBoxPainter(markNeedsPaint);final ImageConfiguration filledConfiguration = configuration.copyWith(size: size);if (position == DecorationPosition.background) {int? debugSaveCount;assert(() {debugSaveCount = context.canvas.getSaveCount();return true;}());_painter!.paint(context.canvas, offset, filledConfiguration);assert(() {if (debugSaveCount != context.canvas.getSaveCount()) {throw FlutterError.fromParts(<DiagnosticsNode>[ErrorSummary('${_decoration.runtimeType} painter had mismatching save and restore calls.'),ErrorDescription('Before painting the decoration, the canvas save count was $debugSaveCount. ''After painting it, the canvas save count was ${context.canvas.getSaveCount()}. ''Every call to save() or saveLayer() must be matched by a call to restore().',),DiagnosticsProperty<Decoration>('The decoration was', decoration, style: DiagnosticsTreeStyle.errorProperty),DiagnosticsProperty<BoxPainter>('The painter was', _painter, style: DiagnosticsTreeStyle.errorProperty),]);}return true;}());if (decoration.isComplex)context.setIsComplexHint();}super.paint(context, offset);if (position == DecorationPosition.foreground) {_painter!.paint(context.canvas, offset, filledConfiguration);if (decoration.isComplex)context.setIsComplexHint();}}@overridevoid debugFillProperties(DiagnosticPropertiesBuilder properties) {super.debugFillProperties(properties);properties.add(_decoration.toDiagnosticsNode(name: 'decoration'));properties.add(DiagnosticsProperty<ImageConfiguration>('configuration', configuration));}
}

4.处理child对应的圆角

class RenderSliverDecoratedBox extends RenderProxySliver {...@overridevoid paint(PaintingContext context, Offset offset) {...if(decoration is BoxDecoration) {BorderRadiusGeometry? borderRadius = (decoration as BoxDecoration).borderRadius;if(borderRadius != null) {RRect clipRect = borderRadius.resolve(configuration.textDirection).toRRect(Rect.fromLTRB(0, 0, constraints.crossAxisExtent, geometry!.maxPaintExtent));context.pushClipRRect(needsCompositing,offset,clipRect.outerRect,clipRect,super.paint,);}}}...}

到此一个支持Sliver的DecoratedBox就编写完了,接下来我们在使用的时候只需要将上面的Container替换成SliverDecoratedBox即可,这里只是以DecoratedBox为例来说明,我们怎么参考系统组件的源码来扩展一个支持Sliver的对应的组件。

CustomScrollView(primary: false,slivers: <Widget>[SliverPadding(padding: const EdgeInsets.all(20),sliver: SliverDecoratedBox(decoration: BoxDecoration(color: Color(0xffff0000),borderRadius: BorderRadius.all(Radius.circular(20))),sliver: SliverGrid.count(crossAxisSpacing: 10,mainAxisSpacing: 10,crossAxisCount: 2,children: <Widget>[Container(padding: const EdgeInsets.all(8),child: const Text("He'd have you all unravel at the"),color: Colors.green[100],),Container(padding: const EdgeInsets.all(8),child: const Text('Heed not the rabble'),color: Colors.green[200],),Container(padding: const EdgeInsets.all(8),child: const Text('Sound of screams but the'),color: Colors.green[300],),Container(padding: const EdgeInsets.all(8),child: const Text('Who scream'),color: Colors.green[400],),Container(padding: const EdgeInsets.all(8),child: const Text('Revolution is coming...'),color: Colors.green[500],),Container(padding: const EdgeInsets.all(8),child: const Text('Revolution, they...'),color: Colors.green[600],),],),),),],
),

5.完整源码

class SliverDecoratedBox extends SingleChildRenderObjectWidget {const SliverDecoratedBox({Key? key,required this.decoration,this.position = DecorationPosition.background,Widget? sliver,}) : assert(decoration != null),assert(position != null),super(key: key, child: sliver);final Decoration decoration;final DecorationPosition position;@overrideRenderSliverDecoratedBox createRenderObject(BuildContext context) {return RenderSliverDecoratedBox(decoration: decoration,position: position,configuration: createLocalImageConfiguration(context),);}@overridevoid updateRenderObject(BuildContext context, RenderSliverDecoratedBox renderObject) {renderObject..decoration = decoration..configuration = createLocalImageConfiguration(context)..position = position;}@overridevoid debugFillProperties(DiagnosticPropertiesBuilder properties) {super.debugFillProperties(properties);final String label;switch (position) {case DecorationPosition.background:label = 'bg';break;case DecorationPosition.foreground:label = 'fg';break;}properties.add(EnumProperty<DecorationPosition>('position', position, level: DiagnosticLevel.hidden));properties.add(DiagnosticsProperty<Decoration>(label, decoration));}
}class RenderSliverDecoratedBox extends RenderProxySliver {RenderSliverDecoratedBox({required Decoration decoration,DecorationPosition position = DecorationPosition.background,ImageConfiguration configuration = ImageConfiguration.empty,RenderSliver? sliver,}) : assert(decoration != null),assert(position != null),assert(configuration != null),_decoration = decoration,_position = position,_configuration = configuration {child = sliver;}BoxPainter? _painter;Decoration get decoration => _decoration;Decoration _decoration;set decoration(Decoration value) {assert(value != null);if (value == _decoration)return;_painter?.dispose();_painter = null;_decoration = value;markNeedsPaint();}DecorationPosition get position => _position;DecorationPosition _position;set position(DecorationPosition value) {assert(value != null);if (value == _position)return;_position = value;markNeedsPaint();}ImageConfiguration get configuration => _configuration;ImageConfiguration _configuration;set configuration(ImageConfiguration value) {assert(value != null);if (value == _configuration)return;_configuration = value;markNeedsPaint();}@overridevoid detach() {_painter?.dispose();_painter = null;super.detach();markNeedsPaint();}@overridevoid paint(PaintingContext context, Offset offset) {Size size = getAbsoluteSize();assert(size.width != null);assert(size.height != null);_painter ??= _decoration.createBoxPainter(markNeedsPaint);final ImageConfiguration filledConfiguration = configuration.copyWith(size: size);if (position == DecorationPosition.background) {int? debugSaveCount;assert(() {debugSaveCount = context.canvas.getSaveCount();return true;}());_painter!.paint(context.canvas, offset, filledConfiguration);assert(() {if (debugSaveCount != context.canvas.getSaveCount()) {throw FlutterError.fromParts(<DiagnosticsNode>[ErrorSummary('${_decoration.runtimeType} painter had mismatching save and restore calls.'),ErrorDescription('Before painting the decoration, the canvas save count was $debugSaveCount. ''After painting it, the canvas save count was ${context.canvas.getSaveCount()}. ''Every call to save() or saveLayer() must be matched by a call to restore().',),DiagnosticsProperty<Decoration>('The decoration was', decoration, style: DiagnosticsTreeStyle.errorProperty),DiagnosticsProperty<BoxPainter>('The painter was', _painter, style: DiagnosticsTreeStyle.errorProperty),]);}return true;}());if (decoration.isComplex)context.setIsComplexHint();}super.paint(context, offset);if (position == DecorationPosition.foreground) {_painter!.paint(context.canvas, offset, filledConfiguration);if (decoration.isComplex)context.setIsComplexHint();}if(decoration is BoxDecoration) {BorderRadiusGeometry? borderRadius = (decoration as BoxDecoration).borderRadius;if(borderRadius != null) {RRect clipRect = borderRadius.resolve(configuration.textDirection).toRRect(Rect.fromLTRB(0, 0, constraints.crossAxisExtent, geometry!.maxPaintExtent));context.pushClipRRect(needsCompositing,offset,clipRect.outerRect,clipRect,super.paint,);}}}@overridevoid debugFillProperties(DiagnosticPropertiesBuilder properties) {super.debugFillProperties(properties);properties.add(_decoration.toDiagnosticsNode(name: 'decoration'));properties.add(DiagnosticsProperty<ImageConfiguration>('configuration', configuration));}
}

37_Flutter之Sliver组件扩展相关推荐

  1. DLUX组件扩展下篇-实践

    一.文章目标 1.1目标 随着SDN技术的逐步成熟,大量的传统数通厂家和新型的IT厂家,都投入了一定的人员进行相关的产品技术预研.而ODL作为SDN控制器的主要开源项目,自然也成了多数人学习和搭建模拟 ...

  2. 基于log4net的日志组件扩展封装,实现自动记录交互日志 XYH.Log4Net.Extend(微服务监控)...

    背景: 随着公司的项目不断的完善,功能越来越复杂,服务也越来越多(微服务),公司迫切需要对整个系统的每一个程序的运行情况进行监控,并且能够实现对自动记录不同服务间的程序调用的交互日志,以及通一个服务或 ...

  3. Firefox - 附加组件 - 扩展 - Firebug - 更新 - 1.2.0b15

    Firefox - 附加组件 - 扩展 - Firebug - 更新 - 1.2.0b15 * Suspend feature 禁用特性 * Display which tabs have fireb ...

  4. extjs6 基础组件扩展和自定义组件封装--02目录结构

    博客主要围绕核心扩展讲解. 1.overrides                            继承ext组件扩展自定义方法. 2.plugin                        ...

  5. layui树组件(扩展为带有图标的layui树组件)

    项目场景: 说起来layui前端框架,做后端开发的也是再熟悉不过了,模块开发,友好的UI界面,对前端后端人员都十分友好,当然,文档和示例也算是相当完备了.但是在开发项目时会遇到这样一种情况,就是想实现 ...

  6. element ui table组件扩展关于列表编辑按钮的位置放置

    最近在用vue做项目,主要是用的element ui的组件,在用的过程中发现有部分组件需要扩展,改源码太折腾,成本高,就想着如何节省成本来实现这些需求,由于项目时间紧张,有些实现来也没来得及记录一下, ...

  7. flutter 一直loading_开源 | FLUI : Flutter 的组件扩展集

    在使用 Flutter 的过程中,总是不断的在与组件打交道,虽然 Flutter 官方已经有极为丰富的 Material Design 以及 iOS 风格的组件集合,但我们也需要不断的抽象,组合出一些 ...

  8. PdfiumViewer组件扩展

    前言: PdfiumViewer github上是这样介绍的: PdfiumViewer 是一个基于 PDFium 项目的 PDF 查看器. PdfiumViewer 提供了许多组件来处理 PDF 文 ...

  9. JUC组件扩展(二)-JAVA并行框架Fork/Join(四):监控Fork/Join池

    Fork/Join 框架是为了解决可以使用 divide 和 conquer 技术,使用 fork() 和 join() 操作把任务分成小块的问题而设计的.主要实现这个行为的是 ForkJoinPoo ...

  10. ElementUI上传组件扩展--重新上传

    前言:在做商城类后台时,避免不了需要使用图片上传组件,然而如果你使用了elementUI,那么恭喜你,你将不能处理,多图片上传后更改其中图片顺序问题,elementUI提供的照片墙和文件缩略图都未提供 ...

最新文章

  1. 2019山东夏令营摸鱼记
  2. 设计模式(八)代理模式(结构型)
  3. VS2010 Visual Assist X 的配合
  4. android开发屏幕横放,android-即使从横向旋转到垂直,细节片段也会...
  5. BT种子 磁力链接 是什么?
  6. KELL中程序封装的实现
  7. Excel分割字符串
  8. 几种工具反编译被编译好的DLL文件
  9. 华为手机所有图标变黑_华为手机app图标变成黑色
  10. APP上架市场隐私政策被拒(关于未经用户同意收集用户信息)
  11. “天才少女”谷爱凌:奥运金牌在左,量子物理在右
  12. (邱维声)高等代数课程笔记:数域
  13. Java项目:高校运动会管理系统(java+SSM+JSP+JS+jQuery+Mysql)
  14. kafka-4.进阶,ISR,OSR,AR,LW,HW,LEO,ACK原理理论
  15. 全球教育ERP系统软件行业调研及趋势分析报告
  16. 5月3日云栖精选夜读:乾隆会判阿尔法狗死刑吗 ——浅谈当前人工智能的技术进化...
  17. 一文讲清chatGPT的发展历程、能力来源和复现它的关键之处
  18. 搭建webgis开发环境
  19. python批量重命名工具
  20. 2022全国职业技能大赛-网络安全赛题解析总结②(超详细)

热门文章

  1. Linux学习笔记(1)--Linux的发展史
  2. C# Control.Refresh的解释是什么意思?
  3. 二进制转十六进制(参考XYJ)
  4. 智遥工作代理问题解析
  5. Java进阶篇 设计模式之十四 ----- 总结篇
  6. Ant-Table查看详情
  7. Microsoft 工作账户无法删除 解决方法
  8. 如何用python实现爬虫自动爬取百度图片原图
  9. 阿里智能App下架,智能家居平台淘汰赛拉开大幕
  10. Vue3.x 深入浅出系列(连载三)