Container类里有一个alignment属性,翻译过来应该叫对齐方式,这个属性用来控制Container的子控件相对于它自身的一个位置。在咱们iOS开发中,咱们知道坐标系的原点是在左上角。而在flutter中,坐标系的原点在父控件的正中心,可使用这个alignment属性来控制子控件在父控件中的位置,它有两个参数分别是double类型的x,y。取值是-1到1,当0,0的时候表示子控件在父控件的正中心;当1,0的时候,表示子控件位于x方向上的最右侧,y方向上居中;当-1,-1的时候,表示子控件位于父控件的左上角位置。有点相似于CALayer的anchorPoint属性。如图代码以下: 数组
Row表示水平布局,它有一个children属性,用来存放它的子控件。代码以下: 微信
最开始咱们尝试了alignment属性的做用,当它是0,0的时候,Text的位置默认是在屏幕中央的。为何这里换成咱们的Row以后,Row的子控件位置不在屏幕中央呢?markdown
Row 和 Column 都有一个mainAxisAlignment属性,叫做主轴对齐方式,默认是MainAxisAlignment.start意思沿着主轴方向开始,Row布局下就是从左至右,Column布局下就是从上至下。
MainAxisAlignment.spaceAround
:将剩下的空间平均分配
MainAxisAlignment.spaceBetween
:将剩下的空间分配到子控件之间
MainAxisAlignment.spaceEvenly
:等间距分配子控件框架
交叉轴对齐方式,start,end,center这几种方式试一下很好理解,stretch会将子控件拉伸。而baseline用的比较少,单独使用它会报错,须要和Text文本结合,还须要配合textBaseline
属性一块儿使用。以下图所示,若是不设置CrossAxisAlignment.baseline
和TextBaseline.alphabetic
就会根据控件高度水平对齐,而若是设置了就会根据控件内文本的基线对齐。
less
这个和Row是对应的,Row是水平布局,这个Column是垂直布局ide
class LayoutDemo extends StatelessWidget {
const LayoutDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.green,
alignment: Alignment(0, 0),
child: Column(
children: [
Container(
child: Icon(Icons.add, size: 180,),
color: Colors.red,
),
Container(
child: Icon(Icons.ac_unit, size: 120,),
color: Colors.yellow,
),
Container(
child: Icon(Icons.access_alarm, size: 60,),
color: Colors.blue,
),
],
),
);
}
}
复制代码
显示效果如图:布局
这个跟Row相似动画
这个跟Row相似ui
这个是用在Z轴上的布局的,row是X轴,column是Y轴。children数组第一个放在最底部,最后一个放在上面,离用户最近的地方。spa
class LayoutDemo extends StatelessWidget {
const LayoutDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.green,
alignment: Alignment(0, 0),
child: Stack(
children: [
Container(
child: Icon(Icons.add, size: 180,),
color: Colors.red,
),
Container(
child: Icon(Icons.ac_unit, size: 120,),
color: Colors.yellow,
),
Container(
child: Icon(Icons.access_alarm, size: 60,),
color: Colors.blue,
),
],
),
);
}
}
复制代码
APP显示效果:
Stack里有一个alignment属性,它用来控制全部子控件相对于最大那个子控件的位置
class StackDemo extends StatelessWidget {
const StackDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.bottomRight,
children: [
Container(
child: Icon(Icons.add, size: 180,),
color: Colors.red,
),
Container(
child: Icon(Icons.ac_unit, size: 120,),
color: Colors.yellow,
),
Container(
child: Icon(Icons.access_alarm, size: 60,),
color: Colors.blue,
),
],
);
}
}
复制代码
Stack里配合Positioned类使用的话,跟咱们iOS的约束有点相似了,能够设置上,左间距之类的
class StackDemo extends StatelessWidget {
const StackDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned(
child: Container(
child: Icon(
Icons.add,
size: 180,
),
color: Colors.red,
),
),
Positioned(
child: Container(
child: Icon(
Icons.ac_unit,
size: 120,
),
color: Colors.yellow,
),
),
Positioned(
top: 20,
left: 20,
right: 20,
child: Container(
child: Icon(
Icons.access_alarm,
size: 60,
),
color: Colors.blue,
),
),
],
);
}
}
复制代码
Expanded是一个相似Container的经常使用的布局容器,它用来填充布局,使用了填充布局在主轴方向上是不会有间隔的,因此Expanded用在Row里面的时候,子控件的宽度设置就没有意义了,而在Column里面使用的使用,子控件的高度设置就没有意义了。这里以Column为例:
class LayoutDemo extends StatelessWidget {
const LayoutDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.green,
alignment: Alignment(0, 0),
child: Column(
children: [
Expanded(
child: Container(
child: Icon(Icons.add, size: 180,),
color: Colors.red,
),
),
Expanded(
child: Container(
child: Icon(Icons.ac_unit, size: 120,),
color: Colors.yellow,
),
),
Expanded(
child: Container(
child: Icon(Icons.access_alarm, size: 60,),
color: Colors.blue,
),
),
],
),
);
}
}
复制代码
AspectRatio是一个容器类,它有一个属性aspectRatio表示宽高比。若是指定了宽度,根据这个aspectRatio能够自动算出高度;若是指定了高度,根据aspectRatio能够自动算出宽度。以下面代码指定了父视图高度为100,aspectRatio宽高比为2,子视图宽度就是200,再把父视图撑起来也是200。
class LayoutDemo extends StatelessWidget {
const LayoutDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.green,
alignment: Alignment(0, 0),
child: Container(
color: Colors.blue,
height: 100,
child: AspectRatio(
aspectRatio: 2,
child: Icon(
Icons.add,
),
),
));
}
}
复制代码
以前介绍的这么多类都是无状态的,意思是显示以后没办法更新UI的,若是想要实时更新UI的话,就不能继承无状态的类了。咱们先来看一个例子:明明count变化了,可是界面显示没有变化 记得修改APP的home视图
而后点击屏幕右下角的加号按钮,能够发现明明控制台打印了count的值已经发生了变化,可是界面显示依然是0
下面咱们解决这个问题,将StateManagerDemo继承改成StatefulWidget,实现createState方法返回一个自定义的State对象,自定义的State对象里面实现build方法。还须要注意在按钮的点击方法里调用一下setState方法。这样每次点击加号按钮就能实时更新UI了。改造完以后以下图所示:
到目前为止,咱们对flutter的一些基础知识就算是介绍的差很少了。接下来咱们开始作一个简单的仿微信APP。咱们应该都有经验,理论的知识学得再多,不动手开始敲代码,不在项目中运用,是很难真正掌握一门知识的。
新建一个flutter工程,命名wechat_demo:
删掉多余的代码,能够所有从新本身写:
建立底部的TabBar和item,默认的type是白色的,显示效果很难看因此改成fixed,还能够设置fixedColor:
BottomNavigationBar有一个属性currentIndex即表明了当前选中的下标。咱们能够经过设置它的值来控制哪一个按钮被选中。既然须要改变UI了,说明咱们须要将StatelessWidget改成StatefulWidget了。还有一个参数onTap是用来回调点击事件的。实现点击事件,切换currentIndex,从新setState就能够实现,点击切换了。咱们将bottomNavigationBar相关代码放到一个新的文件rootPage中。代码以下: 记得修改main.dart文件中
这样就实现了APP的底部TabBar的展现,点击功能。点击每一个item的时候,会发现flutter的bottomNavigationBar还自带了动画效果...
咱们知道Scaffold还有一个body的属性,表示展现在屏幕上的内容。咱们每一个item对应的界面都须要一个AppBar,那么也许意味着,body属性还须要一个Scaffold来展现咱们的每一个item对应的内容。 能够看到微信首页就已经大概出来了,可是点击的时候只会显示这个微信页面,怎么实现切换不一样的页面呢,确定须要一个数组,来存放对应的每一个页面了。
而后body里,根据咱们的_currentIndex返回对应的body 这样点击每一个item都会跳转到对应的界面了,APP的主框架的搭建好了。
今天主要讲了flutter的三大布局类Row,Column,Stack以及他们的一些属性。而后是有状态的Widget和无状态的Widget,最后搭建了一下咱们要作的仿微信APP的底部bottomNavigationBar和切换页面功能