小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
前言
从上一篇中主要了解了Flutter环境的搭建,以及ListView的简单使用,这篇中将继续熟悉一下其他的一些Widget。
工程创建
首先创建一个工程,这里可以通过Android Studio或者命令行创建,进入到工程的目录路径,命令行如下:
# -i:iOS
# objc:选择Objective-C语言,这里不选择语言,iOS默认是Swift,Android默认的是Kotlin
flutter create -i objc flutter_demo
创建工程以后会在lib/main.dart默认生成一些简单的UI样式,运行看一下效果(这里打开的是iPhone的模拟器)。
从运行结果可以看到文字和按钮的显示,点击按钮计数会增加。下面我们删掉原来的代码,然后自定义一些
Widget玩玩,看看效果,为了使代码不要太乱,下面一个Widget创建一个dart文件分开。
常用Widget
在lib文件目录下再创建一个views目录。删掉之前的代码,下面是main.dart的代码。
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter Demo'),
),
body: Container(),
),
);
}
}
Text
创建一个StatelessWidget的text_widget.dart文件放在views里,首先看一下Text的一些属性。
const Text(
String this.data, {
Key? key,
this.style,
this.strutStyle,
this.textAlign,
this.textDirection,
this.locale,
this.softWrap,
this.overflow,
this.textScaleFactor,
this.maxLines,
this.semanticsLabel,
this.textWidthBasis,
this.textHeightBehavior,
})
可以对Text设置样式,对齐方式,文字方向,最大行数等等。可以简单的设置一些属性
import 'package:flutter/material.dart';
class TextWidget extends StatelessWidget {
const TextWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Text(
'Flutter 是 Google 发布的一个用于创建跨平台、高性能移动应用的框架。Flutter 和 Qt mobile 一样,都没有使用原生控件,相反都实现了一个自绘引擎,使用自身的布局、绘制系统。',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16.0,
color: Colors.red,
),
);
}
}
把main.dart的Container替换刚创建的TextWidget,再运行。
如果要设置最大行数,然后多余的用
...的方式,可以设置maxLine和overflow属性。完成后点击Hot reload就可以看到效果了。
RichText
在文本样式里经常会用到富文本,Flutter中提供了富文本的Widget就是RichText,看看RichText的属性定义
RichText({
Key? key,
required this.text,
this.textAlign = TextAlign.start,
this.textDirection,
this.softWrap = true,
this.overflow = TextOverflow.clip,
this.textScaleFactor = 1.0,
this.maxLines,
this.locale,
this.strutStyle,
this.textWidthBasis = TextWidthBasis.parent,
this.textHeightBehavior,
})
要求传的text这里传TextSpan,TextSpan和Text的区别是有children属性,所以可以做嵌套,这样就是可以对文本设置不同的样式。
const TextSpan({
this.text,
this.children,
TextStyle? style,
this.recognizer,
MouseCursor? mouseCursor,
this.onEnter,
this.onExit,
this.semanticsLabel,
this.locale,
this.spellOut,
})
把之前的代码用RichText里嵌套TextSpan稍改一下,然后运行,就实现了简单的富文本效果。
Container
再创建一个container_demo.dart文件,返回一个Container的Widget,然后引入到main的body里,我们给Container设置个颜色,运行。
可以看到整个
body被Container占满,它是个弹性布局,我们在Container里面放个Row,然后再嵌套个Icon,代码如下:
import 'package:flutter/material.dart';
class ContainerDemo extends StatelessWidget {
const ContainerDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
child: Row(
children: [
Container(
color: Colors.red,
child: const Icon(Icons.add),
),
],
),
);
}
}
再次运行。
可以看到之前黄色占满
body的Container变小了,所以如果Container不设置宽度和高度会受到子部件的大小影响。接着看一下Container的属性:
Container({
Key? key,
this.alignment,
this.padding,
this.color,
this.decoration,
this.foregroundDecoration,
double? width,
double? height,
BoxConstraints? constraints,
this.margin,
this.transform,
this.transformAlignment,
this.child,
this.clipBehavior = Clip.none,
})
margin & padding
在上面的代码中再设置一下padding和margin,看一下效果,代码如下:
红色
Container到加号图标的距离是30,也就是内边距padding属性,红色Container到黄色的距离是20,也就是外边距margin属性。
alignment
上面的Container内嵌套了Row,Row是横向占满屏幕的,现在删除之前的Row,在Container内放一个Text,然后设置它的alignment(值的范围是从-1~1),再次运行看一下效果。
通过
alignment可以设置元素的坐标点。
Row
Row是一个横向布局的Widget,横向会占满屏幕,纵向会根据子部件的大小弹性布局,接下来再创建一个row_demo.dart,在Row里放置3个Icon,也设置alignment为(0, 0),代码如下:
import 'package:flutter/material.dart';
class RowDemo extends StatelessWidget {
const RowDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
alignment: const Alignment(0, 0),
color: Colors.yellow,
child: Row(
children: [
Container(
child: const Icon(Icons.add, size: 45,),
color: Colors.red,
),
Container(
child: const Icon(Icons.ac_unit, size: 45,),
color: Colors.blue,
),
Container(
child: const Icon(Icons.access_alarm, size: 45,),
color: Colors.white,
),
],
),
);
}
}
接下来运行看一下效果
可以看到图标在横向是靠左边的,纵向是中心位置,因为
Row是占满横向的,所以对于Row来说alignment设置x轴方向是无效的,同样对于Column设置y轴方向是无效的。
Column
Column是一个纵向布局的Widget,纵向占满屏幕,横向会根据子部件大小自动布局,把上面的代码的Row改成Column,运行。
Stack
Stack是一个层叠布局的Widget,从内层忘外层布局,如果各个子部件的大小一致,外层Widget会覆盖内层的Widget,还是上面的代码,只是改成Stack,运行。
可以看到只显示最后面添加的
alarm的Icon图标,我们把每个子Widget的大小,这样就更直观一点。
更改大小后,可以看到
Stack的子部件是堆叠显示的。
主轴和交叉轴
Row、Column和Stack都有主轴和交叉轴的概念,也就是mainAxisAlignment和crossAxisAlignment属性,横向布局主轴方向是往右的,纵向布局主轴方向是往下的,层叠布局主轴方向是往外的,交叉轴是主轴的垂直方向。
mainAxisAlignment
先看一下MainAxisAlignment的定义。
enum MainAxisAlignment {
start,
end,
center,
spaceBetween,
spaceAround,
spaceEvenly,
}
前三个很好理解,start是默认的从开始方向,end是从结束方向开始,center是中间位置。我们以Row作为例子,分别在这3种方式运行看看效果。
下面再看一下
spaceBetween、spaceAround、spaceEvenly的效果都是什么样的?
根据运行效果可以看到:
spaceBetween: 剩下的空间平均分布到小部件之间。spaceAround: 剩下的空间平均分布到小部件周围。spaceEvenly: 剩下的空间和小部件一起平均分。
crossAxisAlignment
看一下CrossAxisAlignment的定义。
enum CrossAxisAlignment {
start,
end,
stretch,
baseline,
}
直接加上crossAxisAlignment相关属性运行看一下效果。
前3个属性运行都正常,但改为
baseline运行报错了,这里baseline属性要配合Row组件的textBaseline属性使用,代码如下:
Widget build(BuildContext context) {
return Container(
alignment: const Alignment(0, 0),
color: Colors.yellow,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Container(
child: const Icon(Icons.add, size: 120,),
color: Colors.red,
),
Container(
child: const Icon(Icons.ac_unit, size: 60,),
color: Colors.blue,
),
Container(
child: const Icon(Icons.access_alarm, size: 30,),
color: Colors.white,
),
],
),
);
}
再次运行,可以看到和end效果一样。
这时我们把
Icon改成Text,并把文字大小设置成不一样,再看效果。
可以看到所有文字的底部的对齐的。
start: 开始位置对齐。end: 结束位置对齐。stretch: 交叉轴方向占满屏幕。baseline: 需要配合textBaseline使用,文字底部对齐。
Expanded
接下来把之前Row的children内的子部件再嵌套在Expanded里,看一下效果。
子控件占满主轴方向,如果主轴方向放不下会自动换行。
Expanded: 在主轴方向不会剩下间隙,将被Expanded拉伸。
总结
通过这篇对Flutter常用的Widget做了了解,也在实际运行后看不同Widget对应属性的使用的改变,根据这些Widget的使用做个简单的总结。
Text控件的使用,以及RichText富文本控件的使用。Container的用法,Alignment的参数x和y。Row和Column,认识了主轴和交叉轴的概念。Stack的用法,层叠显示。Expanded,填充Widget,可以占满父组件。