Flutter自定义MultiChildRenderObjectWidget实现圆环布局效果

Posted Code-Porter

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter自定义MultiChildRenderObjectWidget实现圆环布局效果相关的知识,希望对你有一定的参考价值。

一、本文主要是学习巩固一下自定义RenderObject这一块内容,用所了解到的知识实现一个圆环布局效果

二、效果图

.
根据自组件的数量,平均将所有的子组件摆放在矩形的内切圆上。

三、自定义Layout步骤

  1. 创建CircleLayoutWidget类继承MultiChildRenderObjectWidget
  2. 创建CircleLayoutData类继承ContainerBoxParentData
  3. 创建CircleLayoutRenderBox类继承RenderBox,同时混入ContainerRenderObjectMixinRenderBoxContainerDefaultsMixin

关于上面两个混入,在开头引入的文章链接中已经详细说明了

如下代码

class CircleLayoutWidget extends MultiChildRenderObjectWidget 
  ///圆环布局的大小
  final double size;

  CircleLayoutWidget(
    required this.size,
    required List<Widget> children,
    Key? key,
  ) : super(key: key, children: children);

  
  RenderObject createRenderObject(BuildContext context) 
    return CircleLayoutRenderBox(size);
  


///每个child的数据
class CircleLayoutData extends ContainerBoxParentData<RenderBox> 

四、这里重点讲一下自定义RenderBox这块操作

  1. 需要混入上面说到的两个mixin
  2. 重写setupParentData函数为每一个child设置数据
  3. 重写performLayout函数进行布局大小设置、逻辑编写;同时需要对每一个child进行layout

    child.layout(constraints, parentUsesSize: true);

  4. 重写paint函数进行child的绘制
class CircleLayoutRenderBox extends RenderBox
    with
        ContainerRenderObjectMixin<RenderBox, CircleLayoutData>,
        RenderBoxContainerDefaultsMixin<RenderBox, CircleLayoutData> 
  final double layoutSize;

  CircleLayoutRenderBox(this.layoutSize);

  ///step 1
  
  void setupParentData(RenderObject child) 
    if (child.parentData is! CircleLayoutData) 
      child.parentData = CircleLayoutData();
    
  

  
  void performLayout() 
    size = Size(layoutSize, layoutSize);
    ///省略部分代码...
  

  
  void paint(PaintingContext context, Offset offset) 
    defaultPaint(context, offset);
  

五、这里说下关于每一个child在圆环上的坐标计算


通过画图来理解各个点位的关系,可以得到以下信息

  • 半径r是画布大小的一半r = layoutSize / 2
  • 角a根据子组件数量进行计算∠a = 2 * pi / childCount
  • 有了上面俩个条件就可以通过三角函数计算出F点的坐标F(x,y) = (sin(∠a) * r, cos(∠a) * r)

void performLayout() 
  final double r = layoutSize / 2;
  ///起始角度0
  double angle = 0;
  int count = 0;
  RenderBox? child = firstChild;
  while (child != null) 
    child.layout(constraints, parentUsesSize: true);
    final CircleLayoutData parentData = child.parentData! as CircleLayoutData;
    ///计算当前child所在的角度
    angle = count * 2 * pi / childCount;
    parentData.offset = Offset(
      sin(angle) * r - child.size.width / 2 + r,
      cos(angle) * r - child.size.height / 2 + r,
    );
    ///下一个child
    child = parentData.nextSibling;
    count++;
  

注意:最后还需要对F点进行(x,y) = (x - child.size.width / 2 + r ,y - child.size.height / 2 +r)运算,这是为了让组件的中心在圆弧上同时将坐标原点移动到画布中心

本文所有源码下载

以上是关于Flutter自定义MultiChildRenderObjectWidget实现圆环布局效果的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 自定义组件实战

flutter自定义组件

Flutter 自定义日历

Flutter Cards:如何在 Flutter 中创建自定义卡片小部件

Flutter中如何实现自定义对话框?

Flutter AppBar基本用法、TabBar基本用法、自定义TabBar