【绘制 widget】Flutter DecratedBox

简介: 【绘制 widget】Flutter DecratedBox

image.png

DecratedBox 介绍

在布局完成后,就进入到了绘制阶段。DecratedBox 有一个 position 参数,是 DecorationPosition 类型枚举,描述了在什么位置应用 decoration。

/// Where to paint a box decoration.
enum DecorationPosition {
  /// Paint the box decoration behind the children.
  background,
  /// Paint the box decoration in front of the children.
  foreground,
}
复制代码

position 决定了 decration 是在 child 之前绘制还是在 child 之后绘制。

DecratedBox 只是包了个 widget 的皮,真正如何绘制一般是由 BoxDecoration(也可以由其它 decoration 的子类) 来决定的。BoxDecoration 是一套不可变的配置,描述了如何绘制一个 box。

在 DecratedBox 对应的 renderBox 的 pain 方法中拿到 BoxDecoration 描述的 BoxPainter 进行绘制。

@override
  void paint(PaintingContext context, Offset offset) {
    assert(size.width != null);
    assert(size.height != null);
    // 拿到 BoxPainter 的实例
    _painter ??= _decoration.createBoxPainter(markNeedsPaint);
   ...
复制代码

BoxPainter 的实例携带着 BoxDecoration 的信息,绘制的时候会根据这些信息进行绘制。

通过 BoxPainter 的源代码,先了解下 BoxPainter 的绘制过程。

BoxPainter 的绘制过程

第一步 paint boxShadow

BoxShadow 是最先被绘制的。

参数 boxShadow 是一个 List 可以添加多个,效果叠加在一起。

第二步 paint BackgroundColor

color、gradient、blendMode 都是在这个时候应用。

第三步 paint BackgroundImage

参数 image 是一个 DecorationImageDecorationImage 和  BoxDecoration 的作用一样,都是配置。真是画图的是 paintImage

参数 shape 是枚举 BoxShape有两个值可以选

enum BoxShape {
  rectangle,
  circle,
}
复制代码

在绘制背景图的时候,shape 参数决定 clipPath

switch (_decoration.shape) {
      case BoxShape.circle:
        assert(_decoration.borderRadius == null);
        final Offset center = rect.center;
        final double radius = rect.shortestSide / 2.0;
        final Rect square = Rect.fromCircle(center: center, radius: radius);
        clipPath = Path()..addOval(square);
        break;
      case BoxShape.rectangle:
        if (_decoration.borderRadius != null) {
          clipPath = Path()..addRRect(_decoration.borderRadius!.resolve(configuration.textDirection).toRRect(rect));
        }
        break;
    }
   _imagePainter!.paint(canvas, rect, clipPath, configuration);
复制代码

第四步 绘制 border

参数 border,参数 shape,参数 borderRadius 会参与 border 的绘制。

_decoration.border?.paint(
      canvas,
      rect,
      shape: _decoration.shape,
      borderRadius: _decoration.borderRadius?.resolve(textDirection),
      textDirection: configuration.textDirection,
    );
复制代码

了解了整个绘制过程,我们来实践一下。

演示 BoxPainter 绘制过程

paint boxShadow


image.png

先画一个 shadow。画出来的 shadow 并不是只在边缘画,而是在整个区域绘制。

paint color

image.png

画上 color 后,会覆盖住主体部分,只漏出边缘。这下 shadow 的效果就出来了。

color 和 gradient 同时存在,color 被忽略

paint image


image.png


加上背景图后,color或 gradinet 会被覆盖。

image.png

加上 shape 参数后,可以 clip 整个 decoration。 shape 参数不能 clip child,这样做也是符合预期的,如果把 child 也 clip了,那作用就是装饰了。

paint border

image.png

border 也会被 shape 影响,在背景图的上面的绘制。是从外向内画,border 如果够粗,可以完全覆盖背景图。

DecratedBox 可以选择的 decration 类

一般情况下,我们选用的是 BoxDecoration。 查看文档发现 ,BoxDecoration 还有三个兄弟,是不是也可以用呢?

FlutterLogoDecoration 是用来画 FlutterLogo 的,pass。

UnderlineTabIndicator 是用来画 tab 下面的装饰的,pass。

ShapeDecoration 可以用,而且官方还给出了示例。border 可以叠加

image.png

Container(
  decoration: ShapeDecoration(
    color: Colors.white,
    shape: Border.all(
      color: Colors.red,
      width: 8.0,
    ) + Border.all(
      color: Colors.green,
      width: 8.0,
    ) + Border.all(
      color: Colors.blue,
      width: 8.0,
    ),
  ),
  child: const Text('RGB', textAlign: TextAlign.center),
)
复制代码

参数 shape 的类型是 ShapeBorder border 的继承关系 ShapeBorder > BoxBorder > border

BoxBorder 还有两个兄弟 InputBorder 和 OutlinedBorder。OutlinedBorder 是抽象类,他下面的子类都可以用。

这些类在 ClipPath 里面也可以用。

Boxdecration 只能画圆形和圆角矩形,如果需要它形状,就得用 ShapeDecration 了。如果 Boxdecration 和 ShapeDecration 都不能满足,还可以自己写 decration。

自定义 decration

自定义 decration 一共分两步。第一步, exteds Decration,第二步 extends BoxPainter。只要 cavas 熟练,这里就没什么难度了。

class MyDecration extends Decoration {
  @override
  BoxPainter createBoxPainter([VoidCallback? onChanged]) {
    return MyBoxPainter(this);
  }
}
class MyBoxPainter extends BoxPainter {
  final MyDecration _decration;
  MyBoxPainter(this._decration);
  @override
  void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
    // 在这随便画点什么
  }
}


目录
相关文章
|
11天前
|
容器
Flutter Widget 解析
Flutter Widget 解析
|
10天前
|
存储 容器
Flutter 有状态Widget 和 无状态Widget
Flutter 有状态Widget 和 无状态Widget
|
1月前
深入理解Flutter鸿蒙next版本 中的Widget继承:使用extends获取数据与父类约束
本文详细介绍了Flutter中如何通过继承其他Widget来创建自定义组件。首先解释了Widget继承的基本概念,包括StatelessWidget和StatefulWidget的区别。接着通过具体示例展示了如何继承StatelessWidget和StatefulWidget,并在子类中访问父类的build方法和状态。最后,结合多个自定义Widget展示了如何在实际应用中灵活使用继承和组合来构建复杂的UI。
83 8
|
1月前
|
容器
flutter&鸿蒙next 使用 InheritedWidget 实现跨 Widget 传递状态
在 Flutter 中,状态管理至关重要。本文详细介绍了如何使用 InheritedWidget 实现跨 Widget 的状态传递。InheritedWidget 允许数据在 Widget 树中向下传递,适用于多层嵌套的场景。通过一个简单的计数器示例,展示了如何创建和使用 InheritedWidget,包括其基础概念、工作原理及代码实现。虽然 InheritedWidget 较底层,但它是许多高级状态管理解决方案的基础。
108 2
|
2月前
|
容器
flutter:第一个flutter&Widget的使用 (二)
本文介绍了Flutter框架下的基本组件及其用法,包括简单的 Stateless Widget 如文本和按钮,以及更复杂的 StatefulWidget 示例。详细解释了如何使用 `context` 获取祖先小部件的信息,并展示了 `MaterialApp` 的属性及用途。此外,还探讨了 `StatefulWidget` 与 `StatelessWidget` 的区别,以及 `AppBar` 的常见属性配置方法。适合Flutter初学者参考学习。
|
1月前
|
Dart JavaScript 前端开发
Flutter 的 Widget 概述与常用 Widgets 与鸿蒙 Next 的对比
Flutter 是 Google 开发的开源 UI 框架,用于快速构建高性能的移动、Web 和桌面应用。Flutter 通过 Widget 构建 UI,每个 UI 元素都是 Widget,包括文本、按钮、图片等。Widget 不仅描述外观,还描述行为,是不可变的。常见的 Widget 包括结构型(Container、Column、Row)、呈现型(Text、Image)、交互型(ElevatedButton)和状态管理型(StatefulWidget)。Flutter 与鸿蒙 Next 在组件化架构、开发语言、布局系统、性能和跨平台支持方面各有优势
82 0
|
5月前
Flutter-底部弹出框(Widget层级)
文章描述了如何在Flutter中使用DraggableScrollableSheet创建一个底部弹出框,同时保持其可手势滑动关闭。作者遇到问题并提出对原控件进行扩展,以支持头部和列表布局的滑动关闭功能。
201 0
|
6月前
Flutter StreamBuilder 实现局部刷新 Widget
Flutter StreamBuilder 实现局部刷新 Widget
54 0
|
7月前
|
Android开发
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
|
7月前
|
开发框架 前端开发 搜索推荐
【Flutter前端技术开发专栏】Flutter中的自定义Widget与渲染流程
【4月更文挑战第30天】探索Flutter的自定义Widget与渲染流程。自定义Widget是实现复杂UI设计的关键,优点在于个性化设计、功能扩展和代码复用,但也面临性能优化和复杂性管理的挑战。创建步骤包括设计结构、定义Widget类、实现构建逻辑和处理交互。Flutter渲染流程涉及渲染对象树、布局、绘制和合成阶段。实践案例展示如何创建带渐变背景和阴影的自定义按钮。了解这些知识能提升应用体验并应对开发挑战。查阅官方文档以深入学习。
88 0
【Flutter前端技术开发专栏】Flutter中的自定义Widget与渲染流程