当前位置:   article > 正文

Flutter 笔记 | Flutter 容器组件

Flutter 笔记 | Flutter 容器组件

Padding

这个组件最能体现Flutter与其他UI框架的一个不同点,那就是在其他UI框架中padding基本都是作为组件的一个属性,例如在html中常见的布局标签都有padding属性,Android中也是如此,但是在Flutter中组件并没有一个叫padding的属性,相反,提供了一个叫作 PaddingWidget 组件。(体现了Flutter中万物皆Widget的理念)

Padding可以给其子节点添加填充(留白),和边距效果类似。

下面是Padding的定义:

Padding({
   
  ...
  EdgeInsetsGeometry padding,
  Widget child,
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

可以看到它的padding参数需要传递一个EdgeInsetsGeometry类型,这是一个抽象类,开发中,我们一般都使用EdgeInsets类,它是EdgeInsetsGeometry的一个子类,定义了一些设置填充的便捷方法。

下面是EdgeInsets提供的便捷方法:

  • fromLTRB(left, top, right, bottom):分别指定四个方向的填充。
  • all(value) : 所有方向均使用相同数值的填充。
  • only({left, top, right ,bottom }):可以设置具体某个方向的填充(可以同时指定多个方向)。
  • symmetric({ vertical, horizontal }):用于设置对称方向的填充,verticaltopbottomhorizontalleftright

下面的示例展示了EdgeInsets的不同用法:

class PaddingTestRoute extends StatelessWidget {
   
  const PaddingTestRoute({
   Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
   
    return Padding(
      //上下左右各添加16像素补白
      padding: const EdgeInsets.all(16),
      child: Column(
        //显式指定对齐方式为左对齐,排除对齐干扰
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisSize: MainAxisSize.min,
        children: const <Widget>[
          Padding(
            //左边添加8像素补白
            padding: EdgeInsets.only(left: 8),
            child: Text("Hello world"),
          ),
          Padding(
            //上下各添加8像素补白
            padding: EdgeInsets.symmetric(vertical: 8),
            child: Text("I am Jack"),
          ),
          Padding(
            // 分别指定四个方向的补白
            padding: EdgeInsets.fromLTRB(20, 0, 20, 20),
            child: Text("Your friend"),
          )
        ],
      ),
    );
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

运行效果:

在这里插入图片描述

Container

Container是一个组合类容器,它本身不对应具体的RenderObject,它是DecoratedBoxConstrainedBoxTransformPaddingAlign等组件组合的一个多功能容器,所以我们只需通过一个Container组件可以实现同时需要装饰、变换、限制的场景。下面是Container的定义:

Container({
   
  this.alignment,
  this.padding, //容器内补白,属于decoration的装饰范围
  Color color, // 背景色
  Decoration decoration, // 背景装饰
  Decoration foregroundDecoration, //前景装饰
  double width,//容器的宽度
  double height, //容器的高度
  BoxConstraints constraints, //容器大小的限制条件
  this.margin,//容器外补白,不属于decoration的装饰范围
  this.transform, //变换
  this.child,
  ...
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
属性 说明
alignment topCenter:顶部居中对齐
topLeft:顶部左对齐
topRight:顶部右对齐
center:水平垂直居中对齐
centerLeft:垂直居中水平居左对齐
centerRight:垂直居中水平居右对齐
bottomCenter:底部居中对齐
bottomLeft:底部居左对齐
bottomRight:底部居右对齐
decoration BoxDecoration背景装饰
foregroundDecoration BoxDecoration前景装饰
margin 表示Container与外部其他组件的距离。 如margin:EdgeInsets.all(20.0),
padding Container的内边距,指Container边缘与Child之间的距离,如padding:EdgeInsets.all(10.0)
transform 让Container容易进行一些旋转之类的,如transform: Matrix4.rotationZ(0.2)
height 容器高度
width 容器宽度
child 容器子元素
color 背景色
constraints 容器大小的限制条件

有两点需要特别注意:

  1. 容器的大小可以通过widthheight属性来指定,也可以通过constraints来指定;如果它们同时存在时,widthheight优先。实际上Container内部会根据widthheight来生成一个constraints
  2. colordecoration是互斥的,如果同时设置它们则会报错!实际上,当指定color时,Container内会自动创建一个decoration

示例代码1:

class MyCard extends StatelessWidget {
   
  const MyCard({
   Key? key}) : super(key: key);
  
  Widget build(BuildContext context) {
   
    return Center(
      child: Container(
        width: 200,
        height: 200,
        color: Colors.blue,
        alignment: Alignment.center,
        child: const Text("你好Flutter", style: TextStyle(fontSize: 20)),
      ),
    );
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

效果:

在这里插入图片描述

示例代码2:

Container(
    margin: const EdgeInsets.only(top: 50.0, left: 120.0),
    constraints: const BoxConstraints.tightFor(width: 200.0, height: 150.0), // 卡片大小
    decoration: BoxDecoration(  // 背景装饰
      gradient: const RadialGradient( // 背景径向渐变
        colors: [Colors.red, Colors.orange],
        center: Alignment.topLeft,
        radius: .98,
      ),
      // LinearGradient 是背景线性渐变
      // gradient: LinearGradient( colors: [Colors.red, Colors.orange]),
      boxShadow: const [ //卡片阴影
        BoxShadow(
          color: Colors.black54,
          offset: Offset(2.0, 2.0),
          blurRadius: 4.0,
        )
      ],
      border: Border.all(color: Colors.red, width: 2.0), 
      borderRadius: BorderRadius.circular(8.0), // 圆角 ,
      color: Colors.blue, 
    ),
    transform: Matrix4.rotationZ(.2),//卡片倾斜变换
    alignment: Alignment.center, //卡片内文字居中
    child: const Text("5.20", style: TextStyle(color: Colors.white, fontSize: 40.0),), //卡片文字
),
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

效果:

在这里插入图片描述

通过Container创建一个按钮:

class MyButton extends StatelessWidget {
   
  const MyButton({
   Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
   
    return Container(
      alignment: Alignment.center,
      width: 200,
      height: 40,
      // margin: const EdgeInsets.all(10),   //四周margin
      margin: const EdgeInsets.fromLTRB(0, 40, 0, 0),
      // padding: const EdgeInsets.fromLTRB(40, 0, 0, 0),
      decoration: BoxDecoration(
        color: Colors.blue,
        borderRadius:BorderRadius.circular(20)
      ),
      child: const Text("按钮",style: TextStyle(
        color: Colors.white,
        fontSize: 20
      )),
    );
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

效果:

在这里插入图片描述

可以看到Container具备多种组件的功能,通过查看Container源码,我们会很容易发现它是多种组件组合而成的。在Flutter中,Container组件也正是组合优先于继承的实例。

Padding和Margin

接下来我们来看一下Container组件marginpadding属性的区别:

...
Container(
  margin: EdgeInsets.all(20.0), //容器外补白
  color: Colors.orange,
  child: Text("Hello world!"),
),
Container(
  padding: EdgeInsets.all(20.0), //容器内补白
  color: Colors.orange,
  child: Text("Hello world!"),
),
...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

效果:

在这里插入图片描述

可以发现,直观的感觉就是margin的留白是在容器外部,而padding的留白是在容器内部,需要记住这个差异。事实上,Containermarginpadding都是通过Padding 组件来实现的,上面的示例代码实际上等价于:

...
Padding(
  padding: EdgeInsets.all(20.0),
  child: DecoratedBox(
    decoration: BoxDecoration(color: Colors.orange),
    child: Text("Hello world!"),
  ),
),
DecoratedBox(
  decoration: BoxDecoration(color: Colors.orange),
  child: Padding(
    padding: const EdgeInsets.all(20.0),
    child: Text("Hello world!"),
  ),
),
...    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

double.infinity 和 double.maxFinite

double.infinitydouble.maxFinite可以让当前元素的width或者height达到父元素的尺寸。

static const double nan = 0.0 / 0.0;
static const double infinity = 1.0 / 0.0;
static const double negativeInfinity = -infinity;
static const double minPositive = 5e-324;
static const double maxFinite = 1.7976931348623157e+308;
  • 1
  • 2
  • 3
  • 4
  • 5

如下代码可以让Container铺满整个屏幕:

Widget build(BuildContext context) {
   
  return Container(
    height: double.infinity,
    width: double.infinity,
    color: Colors.black26,
    child: const Column(
      crossAxisAlignment: CrossAxisAlignment.center,
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        Icon(Icons.home, color: Colors.red),
        Icon(Icons.search, color: Colors.blue),
        Icon(Icons.send, color: Colors.orange),
      ],
    ),
  );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

如下代码可以让Container的宽度和高度等于父元素的宽度高度:

class HomePage extends StatelessWidget {
   
  const HomePage({
   Key? key}) : super(key: key);
  
  Widget build(BuildContext context) {
   
    return Container(
      height: 400,
      width: 600,
      color: Colors.red,
      child: Container(
        height: double.maxFinite,
        width: double.infinity,
        color: Colors.black26,
        child: const Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            Icon(Icons.home, color: Colors.red),
            Icon(Icons.search, color: Colors.blue),
            Icon(Icons.send, color: Colors.orange),
          ],
        ),
      ),
    );
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

DecoratedBox

DecoratedBox可以在其子组件绘制前(或后)绘制一些装饰(Decoration),如背景、边框、渐变等。DecoratedBox定义如下:

const DecoratedBox({
   
  Decoration decoration,
  DecorationPosition position = DecorationPosition.background,
  Widget? child
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • decoration:代表将要绘制的装饰,它的类型为DecorationDecoration是一个抽象类,它定义了一个接口 createBoxPainter(),子类的主要职责是需要通过实现它来创建一个画笔,该画笔用于绘制装饰。
  • position:此属性决定在哪里绘制Decoration,它接收DecorationPosition的枚举类型,该枚举类有两个值:
    • background:在子组件之下绘制,即背景装饰。
    • foreground:在子组件之上绘制,即前景。

BoxDecoration

我们通常会直接使用BoxDecoration类,它是一个Decoration的子类,实现了常用的装饰元素的绘制。

BoxDecoration({
   
  Color color, //颜色
  DecorationImage image,//图片
  BoxBorder border, //边框
  BorderRadiusGeometry borderRadius, //圆角
  List<BoxShadow> boxShadow, //阴影,可以指定多个
  Gradient gradient, //渐变
  BlendMode backgroundBlendMode, //背景混合模式
  BoxShape shape = BoxShape.rectangle, //形状
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

示例:下面代码实现一个带阴影的背景色渐变的按钮

DecoratedBox(
   decoration: BoxDecoration(
     gradient: LinearGradient(colors:[Colors.red,Colors.orange.shade700]), //背景渐变
     borderRadius: BorderRadius.circular(3.0), //3像素圆角
     boxShadow: [ //阴影
       BoxShadow(
         color:Colors.black54,
         offset: Offset(2.0,2.0),
         blurRadius: 4.0
       )
     ]
   ),
  child: Padding(
    padding: EdgeInsets.symmetric(horizontal: 80.0, vertical: 18.0),
    child: Text("Login", style: TextStyle(color: Colors.white),),
  )
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

效果:

在这里插入图片描述

上面的例子中使用了LinearGradient类,它用于定义线性渐变的类,Flutter中还提供了其他渐变配置类,如RadialGradientSweepGradient,若有需要可以自行查看API文档。

Transform

Transform可以在其子组件绘制时对其应用一些矩阵变换来实现一些特效。Matrix4是一个4D矩阵,通过它我们可以实现各种矩阵操作,下面是一个例子:

Container(
  color: Colors.black,
  child: Transform(
    alignment: Alignment.topRight, //相对于坐标系原点的对齐方式
    transform: Matrix4.skewY(0.3), //沿Y轴倾斜0.3弧度
    child: Container(
      padding: const EdgeInsets.all(8.0),
      color: Colors.deepOrange,
      child: const Text('Apartment for rent!'),
    ),
  ),
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

效果:

在这里插入图片描述

由于矩阵变化时发生在绘制时,而无需重新布局和构建等过程,所以性能很好。

平移

Transform.translate接收一个offset参数,可以在绘制时沿x、y轴对子组件平移指定的距离。

DecoratedBox(
  decoration:BoxDecoration(color: Colors.red), 
  child: Transform.translate(
    offset: Offset(-20.0, -5.0), // 默认原点为左上角,左移20像素,向上平移5像素  
    child: Text("Hello world"),
  ),
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

效果:
在这里插入图片描述

旋转

Transform.rotate可以对子组件进行旋转变换,如:

import 'dart:math' as math;  // 要使用math.pi需先进行导包

DecoratedBox(
  decoration:BoxDecoration(color: Colors.red),
  child: Transform.rotate( 
    angle:math.pi / 2 ,  // 旋转90度
    child: Text("Hello world"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/484920
推荐阅读
相关标签
  

闽ICP备14008679号