一个完整的 ParentDataWidget 示例

简介: 一个完整的 ParentDataWidget 示例

image.png

ParentDataWidget 对于布局很重要,但平时用的少,一时间不知道它是做什么的。本文用一个自定义实例让你能够亲自体验 ParentDataWidget 作用和用法。

一句话来说 ParentDataWidget 就是给 RenderObject 的 parentData 提供数据的。但是光凭这一句还是很难理解 ParentDataWidget 是如何工作的。Flutter 有一个案例 Stack,但是代码很多。为了能聚焦,我实现了一个 MyStack,包含自定义的 MyPositioned 组件,就是一个简化版的 Stack,让大家了解 ParentDataWidget 的作用。

我们从需求开始。MyStack 要实现的功能是这样的。

  1. MyStack 在允许范围内尽量大。
  2. MyStack 的 children 都是 MyPositioned Widget。
  3. MyPositioned 可以通过 left 参数 MyStack 指定距 MyStack 左边缘的距离。

为了能使用 ParentDataWidget ,第一步是要先定义一类 extends ParentData 的类。这是 ParentDataWidget 要求的,不能直接用 ParentData 否则会报错。

我们直接从 ContainerBoxParentData 继承,ContainerBoxParentData 是继承自 ParentData 的。用ContainerBoxParentData的好处后,后面可以使用 ContainerRenderObjectMixinRenderBoxContainerDefaultsMixin。里面具体怎么实现不用关注,我们只是为了用最少的代码让示例能跑起来。

我们自定义的 MyStackParentData 很简单,只有一个参数,规定 MyPositioned 距离 MyStack 左边缘的距离。

class MyStackParentData extends ContainerBoxParentData<RenderBox> {
  MyStackParentData({this.left});
  double? left;
}
复制代码

然后我们定义 MyStack 和 MyPositioned。代码已经是最简了,刚好实现我们的需求。

class MyRenderStack extends RenderBox
    with
        ContainerRenderObjectMixin<RenderBox, MyStackParentData>,
        RenderBoxContainerDefaultsMixin<RenderBox, MyStackParentData> {
  @override
  void setupParentData(RenderBox child) {
    if (child.parentData is! MyStackParentData) {
      child.parentData = MyStackParentData();
    }
  }
  @override
  bool get sizedByParent => true;
  @override
  Size computeDryLayout(BoxConstraints constraints) {
    return constraints.biggest;
  }
  @override
  void paint(PaintingContext context, Offset offset) {
    paintStack(context, offset);
  }
  @protected
  void paintStack(PaintingContext context, Offset offset) {
    defaultPaint(context, offset);
  }
  @override
  void performLayout() {
    RenderBox? child = firstChild;
    while (child != null) {
      final MyStackParentData childParentData =
          child.parentData! as MyStackParentData;
      double x = 0, y = 0;
      if (childParentData.left != null) {
        x = childParentData.left!;
      }
      childParentData.offset = Offset(x, y);
      //因为只是演示,简化代码,直接固定宽高 100
      child.layout(constraints.tighten(width: 100, height: 100),
          parentUsesSize: false);
      child = childParentData.nextSibling;
    }
  }
}
class MyPositioned extends ParentDataWidget<MyStackParentData> {
  const MyPositioned({required this.left, Key? key, required Widget child})
      : super(key: key, child: child);
  final double? left;
  @override
  void applyParentData(RenderObject renderObject) {
    assert(renderObject.parentData is MyStackParentData);
    final MyStackParentData parentData =
        renderObject.parentData! as MyStackParentData;
    bool needsLayout = false;
    if (parentData.left != left) {
      parentData.left = left;
      needsLayout = true;
    }
    if (needsLayout) {
      final AbstractNode? targetParent = renderObject.parent;
      if (targetParent is RenderObject) {
        targetParent.markNeedsLayout();
      }
    }
  }
  @override
  Type get debugTypicalAncestorWidgetClass => MyStack;
}
复制代码

用的时候和其它 Widget 一样使用,可以尝试修改 left 值,看下效果。

MaterialApp(
      home: Scaffold(
            body: MyStack(
      children: [
        MyPositioned(
            left: 10,
            child: Container(
              width: 100,
              height: 100,
              color: Colors.green,
              child: Text('I AM 17'),
            ))
      ],
 )));
复制代码

MyPositioned 的 applyParentData 在 MyPositioned 生成的 element 执行 attachRenderObject 的时候就会执行。然后 MyStack  在 performLayout 的时候,可以拿到这些经过 MyPositioned 修改的 parentData,MyPositioned 根据这些 parentData 来做出对 child 的偏移。

setupParentData 这一步是必须的,而且是由父级,本例是 MyStack 完成,否则在 MyPositioned 执行 applyParentData 方法的时候拿不到正确类型的 ParentData。

最后附上 完整示例地址 ,copy 到 main.dart 就能执行。

目录
相关文章
|
Prometheus 监控 Kubernetes
阿里云容器服务GPU监控2.0基础篇2:监控NVLINK带宽
本系列相关文章:阿里云容器服务GPU监控2.0基础篇1:基本功能使用阿里云容器服务GPU监控2.0基础篇2:监控NVLINK带宽阿里云容器服务GPU监控2.0基础篇3:监控NVIDIA XID错误阿里云容器服务GPU监控2.0进阶篇1:剖析(Profiling)GPU使用情况必备知识阿里云容器服务GPU监控2.0进阶篇2:学会剖析(Profiling)GPU使用情况容器服务GPU监控2.0提供了监
2187 0
阿里云容器服务GPU监控2.0基础篇2:监控NVLINK带宽
|
Dart
Flutter|常用数据通信组件
在做需求时经常会遇到组件间通信,本篇汇总了几种常用的通信方式
466 57
Flutter 中使用 ICON
Flutter 中使用 ICON
680 5
Flutter 中使用 ICON
|
Dart API Android开发
Flutter 3.24 中的新功能
Flutter 3.24 已发布,带来诸多新功能与改进。此版本亮点包括:GPU 预览版,支持高级图形和 3D 场景;Web 应用多视图嵌入,增强应用灵活性;新增视频广告变现支持,助您增加收益。框架方面,引入新 Slivers 组件、Cupertino 库更新及 TreeView 小部件。引擎改进包括 Impeller 性能提升和默认图像质量优化。此外,新增重建统计功能,改善开发工具体验。此版本还支持 Swift 包管理器,并对 Navigator 等进行了破坏性变更。感谢社区贡献,期待您的精彩创作!
547 0
Flutter 3.24 中的新功能
|
机器学习/深度学习 编解码 人工智能
扩散模型失宠?端侧非自回归图像生成基础模型Meissonic登场,超越SDXL!
Meissonic是一种新型图像生成模型,采用非自回归的掩码图像建模(MIM)方法,在性能和效率上超越了当前最先进的扩散模型SDXL。其创新点包括改进的注意力机制、多尺度特征提取、先进位置编码策略和优化采样条件等,能够生成高质量、高分辨率图像。此外,Meissonic引入人类偏好评分和特征压缩层,提升图像质量和计算效率。尽管存在一些挑战,Meissonic为统一语言-视觉模型的发展提供了新思路,并在创意设计、虚拟现实等领域展现出广泛应用前景。
319 24
|
人工智能 JavaScript IDE
好消息,在 Visual Studio 中可以免费使用 GitHub Copilot 了!
好消息,在 Visual Studio 中可以免费使用 GitHub Copilot 了!
1429 11
|
Kubernetes Java 调度
Kubernetes中的Pod垃圾回收策略是什么
Kubernetes中的Pod垃圾回收策略是什么
|
前端开发 Linux iOS开发
【Flutter前端技术开发专栏】Flutter在桌面应用(Windows/macOS/Linux)的开发实践
【4月更文挑战第30天】Flutter扩展至桌面应用开发,允许开发者用同一代码库构建Windows、macOS和Linux应用,提高效率并保持平台一致性。创建桌面应用需指定目标平台,如`flutter create -t windows my_desktop_app`。开发中注意UI适配、性能优化、系统交互及测试部署。UI适配利用布局组件和`MediaQuery`,性能优化借助`PerformanceLogging`、`Isolate`和`compute`。
1264 0
【Flutter前端技术开发专栏】Flutter在桌面应用(Windows/macOS/Linux)的开发实践
|
容器
[flutter专题]详解AppBar小部件
AppBar 应用栏是各种应用程序中最常用的组件之一。它可用于容纳搜索字段、以及在页面之间导航的按钮,或者只是页面标题。由于它是一个如此常用的组件,因此 Flutter 为该功能提供了一个名为AppBar的专用小部件。
891 0
[flutter专题]详解AppBar小部件
|
JSON 前端开发 JavaScript
什么是json?json可以存放哪几种数据类型
什么是json?json可以存放哪几种数据类型
1015 2

热门文章

最新文章