Flutter 编写自定义组件 Part2.a ChildSize(with helpers)

image

猫哥说

这是自定义组件的第二篇, 第一篇点这里

有的时候我们需要做些很基础的组件或者工具,我们需要控制渲染、尺寸变化、预处理、销毁

这篇文章是写关于如何封装一个响应式的 ChangeSize 组件,组件继承了 SingleChildRenderObjectWidget,如果你也在写类似的功能,用这个父类可以快速的上手。

老铁记得 转发 ,猫哥会呈现更多 Flutter 好文~~~~

微信群 ducafecat

b 站 https://space.bilibili.com/404904528

原文

https://rlesovyi.medium.com/writing-custom-widgets-in-flutter-part-2-singlechildrenderobjectwidget-5637fecdf9bb

代码

https://github.com/MatrixDev/Flutter-CustomWidgets

参考

正文

image

介绍

我写第二篇文章的时候到了。这次它将是一个非常简单的小部件,当它的子大小发生变化时,它会通知我们。这个任务非常简单,但是它的主要目的是向您展示如何在 RenderObject 中管理子对象。

当我刚开始学习 Flutter 的时候,它是我的问题之一,甚至是完成 Udemy 的课程,很遗憾,我没有得到我的答案。在 StackOverflow 上,人们建议在 Widget 上使用 GlobalKey,找到它的 RenderObject 并获取它的大小。

虽然上述解决方案没有任何问题,但我仍然不喜欢它的一些地方:

  • 它改变了 Widget 元素的销毁方式
  • 您需要在声明性小部件结构中添加命令式代码
  • 没有能力实际跟踪小部件的大小,它需要根据需要拉

一般来说,我想要达到的目标是:

return ChildSize(
  child: buildChildWidget(),
  onChildSizeChanged: (size) => handleNewChildSize(size),
);

简单的理论

当编写一个包含子窗口的自定义窗口小部件时,我们需要知道一些重要的事情:

  • 对于每个自定义小部件,我们需要编写它的 Element 和(有时) RenderObject 实现
  • 元素将扩展其子元素 Widgets 为单独的元素,并在需要时更新/重新创建它们
  • 几乎没有什么重要的角色ーー子代管理、布局、绘制和命中测试(鼠标指针、触摸事件等)

代码

首先,我们需要声明一个实际的 Widget:

class ChildSize extends SingleChildRenderObjectWidget {
  final void Function(Size)? onChildSizeChanged;

  const ChildSize({
    Key? key,
    Widget? child,
    this.onChildSizeChanged,
  }) : super(key: key, child: child);
}

这里没有什么新东西,除了 SingleChildRenderObjectWidget。这是 Flutter 框架中的一个帮手,它允许我们编写不超过一个子窗口的自定义窗口小部件。这大大简化了我们的代码,因为我们根本不需要编写定制的 Element。

我们唯一需要添加到 Widget 中的是创建 RenderObject,并在 Widget 更改时更新它:

class ChildSize extends SingleChildRenderObjectWidget {
  // ...

  @override
  RenderObject createRenderObject(BuildContext context) {
    return RenderChildSize().._widget = this;
  }

  @override
  void updateRenderObject(BuildContext context, RenderChildSize renderObject) {
    renderObject.._widget = this;
  }
}

现在我们需要创建渲染对象:

class RenderChildSize extends RenderBox
    with RenderObjectWithChildMixin<RenderBox> {
      var _widget = const ChildSize();
}

是一个特殊的 mixin,我们在使用 SingleChildRenderObjectWidget 时必须添加它。这个 mixin 将处理所有无聊的东西(请参阅下一篇文章)。几乎这种类型的每个帮助器都需要添加一些 mixin。您可以在每个帮助器的文档中找到这些要求。

下一步要做的是布局我们的 child:

class RenderChildSize ... {
  // ...
  var _lastSize = Size.zero;

  @override
  void performLayout() {
    final child = this.child;
    if (child != null) {
      child.layout(constraints, parentUsesSize: true);
      size = child.size;
    } else {
      size = constraints.smallest;
    }    if (_lastSize != size) {
      _lastSize = size;
      _widget.onChildSizeChanged?.call(_lastSize);
    }
  }
}

在布局过程中我们必须决定渲染对象的大小。要做到这一点,我们需要布置我们的孩子。渲染对象的大小将与子对象的大小相同(我们没有任何填充、边距等)。

这里有一些重要的注意事项:

  • 我们必须通过 parentUsesSize = true 子布局函数,以便事后得到它的大小。否则将抛出异常。感谢这个 flag Flutter 可以添加额外的优化
  • 可能有这样一种情况,即使我们有一个子 Widget,也不会有它的子 RenderObject。并不是所有的小工具都有渲染对象。在这种情况下,如果存在任何最近的嵌套渲染对象,Flutter 将尝试提供给我们

在这个方法中,我们还检查大小是否发生了变化,并在发生变化时调用回调。

我们需要做的最后一件事就是描绘我们的 child 和路由命中测试事件:

class RenderChildSize ... {
  // ...

  @override
  void paint(PaintingContext context, Offset offset) {
    final child = this.child;
    if (child != null) {
      context.paintChild(child, offset);
    }
  }

  @override
  bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
    return child?.hitTest(result, position: position) == true;
  }
}

结果如下:

image

© 猫哥

https://ducafecat.tech/

https://github.com/ducafecat

往期

开源

GetX Quick Start

https://github.com/ducafecat/getx_quick_start

新闻客户端

https://github.com/ducafecat/flutter_learn_news

strapi 手册译文

https://getstrapi.cn

微信讨论群 ducafecat

系列集合

译文

https://ducafecat.tech/categories/%E8%AF%91%E6%96%87/

开源项目

https://ducafecat.tech/categories/%E5%BC%80%E6%BA%90/

Dart 编程语言基础

https://space.bilibili.com/404904528/channel/detail?cid=111585

Flutter 零基础入门

https://space.bilibili.com/404904528/channel/detail?cid=123470

Flutter 实战从零开始 新闻客户端

https://space.bilibili.com/404904528/channel/detail?cid=106755

Flutter 组件开发

https://space.bilibili.com/404904528/channel/detail?cid=144262

Flutter Bloc

https://space.bilibili.com/404904528/channel/detail?cid=177519

Flutter Getx4

https://space.bilibili.com/404904528/channel/detail?cid=177514

Docker Yapi

https://space.bilibili.com/404904528/channel/detail?cid=130578

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容