flutter - 布局必备手册

flutter布局

相信很多同学,都是在wendux的flutter中文社区中学习入门的,但是我在看完之后,使用 flutter 开发项目时,布局依然遇到了一些困难,不知道该用哪种widget,所以详细记录了一下项目必用的布局内容。

写作风格参考了 阮一峰的flex布局

目录

  • Row and Column 【横向,纵向布局】
  • IntrinsicWidth and IntrinsicHeight 【宽度,高度 对齐布局】
  • Stack 【绝对定位布局】
  • Expanded 【绝对定位布局】
  • ConstrainedBox 【容器尺寸修饰】
  • Container 【容器】
  • decoration: BoxDecoration 【容器样式修饰】
    • image: DecorationImage 【背景图片】
    • border: Border 【边框】
    • borderRadius: BorderRadius 【边框圆角】
    • shape: BoxShape 【容器形状,三角形,菱形,梯形…】
    • boxShadow: List 【容器阴影,左上右下】
    • gradient: RadialGradient 【渐变】
    • backgroundBlendMode: BlendMode 【背景滤镜】
  • 最新Widget:
    • shape: BeveledRectangleBorder 【形状修饰】
  • SizedBox 【容器尺寸】
  • SafeArea 【是否使用沉浸式头部】

Row and Column

MainAxisAlignment,控制容器中的元素如何在当前容器中布局,例如居中,从头开始,从尾开始.


MainAxisAlignment.start

Row /*or Column*/( 
  mainAxisAlignment: MainAxisAlignment.start,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),

在这里插入图片描述


MainAxisAlignment.center

Row /*or Column*/( 
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),

在这里插入图片描述


MainAxisAlignment.end

Row /*or Column*/( 
  mainAxisAlignment: MainAxisAlignment.end,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),

在这里插入图片描述


MainAxisAlignment.spaceBetween

Row /*or Column*/( 
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),

在这里插入图片描述


MainAxisAlignment.spaceEvenly (两边间距相同)

Row /*or Column*/( 
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),

在这里插入图片描述


MainAxisAlignment.spaceAround (均匀分布)

Row /*or Column*/( 
  mainAxisAlignment: MainAxisAlignment.spaceAround,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),

在这里插入图片描述


baseline 用于对齐不同文本基线

Row(
  crossAxisAlignment: CrossAxisAlignment.baseline,
  textBaseline: TextBaseline.alphabetic,
  children: <Widget>[
    Text(
      'Baseline',
      style: Theme.of(context).textTheme.display3,
    ),
    Text(
      'Baseline',
      style: Theme.of(context).textTheme.body1,
    ),
  ],
),

在这里插入图片描述

CrossAxisAlignment

  • RowColumn 非常常用,需要多个元素进行排布时,必然会用到,他们都有两条轴,相互垂直。(主轴方向,自动占满可用区域,后面示例可发现)

  • 对于 Row,主轴是横向的(x轴),副轴是纵向的(y轴)

  • 对于 Column,主轴是纵向的(y轴),副轴是横向的(x轴)

MainAxisAlignment 用于控制主轴方向上元素排列

CrossAxisAlignment 用于控制 副轴 上元素排列


这里使用 Row 以及 两小,一大 图标做示例,注意 副轴方向上的小星星位置.


CrossAxisAlignment.start

Row /*or Column*/( 
  crossAxisAlignment: CrossAxisAlignment.start,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 200),
    Icon(Icons.star, size: 50),
  ],
),

在这里插入图片描述


CrossAxisAlignment.center

Row /*or Column*/( 
  crossAxisAlignment: CrossAxisAlignment.center,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 200),
    Icon(Icons.star, size: 50),
  ],
),

在这里插入图片描述


CrossAxisAlignment.end

Row /*or Column*/( 
  crossAxisAlignment: CrossAxisAlignment.end,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 200),
    Icon(Icons.star, size: 50),
  ],
),

在这里插入图片描述


CrossAxisAlignment.stretch (容器副轴延展并占满可用区域,主轴会自动占满)

Row /*or Column*/( 
  crossAxisAlignment: CrossAxisAlignment.stretch,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 200),
    Icon(Icons.star, size: 50),
  ],
),

在这里插入图片描述


MainAxisSize

MainAxisSize用于控制容器在主轴方向上是否占满可用区域,默认为MainAxisSize.max,表示主轴方向会自动占满

Row /*or Column*/( 
  mainAxisSize: MainAxisSize.max,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 200),
    Icon(Icons.star, size: 50),
  ],
),

在这里插入图片描述

在这里插入图片描述

IntrinsicWidth and IntrinsicHeight 元素相等宽高

对于一个排列中的多个不等宽(高)元素,如何让他们等宽(高)呢?

示例:

/// 多个RaisedButton元素,具有不同的文本内容,占据的宽度不同
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('IntrinsicWidth')),
    body: Center(
      child: Column(
        children: <Widget>[
          RaisedButton(
            onPressed: () {},
            child: Text('Short'),
          ),
          RaisedButton(
            onPressed: () {},
            child: Text('A bit Longer'),
          ),
          RaisedButton(
            onPressed: () {},
            child: Text('The Longest text button'),
          ),
        ],
      ),
    ),
  );

在这里插入图片描述

使用IntrinsicWidth 进行包裹后,即可让他们等宽。(等高同理,使用IntrinsicHeight)。效果如下:

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('IntrinsicWidth')),
    body: Center(
      child: IntrinsicWidth(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            RaisedButton(
              onPressed: () {},
              child: Text('Short'),
            ),
            RaisedButton(
              onPressed: () {},
              child: Text('A bit Longer'),
            ),
            RaisedButton(
              onPressed: () {},
              child: Text('The Longest text button'),
            ),
          ],
        ),
      ),
    ),
  );
}

在这里插入图片描述

对于一个组件中需要不规则定位的元素如何处理?

对于需要不规则,特殊定位的组件,使用 stack + position 进行布局

@override
Widget build(BuildContext context) {
  Widget main = Scaffold(
    appBar: AppBar(title: Text('Stack')),
  );

  return Stack(
    fit: StackFit.expand,
    children: <Widget>[
      main,
      Banner(
        message: "Top Start",
        location: BannerLocation.topStart,
      ),
      Banner(
        message: "Top End",
        location: BannerLocation.topEnd,
      ),
      Banner(
        message: "Bottom Start",
        location: BannerLocation.bottomStart,
      ),
      Banner(
        message: "Bottom End",
        location: BannerLocation.bottomEnd,
      ),
    ],
  );
}

在这里插入图片描述


下面这个示例,使用Posotion进行绝对定位,需要知道定位元素的 top / left值。(一个定位在左上角,一个定位在右下角)

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Stack')),
    body: Stack(
      fit: StackFit.expand,
      children: <Widget>[
        Material(color: Colors.yellowAccent),
        Positioned(
          top: 0,
          left: 0,
          child: Icon(Icons.star, size: 50),
        ),
        Positioned(
          top: 340,
          left: 250,
          child: Icon(Icons.call, size: 50),
        ),
      ],
    ),
  );
}

在这里插入图片描述


如果不想去猜 top/left 的值,可以使用 layoutbuilder,它的builder中自带一个参数,可以获取当前容器的宽高。

Widget build(BuildContext context) {
  const iconSize = 50;
  return Scaffold(
    appBar: AppBar(title: Text('Stack with LayoutBuilder')),
    body: LayoutBuilder(
      builder: (context, constraints) =>
        Stack(
          fit: StackFit.expand,
          children: <Widget>[
            Material(color: Colors.yellowAccent),
            Positioned(
              top: 0,
              child: Icon(Icons.star, size: iconSize),
            ),
            Positioned(
              top: constraints.maxHeight - iconSize,
              left: constraints.maxWidth - iconSize,
              child: Icon(Icons.call, size: iconSize),
            ),
          ],
        ),
    ),
  );
}

在这里插入图片描述

Expand

Expand 常用于需要按照一定比例占满某些区域。比如两栏,三栏布局,其中px值是可以这样用的.(flex可以作为px的值进行设置)

Row(
  children: <Widget>[
    Expanded(
      child: Container(
        decoration: const BoxDecoration(color: Colors.red),
      ),
      flex: 300,
    ),
    Expanded(
      child: Container(
        decoration: const BoxDecoration(color: Colors.green),
      ),
      flex: 200,
    ),
    Expanded(
      child: Container(
        decoration: const BoxDecoration(color: Colors.blue),
      ),
      flex: 100,
    ),
  ],
),

在这里插入图片描述

Container

Container是最为常用的容器,它拥有多种属性。


Container 单纯作为一个容器

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Container as a layout')),
    body: Container(
      color: Colors.yellowAccent,
      child: Text("Hi"),
    ),
  );
}

在这里插入图片描述


Container 撑满整个可用区域

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Container as a layout')),
    body: Container(
      height: double.infinity,
      width: double.infinity,
      color: Colors.yellowAccent,
      child: Text("Hi"),
    ),
  );
}

在这里插入图片描述


Container 作为一个 decoration 装饰容器。颜色通过 BoxDecoration 属性去定义。
一个好的写法是:decoration 属性放在 child 之前, foregroundDecoration 属性放在 child之前

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Container.foregroundDecoration')),
    body: Container(
      height: double.infinity,
      width: double.infinity,
      decoration: BoxDecoration(color: Colors.yellowAccent),
      foregroundDecoration: BoxDecoration(color: Colors.red.withOpacity(0.5)),
      child: Text("Hi"),
    ),
  );
}

在这里插入图片描述


Container 有一个属性:transform,可以直接控制容器转换,而不必使用 Tansform 组件

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Container.transform')),
    body: Container(
      height: 300,
      width: 300,
      transform: Matrix4.rotationZ(pi / 4),
      decoration: BoxDecoration(color: Colors.yellowAccent),
      child: Text(
        "Hi",
        textAlign: TextAlign.center,
      ),
    ),
  );
}

在这里插入图片描述


Container将一张图片设置为背景

Scaffold(
  appBar: AppBar(title: Text('image: DecorationImage')),
  body: Center(
    child: Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
        color: Colors.yellow,
        image: DecorationImage(
          fit: BoxFit.fitWidth,
          image: NetworkImage(
            'https://flutter.io/images/catalog-widget-placeholder.png',
          ),
        ),
      ),
    ),
  ),
);

在这里插入图片描述


Container 添加边框

Scaffold(
  appBar: AppBar(title: Text('border: Border')),
  body: Center(
    child: Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
        color: Colors.yellow,
        border: Border.all(color: Colors.black, width: 3),
      ),
    ),
  ),
);

在这里插入图片描述


Container 添加圆角边框

Scaffold(
  appBar: AppBar(title: Text('borderRadius: BorderRadius')),
  body: Center(
    child: Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
        color: Colors.yellow,
        border: Border.all(color: Colors.black, width: 3),
        borderRadius: BorderRadius.all(Radius.circular(18)),
      ),
    ),
  ),
);

在这里插入图片描述


Container 设置圆形: shape: BoxShape

Scaffold(
  appBar: AppBar(title: Text('shape: BoxShape')),
  body: Center(
    child: Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
        color: Colors.yellow,
        shape: BoxShape.circle,
      ),
    ),
  ),
);

在这里插入图片描述


Container 添加阴影 :boxShadow: List<BoxShadow>

Scaffold(
  appBar: AppBar(title: Text('boxShadow: List<BoxShadow>')),
  body: Center(
    child: Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
        color: Colors.yellow,
        boxShadow: const [
          BoxShadow(blurRadius: 10),
        ],
      ),
    ),
  ),
);

在这里插入图片描述


Container 添加渐变 :boxShadow: List<BoxShadow>
渐变有3种 LinearGradient(线性渐变), RadialGradient(曲线渐变), SweepGradient (?).

Scaffold(
  appBar: AppBar(title: Text('gradient: LinearGradient')),
  body: Center(
    child: Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
        gradient: LinearGradient(
          colors: const [
            Colors.red,
            Colors.blue,
          ],
        ),
      ),
    ),
  ),
);

在这里插入图片描述


Scaffold(
  appBar: AppBar(title: Text('gradient: RadialGradient')),
  body: Center(
    child: Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
        gradient: RadialGradient(
          colors: const [Colors.yellow, Colors.blue],
          stops: const [0.4, 1.0],
        ),
      ),
    ),
  ),
);

在这里插入图片描述


Scaffold(
  appBar: AppBar(title: Text('gradient: SweepGradient')),
  body: Center(
    child: Container(
      height: 200,
      width: 200,
      decoration: BoxDecoration(
        gradient: SweepGradient(
          colors: const [
            Colors.blue,
            Colors.green,
            Colors.yellow,
            Colors.red,
            Colors.blue,
          ],
          stops: const [0.0, 0.25, 0.5, 0.75, 1.0],
        ),
      ),
    ),
  ),
);

在这里插入图片描述

SizeBox (布局重点)

当几个小组件之间需要 Padding 边距,Margin间隔 时,使用Sizebox可以高效复用布局.

Column(
  children: <Widget>[
    Icon(Icons.star, size: 50),
    const SizedBox(height: 100),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),

在这里插入图片描述

SizeBox 控制一个元素 显示/隐藏

Widget build(BuildContext context) {
  bool isVisible = true;
  return Scaffold(
    appBar: AppBar(
      title: Text('isVisible = $isVisible'),
    ),
    body: isVisible 
      ? Icon(Icons.star, size: 150) 
      : const SizedBox(),
  );
}

在这里插入图片描述

SafeArea

SafeArea 用于控制设备顶部的工具栏。包裹整个容器即可。

Widget build(BuildContext context) {
  return Material(
    color: Colors.blue,
    child: SafeArea(
      child: SizedBox.expand(
        child: Card(color: Colors.yellowAccent),
      ),
    ),
  );
}

在这里插入图片描述

Offstage 控制元素的显示和隐藏

bool _value = false;
Offstage(
    offstage: _value,
    child: Image.asset("order/ic_check", width: 16.0, height: 16.0)),
参考资料

api.flutter.dev 英文文档

flutter 中文文档

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值