可容纳多个组件, 需要自己制定排布的代理,可以高强度自定义组件的排布,实现普通布局无法达到的效果。布局王者,当之无愧。

相关组件

Flex Warp

Flow圆形排布

  1. <br />【children】 : 组件列表 【List<Widget><br />【delegate】 : 代理 【FlowDelegate】<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/326147/1589509260717-1a0a2af9-bc1a-4b9a-8d66-faa45b258e01.png#align=left&display=inline&height=321&margin=%5Bobject%20Object%5D&name=image.png&originHeight=321&originWidth=341&size=51121&status=done&style=none&width=341)
import 'dart:math';
import 'package:flutter/material.dart';
class CircleFlow extends StatelessWidget {
  final data = List.generate(
      16,
      (index) => index.isEven
          ? "assets/images/icon_head.png"
          : "assets/images/wy_300x200.jpg");

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 300,
      height: 300,
      alignment: Alignment.center,
      child: Flow(
        delegate: _CircleFlowDelegate(),
        children: data
            .map((e) => CircleAvatar(backgroundImage: AssetImage(e)))
            .toList(),
      ),
    );
  }
}

class _CircleFlowDelegate extends FlowDelegate {
  @override //绘制孩子的方法
  void paintChildren(FlowPaintingContext context) {
    double radius = context.size.shortestSide / 2;
    print(context.getChildSize(0));
    var count = context.childCount;
    var perRad = 2 * pi / count;
    for (int i = 0; i < count; i++) {
      var cSizeX = context.getChildSize(i).width / 2;
      var cSizeY = context.getChildSize(i).height / 2;

      var offsetX = (radius - cSizeX) * cos(i * perRad) + radius;
      var offsetY = (radius - cSizeY) * sin(i * perRad) + radius;
      context.paintChild(i,
          transform: Matrix4.translationValues(
              offsetX - cSizeX, offsetY - cSizeY, 0.0));
    }
  }

  @override
  bool shouldRepaint(FlowDelegate oldDelegate) {
    return true;
  }
}

Flow圆形与动画结合

   <br />通过动画来更改周围组件的位置实现效果<br />![197.gif](https://cdn.nlark.com/yuque/0/2020/gif/326147/1589509301897-1dd5c874-18d2-4433-8239-24591c9a88c3.gif#align=left&display=inline&height=332&margin=%5Bobject%20Object%5D&name=197.gif&originHeight=332&originWidth=370&size=863901&status=done&style=none&width=370)
import 'dart:math';
import 'package:flutter/material.dart';
class BurstFlow extends StatefulWidget {
  static final data = List.generate(
      16,
      (index) => index.isEven
          ? "assets/images/icon_head.png"
          : "assets/images/wy_300x200.jpg");
  static final show = Container(
      width: 300,
      height: 300,
      alignment: Alignment.center,
      child: BurstFlow(
          children: data
              .map((e) => CircleAvatar(backgroundImage: AssetImage(e)))
              .toList(),
          menu: CircleAvatar(
            backgroundImage: AssetImage('assets/images/icon_head.png'),
          )));

  final List<Widget> children;
  final Widget menu;

  BurstFlow({@required this.children, @required this.menu});

  @override
  _BurstFlowState createState() => _BurstFlowState();
}

class _BurstFlowState extends State<BurstFlow>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  double _rad = 0.0;
  bool _closed = true;

  @override
  void initState() {
    _controller = AnimationController(
        duration: Duration(milliseconds: 1000), vsync: this)
      ..addListener(() => setState(
          () => _rad = (_closed ? (_controller.value) : 1 - _controller.value)))
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          _closed = !_closed;
        }
      });
    super.initState();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Flow(
      delegate: _BurstFlowDelegate(_rad),
      children: [
        ...widget.children,
        InkWell(
            onTap: () {
              _controller.reset();
              _controller.forward();
            },
            child: widget.menu)
      ],
    );
  }
}

class _BurstFlowDelegate extends FlowDelegate {
  final double rad;

  _BurstFlowDelegate(this.rad);

  @override //绘制孩子的方法
  void paintChildren(FlowPaintingContext context) {
    double radius = context.size.shortestSide / 2;
    var count = context.childCount - 1;
    var perRad = 2 * pi / count;
    for (int i = 0; i < count; i++) {
      print(i);
      var cSizeX = context.getChildSize(i).width / 2;
      var cSizeY = context.getChildSize(i).height / 2;
      var offsetX = rad * (radius - cSizeX) * cos(i * perRad) + radius;
      var offsetY = rad * (radius - cSizeY) * sin(i * perRad) + radius;
      context.paintChild(i,
          transform: Matrix4.translationValues(
              offsetX - cSizeX, offsetY - cSizeY, 0.0));
    }
    context.paintChild(context.childCount - 1,
        transform: Matrix4.translationValues(
            radius - context.getChildSize(context.childCount - 1).width / 2,
            radius - context.getChildSize(context.childCount - 1).height / 2,
            0.0));
  }

  @override
  bool shouldRepaint(FlowDelegate oldDelegate) {
    return true;
  }
}