抽取Widget
打开我们上一篇的示例的代码,我们发现main.dart里面有很多Widget,这个时候我们是不是可以抽取一下呢?
- 抽取listView: 右键选中
New->Dart File
,创建一个新的listview.dart
文件, 输入s
就可以快速联想出来有状态和无状态的两种widget的代码块
选中stless回车
class extends StatelessWidget {
const ({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container();
}
}
extends前面的类型为class ListViewDemo extends StatelessWidget
删除const ({Key? key}) : super(key: key);
我们这里暂时用不到。此时这个文件中的代码就成为了
import 'package:flutter/material.dart';
import 'model/car.dart';
class ListViewDemo extends StatelessWidget {
Widget _itemForRow(BuildContext context, int index) {
return Container(
color: Colors.white,
margin: EdgeInsets.all(10),
child: Column(
children: [
Image.network(datas[index].imageUrl!),
Text(datas[index].name!)
],
)
);
}
@override
Widget build(BuildContext context) {
return ListView.builder(
itemBuilder: _itemForRow,
itemCount: datas.length,
);
}
}
然后把main.dart
的关于ListView全部剪切到这里,此时主工程的home修改为当前的ListViewDemo()
import 'package:flutter/material.dart';
import 'package:hello_flutter/base_widget.dart';
import 'package:hello_flutter/listview.dart';
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Home(),
);
}
}
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(
title: Text(
'flutterDemo'
),
),
body: ListViewDemo(),
);
}
}
- 抽取model:把main.dart文件中的
List<Car> datas
数据源放置到当前的car.dart文件中。这样主文件中的代码就比较精简了。 同理,把最开始创建的
MyWidget
也抽取出去,这样在main.dart如果想要切换使用的话,只需要修改对应的组件构造方法即可。常用Widget
Text组件: String拼接使用
$
```dart import ‘package:flutter/material.dart’;
class TextDemo extends StatelessWidget {
final TextStyle _textStyle = TextStyle(
fontSize: 16.0,
backgroundColor: Colors.red,
);
final String _pre = ‘前缀’;
final String _end = ‘后缀’;
@override
Widget build(BuildContext context) {
return Text(‘$_pre 1. 抽取model:把main.dart文件中的List
运行之后:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21860540/1635320864110-e3eaefce-9922-4caf-949b-a7eb2e38459b.png#clientId=u719830bc-2875-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=680&id=u15f61764&margin=%5Bobject%20Object%5D&name=image.png&originHeight=680&originWidth=313&originalType=binary&ratio=1&rotation=0&showTitle=false&size=50491&status=done&style=none&taskId=uf394474d-d291-4394-83f7-81b0ae88ce7&title=&width=313)<br />还可以设置最大行数,以及多余的行用省略号表示
```dart
Text(
maxLines: 3, // 最大3行
overflow: TextOverflow.ellipsis, // 超过3行后缀省略
);
富文本RichText组件:这个RichText组件有一个TextSpan可以嵌套使用,针对不同的文本设置不同的style
@override
Widget build(BuildContext context) {
return RichText(text: TextSpan(
children: <TextSpan>[
TextSpan(
text: '$_pre 1. 抽取model:把main.dart文件中的List<Car> ',
style: TextStyle(
fontSize: 30,
color: Colors.yellow
)
),
TextSpan(
text: '$_pre 1. 抽取model:把main.dart文件中的List<Car> ',
style: TextStyle(
fontSize: 20,
color: Colors.red
)
),
TextSpan(
text: '$_pre 1. 抽取model:把main.dart文件中的List<Car> ',
style: TextStyle(
fontSize: 40,
color: Colors.purpleAccent
)
),
],
));
}
Container组件:这个组件在布局的时候经常使用。因为它有一个childen而且会自适应布局。搭配着Row这个组件就可以
无限套娃
当然还有上一篇介绍的Column
class TextDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Row(
children: [
Container(
color: Colors.red,
child: Icon(Icons.add),
//padding: EdgeInsets.all(30) , // 内边距和子部件有关系
// margin: EdgeInsets.all(20), // 外边距和父部件有关系
)
],
),
);
}
}
这里的
padding
是内边距,如果没有设置的话是这样的
打开padding
的注释可以清楚的看到是图片的内边距
继续打开margin
的注释,这样一对比就能看出,margin
是图片的外边距。
Flutter布局之Row
弹性盒子布局:横向Row、纵向Column、折叠Stack。在flutter中Alignment的中心点即为父控件的center
import 'package:flutter/material.dart';
class LayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Text('Layout Demo'),
alignment: Alignment(0,0), // [-1,1]
);
}
}
我们研究下搭配着row使用的对齐方式,为了方便观察,给每个
Icon
包装了一层Container
用来设置颜色
class LayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment(0,0),
child: Row(
children: [
Container(child: Icon(Icons.add, size: 60,), color: Colors.yellow,),
Container(child: Icon(Icons.sort, size: 60,), color: Colors.red,),
Container(child: Icon(Icons.ac_unit, size: 60,), color: Colors.purpleAccent,),
Container(child: Icon(Icons.access_alarm, size: 60,), color: Colors.greenAccent,),
],
),
);
}
}
修改Alignment(-1,-1)
的x坐标:
修改Alignment(-1,-1)
的y坐标:
此时修改alignment: Alignment(-1,0)
让它的横坐标修改为-1或者1都对这个没有影响。但是修改纵坐标y的话:y = -1
时图像跑到了最上面,y = 1
时图像跑到了最下面,所以说使用Row布局的时候,修改x的坐标不会产生影响。
Flutter布局之Column
此时修改下布局的方式
import 'package:flutter/material.dart';
class LayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment(1,1),
child: Column(
children: [
Container(child: Icon(Icons.add, size: 60,), color: Colors.yellow,),
Container(child: Icon(Icons.sort, size: 60,), color: Colors.red,),
Container(child: Icon(Icons.ac_unit, size: 60,), color: Colors.purpleAccent,),
Container(child: Icon(Icons.access_alarm, size: 60,), color: Colors.greenAccent,),
],
),
);
}
}
同理,如果搭配着Column来布局的话,此时修改alignment: Alignment(-1,0)
让它的纵坐标修改为-1或者1都对这个没有影响。但是修改横坐标x的话:x = -1
时图像跑到了最左面,x = 1
时图像跑到了最右面,所以说使用Column布局的时候,修改y的坐标不会产生影响。
Flutter布局之Stack
import 'package:flutter/material.dart';
class LayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment(0,0),
child: Stack(
children: [
Container(child: Icon(Icons.add, size: 120,), color: Colors.yellow,),
Container(child: Icon(Icons.sort, size: 90,), color: Colors.red,),
Container(child: Icon(Icons.ac_unit, size: 60,), color: Colors.purpleAccent,),
Container(child: Icon(Icons.access_alarm, size: 30,), color: Colors.greenAccent,),
],
),
);
}
}
stack可以和Positioned
搭配使用来布局相对位置
class LayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment(0,0),
child: Stack(
children: [
Container(
child: Icon(Icons.clear, size: 120),
color: Colors.yellow,
),
Positioned(child: Container(
child: Icon(Icons.sort, size: 60),
color: Colors.red,
), left: 0,), // 距离父视图左边为0 double像素点
Positioned(child: Container(
child: Icon(Icons.search, size: 30),
color: Colors.blue,
), right: 0,) // 距离父视图右边为0
],
),
);
}
}
主轴和交叉轴
使用以上三种方向布局的时候我们需要知道主轴!主轴属性:居中、开始、结束
主轴方向:
- 横向Row -> 右边
- 纵向Column -> 下
- 多层Stack -> 外
- 主轴添加属性(start/center/end):默认是从左边开始,如果想从右边开始我们可以添加主轴属性
mainAxisAlignment: MainAxisAlignment.end
。同理设置start就是左边,center就是中间 ```dart import ‘package:flutter/material.dart’;
class LayoutDemo extends StatelessWidget { @override Widget build(BuildContext context) { return Container( alignment: Alignment(0,0), child: Row( mainAxisAlignment: MainAxisAlignment.end, // 已结束位置 children: [ Container(child: Icon(Icons.add, size: 120,), color: Colors.yellow,), Container(child: Icon(Icons.sort, size: 90,), color: Colors.red,), Container(child: Icon(Icons.ac_unit, size: 60,), color: Colors.purpleAccent,), Container(child: Icon(Icons.access_alarm, size: 30,), color: Colors.greenAccent,), ], ), ); } }
设置主轴方向之后运行效果:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21860540/1635326579965-a6fe5848-3682-4552-8180-06e46dedd7e9.png#clientId=u719830bc-2875-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=680&id=ub52a2add&margin=%5Bobject%20Object%5D&name=image.png&originHeight=680&originWidth=313&originalType=binary&ratio=1&rotation=0&showTitle=false&size=35710&status=done&style=none&taskId=ufe7c47a8-bd6e-4734-b0ac-9099e3b08f1&title=&width=313)
2. 主轴添加属性`MainAxisAlignment.spaceBetween`:剩下的空间平均分配到小部件之间。
![image.png](https://cdn.nlark.com/yuque/0/2021/png/21860540/1635326817095-54311d3c-16a6-4d3a-87a3-288df0afee4b.png#clientId=u719830bc-2875-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=680&id=ua159b227&margin=%5Bobject%20Object%5D&name=image.png&originHeight=680&originWidth=313&originalType=binary&ratio=1&rotation=0&showTitle=false&size=35378&status=done&style=none&taskId=ua55af9e6-ccc2-41ed-af29-04186d58201&title=&width=313)
3. 主轴添加属性`MainAxisAlignment.spaceAround`:剩下的空间平均分配到小部件周围。
![image.png](https://cdn.nlark.com/yuque/0/2021/png/21860540/1635326875621-212c3310-0d9d-4cdf-91de-22fb3f48c844.png#clientId=u719830bc-2875-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=680&id=ubb3d989a&margin=%5Bobject%20Object%5D&name=image.png&originHeight=680&originWidth=313&originalType=binary&ratio=1&rotation=0&showTitle=false&size=34679&status=done&style=none&taskId=u91983e32-e16b-42d8-8c52-36f9edd8478&title=&width=313)
4. 主轴添加属性`MainAxisAlignment.spaceEvenly`:剩下的空间和小部件一起平均分。
![image.png](https://cdn.nlark.com/yuque/0/2021/png/21860540/1635327057067-ce87d0ce-ae58-4480-835c-21b32085a193.png#clientId=u719830bc-2875-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=680&id=u53952ec1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=680&originWidth=313&originalType=binary&ratio=1&rotation=0&showTitle=false&size=34398&status=done&style=none&taskId=u46cf220b-fd7a-46ae-8520-2d9b5282726&title=&width=313)
5. 交叉轴也就是y轴`CrossAxisAlignment`也有属性(start/center/end),除了基本的三个之外还有一个`CrossAxisAlignment.baseline`,使用Text组件的时候可以看的比较明显就是文字底部对齐,一般搭配着`textBaseline `一起使用,不然会报错
```dart
import 'package:flutter/material.dart';
class LayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment(0,0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, // 已结束位置
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Container(child: Icon(Icons.add, size: 120,), color: Colors.yellow,),
Container(child: Icon(Icons.sort, size: 90,), color: Colors.red,),
Container(child: Icon(Icons.ac_unit, size: 60,), color: Colors.purpleAccent,),
Container(child: Icon(Icons.access_alarm, size: 30,), color: Colors.greenAccent,),
],
),
);
}
}
Expanded
使用Expand的小组件会占满当前的主轴方法,在主轴方向不会剩下空隙,当横线布局不够用的时候会自动换行。
import 'package:flutter/material.dart';
class LayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment(0,0),
child: Row(
children: [
Expanded(child: Container(child: Icon(Icons.add, size: 120,), color: Colors.yellow,)),
Expanded(child: Container(child: Icon(Icons.sort, size: 90,), color: Colors.red,)),
Expanded(child: Container(child: Icon(Icons.ac_unit, size: 60,), color: Colors.purpleAccent,)),
Expanded(child: Container(child: Icon(Icons.access_alarm, size: 30,), color: Colors.greenAccent,)),
],
),
);
}
}
AspectRatio
宽高比
class LayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context){
return Container(
alignment: Alignment(0,0),
child: Container(
color: Colors.greenAccent,
width: 300, // 如果此时设置了高度 那么下面的宽高比设置将会无效
child: AspectRatio(aspectRatio: 1/2,),
),
);
}
}
�