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

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容