常用的Widget和弹性盒子布局~
抽取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的body修改为当前的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拼接使用
$
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<Car> datas数据源放置到当前的car.dart文件中。这样主文件中的代码就比较精简了。3. 同理,把最开始创建的MyWidget也抽取出去,这样在main.dart如果想要切换使用的话,只需要修改对应的组件构造方法即可。$_end',
textAlign: TextAlign.center,
style: _textStyle); // 这个TextStyle的构造也可以抽取出去
}
}
运行之后:
还可以设置最大行数,以及多余的行用省略号表示
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
)
),
],
));
}
3.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,),
],
),
);
}
}
主轴和交叉轴
使用以上三种方向布局的时候我们需要知道主轴!主轴属性:居中、开始、结束 主轴方向:
- 横向Row -> 右边
- 纵向Column -> 下
- 多层Stack -> 外
- 主轴添加属性(start/center/end):默认是从左边开始,如果想从右边开始我们可以添加主轴属性
mainAxisAlignment: MainAxisAlignment.end
。同理设置start就是左边,center就是中间
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,),
],
),
);
}
}
设置主轴方向之后运行效果:
- 主轴添加属性
MainAxisAlignment.spaceBetween
:剩下的空间平均分配到小部件之间。
- 主轴添加属性
MainAxisAlignment.spaceAround
:剩下的空间平均分配到小部件周围。
- 主轴添加属性
MainAxisAlignment.spaceEvenly
:剩下的空间和小部件一起平均分。
- 交叉轴也就是y轴
CrossAxisAlignment
也有属性(start/center/end),除了基本的三个之外还有一个CrossAxisAlignment.baseline
,使用Text组件的时候可以看的比较明显就是文字底部对齐,一般搭配着textBaseline
一起使用,不然会报错
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,)),
],
),
);
}
}
转载自:https://juejin.cn/post/7023684597003059231