Flutter 约束布局

413 阅读9分钟

配置插件依赖

image.png

设置组件大小

通过属性 childConstraints 实现

分别设置 约束布局一 和 约束布局二 大大小为:160 和 200

点击查看代码文件

class SummaryPageState extends State<SummaryPage1> {
  ConstraintId constraintId_1 = ConstraintId('ConstraintId_1');
  ConstraintId constraintId_2 = ConstraintId('ConstraintId_2');
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Summary'),
      ),
      body: ConstraintLayout(
        childConstraints: [
          // todo 约束布局一
          Constraint(
            id: constraintId_1,
            size: 160,
            bottomLeftTo: parent,
            zIndex: 20,
          ),
          // todo 约束布局二
          Constraint(
            id: constraintId_2,
            size: 200,
            topRightTo: parent,
            zIndex: 20,
          ),
        ],
        children: [
          // todo 约束布局一
          Container(
            color: Colors.redAccent,
            alignment: Alignment.center,
            child: const Text('约束布局ID:ConstraintId_1'),
          ).applyConstraintId(id: constraintId_1),
          // todo 约束布局二
          Container(
            color: Colors.blueAccent,
            alignment: Alignment.center,
            child: const Text('约束布局ID:ConstraintId_2'),
          ).applyConstraintId(id: constraintId_2),
        ],
      ),
    );
  }
}

位于某个视图底部

约束布局ID:ConstraintId_4 位于 ConstraintId_3 下面

点击查看代码文件

import 'package:flutter/material.dart';
import 'package:flutter_constraintlayout/flutter_constraintlayout.dart';

class SummaryPage2 extends StatefulWidget {
  const SummaryPage2({super.key});

  @override
  State<StatefulWidget> createState() {
    return SummaryPageState();
  }
}

class SummaryPageState extends State<SummaryPage2> {
  ConstraintId constraintId_3 = ConstraintId('ConstraintId_3');
  ConstraintId constraintId_4 = ConstraintId('ConstraintId_4');

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Summary'),
      ),
      body: ConstraintLayout(
        children: [
          // todo 约束布局三
          Container(
            color: Colors.blue,
            alignment: Alignment.center,
            child: const Text('约束布局ID:ConstraintId_3'),
          ).applyConstraint(
              id: constraintId_3,
              width: 200,
              height: 150,
              top: parent.top,
              left: parent.left,
              right: parent.right),

          // todo 约束布局四
          Container(
            color: Colors.orange,
            width: 200,
            height: 150,
            alignment: Alignment.center,
            child: const Text('约束布局ID:ConstraintId_4\n位于 ConstraintId_3 下面'),
          ).applyConstraint(
            id: constraintId_4,
            left: parent.left,
            top: constraintId_3.bottom,
          ),
        ],
      ),
    );
  }
}

位于某个视图底部中间

约束布局ID:ConstraintId_6 位于 ConstraintId_5 底部 中间 位置

点击查看代码文件

import 'package:flutter/material.dart';
import 'package:flutter_constraintlayout/flutter_constraintlayout.dart';

class SummaryPage3 extends StatefulWidget {
  const SummaryPage3({super.key});

  @override
  State<StatefulWidget> createState() {
    return SummaryPageState();
  }
}

class SummaryPageState extends State<SummaryPage3> {
  ConstraintId constraintId_5 = ConstraintId('ConstraintId_5');
  ConstraintId constraintId_6 = ConstraintId('ConstraintId_6');

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Summary'),
      ),
      body: ConstraintLayout(
        children: [
          // todo 约束布局三
          Container(
            color: Colors.blue,
            alignment: Alignment.center,
            child: const Text('约束布局ID:ConstraintId_5'),
          ).applyConstraint(
              id: constraintId_5,
              width: 200,
              height: 150,
              top: parent.top,
              left: parent.left,
              right: parent.right),

          // todo 约束布局四
          Container(
            color: Colors.orange,
            width: 200,
            height: 150,
            alignment: Alignment.center,
            child: const Text('约束布局ID:ConstraintId_6 \n 位于 ConstraintId_5 底部 中间 位置'),
          ).applyConstraint(
            id: constraintId_6,
            left: constraintId_5.left,
            top: constraintId_5.bottom,
            right: constraintId_5.right,
          ),
        ],
      ),
    );
  }
}

位于父视图中间

约束布局ID:ConstraintId_7 位于 父视图 中间 位置

点击查看代码文件

// todo © 国宝宝 2024年09月19日 周四 17:31
class SummaryPageState extends State<SummaryPage4> {
  ConstraintId constraintId_7 = ConstraintId('ConstraintId_7');

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Summary'),
      ),
      body: ConstraintLayout(
        children: [
          // todo 约束布局七
          Container(
            color: Colors.blue,
            alignment: Alignment.center,
            child: const Text('约束布局ID:ConstraintId_7 位于 父视图中间位置'),
          ).applyConstraint(
              id: constraintId_7,
              width: 200,
              height: 150,
              centerTo: parent),
        ],
      ),
    );
  }
}

拖拽约束布局

约束布局ID:ConstraintId_8 进行拖拽  x:x y:x y:y

点击查看代码文件

// todo © 国宝宝 2024年09月19日 周四 17:40
class SummaryPageState extends State<SummaryPage5> {
  ConstraintId constraintId_8 = ConstraintId('ConstraintId_8');
  double x = 0;
  double y = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Summary'),
      ),
      body: ConstraintLayout(
        children: [
          // todo 约束布局七
          GestureDetector(
            child: Container(
              color: Colors.orangeAccent,
              alignment: Alignment.center,
              child:  Text('约束布局ID:ConstraintId_8 进行拖拽\n x:$x y:$y'),
            ),
            onPanUpdate: (details) {
              setState(() {
                x += details.delta.dx;
                y += details.delta.dy;
              });
            },
          ).applyConstraint(
            id: constraintId_8,
            width: 200,
            height: 160,
            centerTo: parent,
            zIndex: 100,
            translate: Offset(x, y),
            translateConstraint: true,
          ),
        ],
      ),
    );
  }
}

约束布局 verticalBias 属性

中间垂直对齐 verticalBias 向上或者向下偏移的比例

点击查看代码文件

// todo © 国宝宝 2024年09月19日 周四 17:53
class SummaryPageState extends State<SummaryPage6> {
  ConstraintId constraintId_9 = ConstraintId('ConstraintId_9');
  ConstraintId constraintId_10 = ConstraintId('ConstraintId_10');
  ConstraintId constraintId_11 = ConstraintId('ConstraintId_11');

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Summary'),
      ),
      body: ConstraintLayout(
        childConstraints: [
          // todo 约束布局九
          Constraint(
            id: constraintId_9,
            width: 100,
            height: 240,
            centerLeftTo: parent,
            zIndex: 20,
          ),
        ],
        children: [
          // todo 约束布局九
          Container(
            color: Colors.blueAccent,
            alignment: Alignment.center,
            child: const Text('约束布局ID:ConstraintId_9'),
          ).applyConstraintId(id: constraintId_9),

          // todo 约束布局十
          Container(
            color: Colors.lightGreen,
            alignment: Alignment.center,
            child: const Text('约束布局ID:ConstraintId_10 中间对齐'),
          ).applyConstraint(
            id: constraintId_10,
            size: 120,
            margin: const EdgeInsets.only(right: 100),
            centerVerticalTo: constraintId_9,
            verticalBias: 0.5,// todo 偏移量0
            right: parent.right,
          ),

          // todo 约束布局十一
          Container(
            color: Colors.orangeAccent,
            alignment: Alignment.center,
            child: const Text('约束布局ID:ConstraintId_11 中间对齐 向上偏移'),
          ).applyConstraint(
            id: constraintId_11,
            width: 80,
            height: 200,
            centerVerticalTo: constraintId_9,
            verticalBias: 0.1,// todo 向上偏移量 0.4
            right: parent.right,
          ),
        ],
      ),
    );
  }
}

约束布局 matchConstraint 属性

matchConstraint 和 widthPercent (设置宽度百分比) 一起使用才有效

点击查看代码文件

// todo © 国宝宝 2024年09月20日 周四 12:35
class SummaryPageState extends State<SummaryPage7> {
  ConstraintId constraintId_9 = ConstraintId('ConstraintId_9');
  ConstraintId constraintId_10 = ConstraintId('ConstraintId_10');
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Summary'),
      ),
      body: ConstraintLayout(
        children: [
          Container(
            color: Colors.lightGreen,
            alignment: Alignment.center,
            child: const Text('约束布局ID:ConstraintId_9'),
          ).applyConstraint(
            id: constraintId_9,
            width: matchConstraint,// todo
            height: 200,
            widthPercent: 0.5,// todo 占 宽度百分之50
            bottomCenterTo: parent,
          ),
 
          Container(
            color: Colors.orangeAccent,
            alignment: Alignment.center,
            child: const Text('约束布局ID:ConstraintId_10'),
          ).applyConstraint(
            id: constraintId_10,
            width: matchConstraint,// todo
            height: 200,
            widthPercent: 0.7,// todo 占 宽度百分之70
            topCenterTo: parent,
          ),
        ],
      ),
    );
  }
}

约束ID.baseLine

以某个约束布局组件的ID为基准 来改变位置

点击查看代码文件

// todo © 国宝宝 2024年09月20日 周四 13:47
class SummaryPageState extends State<SummaryPage9> {
  ConstraintId constraintId_9 = ConstraintId('ConstraintId_9');
  ConstraintId constraintId_10 = ConstraintId('ConstraintId_10');
  ConstraintId constraintId_11 = ConstraintId('ConstraintId_11');
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Summary'),
      ),
      body: ConstraintLayout(
        children: [
          Container(
            color: Colors.lightGreen,
            alignment: Alignment.center,
            child: const Text('约束布局ID:ConstraintId_9'),
          ).applyConstraint(
            id: constraintId_9,
            height: matchConstraint, // todo
            width: 100,
            heightPercent: 0.3, // todo 占 高度百分之50
            centerLeftTo: parent,
          ),
 
          Container(
            color: Colors.orangeAccent,
            alignment: Alignment.center,
            child: const Text('约束布局ID:ConstraintId_10'),
          ).applyConstraint(
            id: constraintId_10,
            width: 100,
            height: 200,
            left: constraintId_9.right,// 位于 ConstraintId_9 右边
            baseline: constraintId_9.baseline,// 约束ID.baseLine
          ),
 
          Container(
            color: Colors.greenAccent,
            alignment: Alignment.center,
            child: const Text('约束布局ID:ConstraintId_11'),
          ).applyConstraint(
            id: constraintId_11,
            width: 100,
            height: 200,
            left: constraintId_10.right,// 位于 ConstraintId_10 右边
            baseline: constraintId_10.baseline,// 约束ID.baseLine
          ),
        ],
      ),
    );
  }
}

栅栏(屏障)Barrier

Barrier 是一个虚拟的组件并不会显示

点击查看代码文件

// todo © 国宝宝 2024年09月20日 周四 14:23

class SummaryPageState extends State<SummaryPage10> {

  ConstraintId constraintId_9 = ConstraintId('constraintId_9');

  ConstraintId constraintId_10 = ConstraintId('constraintId_10');

  ConstraintId barrier_11 = ConstraintId('barrier_11');



  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: const Text('Summary'),

      ),

      body: ConstraintLayout(

        children: [

          Container(

            color: Colors.greenAccent,

          ).applyConstraint(

            id: constraintId_9,

            size: 200,

            topLeftTo: parent,

          ),

          Container(

            color: Colors.orangeAccent,

          ).applyConstraint(

            id: constraintId_10,

            width: 200,

            height: matchConstraint,

            centerRightTo: parent,

            heightPercent: 0.5,

            verticalBias: 0,

          ),

          Barrier(

            id: barrier_11,

            direction: BarrierDirection.bottom, // todo 方向

            referencedIds: [

              constraintId_9,

              constraintId_10

            ], // todo 引用的子元素的 id,此处的 id 不能为相对 id

          ),

          const Text(

            '位于 栅栏(屏障)Barrier 底部',

            style: TextStyle(

              fontSize: 20,

              color: Colors.blue,

            ),

          ).applyConstraint(

            centerHorizontalTo: parent,

            top: barrier_11.bottom,

          )

        ],

      ),

    );

  }

}

通过 setId 设置相对位置

setId (1) 代表第 1 个子元素,setId(2)代表第 2 个子元素,以此类推

setId (-1) 代表 倒数 第 1 个子元素,setId(-2)代表 倒数 第 2 个子元素,以此类推

点击查看代码文件

// todo © 国宝宝 2024年09月20日 周四 14:49
class SummaryPageState extends State<SummaryPage11> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Summary'),
      ),
        body: ConstraintLayout(
          children: [
            Container(
              color: Colors.greenAccent,
            ).applyConstraint(
              size: 50,
              topLeftTo: parent,
              margin: const EdgeInsets.only(
                left: 20,
                top: 100,
              ),
            ),
            Container(
              color: Colors.yellowAccent,
            ).applyConstraint(
              size: 100,
              top: sId(1).bottom,// todo 相对于第一个子元素 的底部
              right: parent.right.margin(100),
            ),
            Container(
              color: Colors.purpleAccent,
            ).applyConstraint(
              size: 50,
              topRightTo: parent.rightMargin(20).topMargin(0),
            ),
            Container(
              color: Colors.cyanAccent,
            ).applyConstraint(
              size: 50,
              topRightTo: parent.rightMargin(20).topMargin(200),
            ),
            Container(
              color: Colors.blueAccent,
            ).applyConstraint(
              size: 50,
              // todo 相对于 倒数 第4个子元素 的底部中心
              outBottomCenterTo: sId(-4).topMargin(20),
            )
          ],
        )
    );
  }
}

引导线 Guideline

Guideline 有四个属性可以设置,分别是 horizontal、guidelinePercent、guidelineBegin、guidelineEnd。后三个属性都是相对于 parent 而言。
点击查看代码文件

// todo © 国宝宝 2024年09月20日 周四 16:41
class SummaryPageState extends State<SummaryPage12> {
  ConstraintId guideline = ConstraintId('guideline');
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Summary'),
        ),
        body: ConstraintLayout(
          children: [
            Container(
              color: const Color(0xFF005BBB),
            ).applyConstraint(
                width: matchParent,
                height: matchConstraint,
                top: parent.top,
                bottom: guideline.top,
                margin: const EdgeInsets.only(top: 150)),
            Guideline(
              id: guideline,
              horizontal: true, //todo 方向,true 为水平,false 为垂直
              guidelinePercent: 0.5,
            ),
            Container(
              color: const Color(0xFFFFD500),
            ).applyConstraint(
                width: matchParent,
                height: matchConstraint,
                top: guideline.bottom,
                bottom: parent.bottom,
                margin: const EdgeInsets.only(bottom: 150)),
            const Text(
              '引导线 Guideline',
              style: TextStyle(
                fontSize: 20,
                color: Colors.white,
              ),
              textAlign: TextAlign.center,
            ).applyConstraint(
              centerHorizontalTo: parent,
              top: guideline.bottom,// todo 位于 引导线 底部
            )
          ],
        ));
  }
}

zIndex 属性

zIndex 越大视图所处的层级就越高(试图层级越高视图就会位于其他视图上面)

点击查看代码文件

// todo © 国宝宝 2024年09月20日 周四 17:01
class SummaryPageState extends State<SummaryPage13> {
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Summary'),
        ),
        body: ConstraintLayout(
          children: [
            // todo 位于左上角 zIndex =3 位于最上面
            Container(
              color: Colors.blueAccent,
            ).applyConstraint(
              width: 160,
              height: 160,
              topLeftTo: parent,
              zIndex: 3,
            ),
 
            // todo 位于左上角 zIndex =1 位于最下面
            Container(
              color: Colors.greenAccent,
            ).applyConstraint(
                width: 160,
                height: 160,
                topLeftTo: parent,
                zIndex: 1,
                margin: const EdgeInsets.only(top: 30, left: 30)),
 
            // todo 位于左上角 zIndex =2 位于中间
            Container(
              color: Colors.redAccent,
            ).applyConstraint(
                width: 160,
                height: 160,
                topLeftTo: parent,
                zIndex: 2,
                margin: const EdgeInsets.only(top: 60, left: 60)),
          ],
        ));
  }
}

 clickPadding

改变视图点击区域无需改变视图本身大小

点击查看代码文件

// todo © 国宝宝 2024年09月20日 周四 17:21
class SummaryPageState extends State<SummaryPage14> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Summary'),
        ),
        body: ConstraintLayout(
          children: [
            // todo 位于 父视图中间
            GestureDetector(
              onTap: () {
                debugPrint('当前区域是否被点击......');
              },
              child: Container(
                color: Colors.blueAccent,
              ),
            ).applyConstraint(
                width: 160,
                height: 160,
                centerTo: parent,
                clickPadding: const EdgeInsets.all(30)),
          ],
        ));
  }
}

translate 平移布局

对子视图位置进行平移

点击查看代码文件

// todo © 国宝宝 2024年09月20日 周四 17:43
class SummaryPageState extends State<SummaryPage15> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Summary'),
        ),
        body: ConstraintLayout(
          children: [
            Container(
              color: Colors.blueAccent,
            ).applyConstraint(
              width: 160,
              height: 160,
              translate: const Offset(80, 0), // todo 向右边平移动 80 距离
              centerTo: parent,// todo 位于 父视图 中间位置
            ),
 
            // todo 位于 父视图中间
            Container(
              color: Colors.greenAccent,
            ).applyConstraint(
              width: 160,
              height: 160,
              translate: const Offset(0, -200), // todo 向上边平移动 200 距离
              centerTo: parent,// todo 位于 父视图 中间位置
            ),
          ],
        ));
  }
}

根据宽高比例布局

宽高比例属性 widthHeightRatio,根据宽高比例来设置视图高度或者宽度

点击查看代码文件

// todo © 国宝宝 2024年09月20日 周四 17:52
class SummaryPageState extends State<SummaryPage16> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Summary'),
        ),
        body: ConstraintLayout(
          children: [
            Container(
              color: Colors.blueAccent,
            ).applyConstraint(
                width: 100,
                height: matchConstraint,
                widthHeightRatio: 2 / 5, // todo 宽高比例 2/5
                centerLeftTo: parent // todo 位于 父视图 左边 中间位置
                ),
            Container(
              color: Colors.greenAccent,
            ).applyConstraint(
                width: 100,
                height: matchConstraint,
                widthHeightRatio: 2 / 3, // todo 宽高比例 2/3
                centerRightTo: parent // todo 位于 父视图 右边 中间位置
                ),
          ],
        ));
  }
}

网格列表

点击查看代码文件

// todo © 国宝宝 2024年09月21日 周四 19:22
class SummaryPageState extends State<SummaryPage17> {
  List<Color> colors = [    Colors.yellowAccent,    Colors.indigoAccent,    Colors.orangeAccent,    Colors.purpleAccent,    Colors.indigoAccent,    Colors.amberAccent,    Colors.lightBlueAccent  ];
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Summary'),
        ),
        body: SingleChildScrollView(
          child: ConstraintLayout(
            children: [
              ...constraintGrid(
                  id: ConstraintId('grid'),
                  left: parent.left,
                  top: parent.top,
                  itemCount: 160,
                  columnCount: 6,
                  itemSize: 60,
                  itemBuilder: (index, _, __) {
                    return Container(
                      color: colors[index % colors.length],
                    );
                  },
                  itemMarginBuilder: (index, _, __) {
                    return const EdgeInsets.only(
                      left: 10,
                      top: 10,
                    );
                  })
            ],
          ),
        ));
  }
}

wrapContent

不限制视图大小,自适应

点击查看代码文件

// todo © 国宝宝 2024年09月21日 周四 20:09
class SummaryPageState extends State<SummaryPage18> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Summary'),
        ),
        body: Center(
          child: ConstraintLayout(
            size: wrapContent,
            children: [
              Container(
                color: Colors.blueAccent,
              ).applyConstraint(
                size: matchParent,
              ),
              Container(
                color: Colors.yellowAccent,
              ).applyConstraint(
                width: 250,
                height: 150,
                topLeftTo: parent,
                margin: const EdgeInsets.only(
                  top: 10,
                  left: 10,
                ),
              ),
              Container(
                color: Colors.orangeAccent,
              ).applyConstraint(
                size: 50,
                topRightTo: parent,
              ),
              const Text(
                'wrapContent',
              ).applyConstraint(
                outBottomRightTo: rId(1),
              ),
              Container(
                color: Colors.redAccent,
              ).applyConstraint(
                size: 50,
                centerVerticalTo: parent,
                right: rId(2).left,
              )
            ],
          ),
        ));
  }
}

约束布局案例