抽取Widget

打开我们上一篇的示例的代码,我们发现main.dart里面有很多Widget,这个时候我们是不是可以抽取一下呢?

  1. 抽取listView: 右键选中New->Dart File,创建一个新的listview.dart文件, 输入s就可以快速联想出来有状态和无状态的两种widget的代码块

image.png
选中stless回车

  1. class extends StatelessWidget {
  2. const ({Key? key}) : super(key: key);
  3. @override
  4. Widget build(BuildContext context) {
  5. return Container();
  6. }
  7. }

extends前面的类型为class ListViewDemo extends StatelessWidget
删除const ({Key? key}) : super(key: key);我们这里暂时用不到。此时这个文件中的代码就成为了

  1. import 'package:flutter/material.dart';
  2. import 'model/car.dart';
  3. class ListViewDemo extends StatelessWidget {
  4. Widget _itemForRow(BuildContext context, int index) {
  5. return Container(
  6. color: Colors.white,
  7. margin: EdgeInsets.all(10),
  8. child: Column(
  9. children: [
  10. Image.network(datas[index].imageUrl!),
  11. Text(datas[index].name!)
  12. ],
  13. )
  14. );
  15. }
  16. @override
  17. Widget build(BuildContext context) {
  18. return ListView.builder(
  19. itemBuilder: _itemForRow,
  20. itemCount: datas.length,
  21. );
  22. }
  23. }

然后把main.dart的关于ListView全部剪切到这里,此时主工程的home修改为当前的ListViewDemo()

  1. import 'package:flutter/material.dart';
  2. import 'package:hello_flutter/base_widget.dart';
  3. import 'package:hello_flutter/listview.dart';
  4. void main() => runApp(App());
  5. class App extends StatelessWidget {
  6. @override
  7. Widget build(BuildContext context) {
  8. return MaterialApp(
  9. home: Home(),
  10. );
  11. }
  12. }
  13. class Home extends StatelessWidget {
  14. @override
  15. Widget build(BuildContext context) {
  16. return Scaffold(
  17. backgroundColor: Colors.grey,
  18. appBar: AppBar(
  19. title: Text(
  20. 'flutterDemo'
  21. ),
  22. ),
  23. body: ListViewDemo(),
  24. );
  25. }
  26. }
  1. 抽取model:把main.dart文件中的List<Car> datas数据源放置到当前的car.dart文件中。这样主文件中的代码就比较精简了。
  2. 同理,把最开始创建的MyWidget也抽取出去,这样在main.dart如果想要切换使用的话,只需要修改对应的组件构造方法即可。

    常用Widget

  3. 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 datas数据源放置到当前的car.dart文件中。这样主文件中的代码就比较精简了。3. 同理,把最开始创建的MyWidget也抽取出去,这样在main.dart如果想要切换使用的话,只需要修改对应的组件构造方法即可。$_end’, textAlign: TextAlign.center, style: _textStyle); // 这个TextStyle的构造也可以抽取出去 } }

  1. 运行之后:<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 />还可以设置最大行数,以及多余的行用省略号表示
  2. ```dart
  3. Text(
  4. maxLines: 3, // 最大3行
  5. overflow: TextOverflow.ellipsis, // 超过3行后缀省略
  6. );

image.png

  1. 富文本RichText组件:这个RichText组件有一个TextSpan可以嵌套使用,针对不同的文本设置不同的style

    1. @override
    2. Widget build(BuildContext context) {
    3. return RichText(text: TextSpan(
    4. children: <TextSpan>[
    5. TextSpan(
    6. text: '$_pre 1. 抽取model:把main.dart文件中的List<Car> ',
    7. style: TextStyle(
    8. fontSize: 30,
    9. color: Colors.yellow
    10. )
    11. ),
    12. TextSpan(
    13. text: '$_pre 1. 抽取model:把main.dart文件中的List<Car> ',
    14. style: TextStyle(
    15. fontSize: 20,
    16. color: Colors.red
    17. )
    18. ),
    19. TextSpan(
    20. text: '$_pre 1. 抽取model:把main.dart文件中的List<Car> ',
    21. style: TextStyle(
    22. fontSize: 40,
    23. color: Colors.purpleAccent
    24. )
    25. ),
    26. ],
    27. ));
    28. }

    image.png

  2. Container组件:这个组件在布局的时候经常使用。因为它有一个childen而且会自适应布局。搭配着Row这个组件就可以无限套娃当然还有上一篇介绍的Column

    1. class TextDemo extends StatelessWidget {
    2. @override
    3. Widget build(BuildContext context) {
    4. return Container(
    5. child: Row(
    6. children: [
    7. Container(
    8. color: Colors.red,
    9. child: Icon(Icons.add),
    10. //padding: EdgeInsets.all(30) , // 内边距和子部件有关系
    11. // margin: EdgeInsets.all(20), // 外边距和父部件有关系
    12. )
    13. ],
    14. ),
    15. );
    16. }
    17. }

    这里的padding是内边距,如果没有设置的话是这样的
    image.png
    打开padding的注释可以清楚的看到是图片的内边距
    image.png
    继续打开margin的注释,这样一对比就能看出,margin是图片的外边距。
    image.png

    Flutter布局之Row

    弹性盒子布局:横向Row、纵向Column、折叠Stack。在flutter中Alignment的中心点即为父控件的center

  1. import 'package:flutter/material.dart';
  2. class LayoutDemo extends StatelessWidget {
  3. @override
  4. Widget build(BuildContext context) {
  5. return Container(
  6. child: Text('Layout Demo'),
  7. alignment: Alignment(0,0), // [-1,1]
  8. );
  9. }
  10. }

image.png

我们研究下搭配着row使用的对齐方式,为了方便观察,给每个Icon包装了一层Container用来设置颜色

  1. class LayoutDemo extends StatelessWidget {
  2. @override
  3. Widget build(BuildContext context) {
  4. return Container(
  5. alignment: Alignment(0,0),
  6. child: Row(
  7. children: [
  8. Container(child: Icon(Icons.add, size: 60,), color: Colors.yellow,),
  9. Container(child: Icon(Icons.sort, size: 60,), color: Colors.red,),
  10. Container(child: Icon(Icons.ac_unit, size: 60,), color: Colors.purpleAccent,),
  11. Container(child: Icon(Icons.access_alarm, size: 60,), color: Colors.greenAccent,),
  12. ],
  13. ),
  14. );
  15. }
  16. }

image.png
修改Alignment(-1,-1)的x坐标:
image.png
修改Alignment(-1,-1)的y坐标:
image.png
此时修改alignment: Alignment(-1,0)让它的横坐标修改为-1或者1都对这个没有影响。但是修改纵坐标y的话:y = -1时图像跑到了最上面,y = 1时图像跑到了最下面,所以说使用Row布局的时候,修改x的坐标不会产生影响。

Flutter布局之Column

此时修改下布局的方式

  1. import 'package:flutter/material.dart';
  2. class LayoutDemo extends StatelessWidget {
  3. @override
  4. Widget build(BuildContext context) {
  5. return Container(
  6. alignment: Alignment(1,1),
  7. child: Column(
  8. children: [
  9. Container(child: Icon(Icons.add, size: 60,), color: Colors.yellow,),
  10. Container(child: Icon(Icons.sort, size: 60,), color: Colors.red,),
  11. Container(child: Icon(Icons.ac_unit, size: 60,), color: Colors.purpleAccent,),
  12. Container(child: Icon(Icons.access_alarm, size: 60,), color: Colors.greenAccent,),
  13. ],
  14. ),
  15. );
  16. }
  17. }

image.png
image.png

同理,如果搭配着Column来布局的话,此时修改alignment: Alignment(-1,0)让它的纵坐标修改为-1或者1都对这个没有影响。但是修改横坐标x的话:x = -1时图像跑到了最左面,x = 1时图像跑到了最右面,所以说使用Column布局的时候,修改y的坐标不会产生影响。

Flutter布局之Stack

  1. import 'package:flutter/material.dart';
  2. class LayoutDemo extends StatelessWidget {
  3. @override
  4. Widget build(BuildContext context) {
  5. return Container(
  6. alignment: Alignment(0,0),
  7. child: Stack(
  8. children: [
  9. Container(child: Icon(Icons.add, size: 120,), color: Colors.yellow,),
  10. Container(child: Icon(Icons.sort, size: 90,), color: Colors.red,),
  11. Container(child: Icon(Icons.ac_unit, size: 60,), color: Colors.purpleAccent,),
  12. Container(child: Icon(Icons.access_alarm, size: 30,), color: Colors.greenAccent,),
  13. ],
  14. ),
  15. );
  16. }
  17. }

image.png
stack可以和Positioned搭配使用来布局相对位置

  1. class LayoutDemo extends StatelessWidget {
  2. @override
  3. Widget build(BuildContext context) {
  4. return Container(
  5. alignment: Alignment(0,0),
  6. child: Stack(
  7. children: [
  8. Container(
  9. child: Icon(Icons.clear, size: 120),
  10. color: Colors.yellow,
  11. ),
  12. Positioned(child: Container(
  13. child: Icon(Icons.sort, size: 60),
  14. color: Colors.red,
  15. ), left: 0,), // 距离父视图左边为0 double像素点
  16. Positioned(child: Container(
  17. child: Icon(Icons.search, size: 30),
  18. color: Colors.blue,
  19. ), right: 0,) // 距离父视图右边为0
  20. ],
  21. ),
  22. );
  23. }
  24. }

image.png

主轴和交叉轴

使用以上三种方向布局的时候我们需要知道主轴!主轴属性:居中、开始、结束
主轴方向:

  • 横向Row -> 右边
  • 纵向Column -> 下
  • 多层Stack -> 外
  1. 主轴添加属性(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,), ], ), ); } }

  1. 设置主轴方向之后运行效果:<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. 2. 主轴添加属性`MainAxisAlignment.spaceBetween`:剩下的空间平均分配到小部件之间。
  3. ![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)
  4. 3. 主轴添加属性`MainAxisAlignment.spaceAround`:剩下的空间平均分配到小部件周围。
  5. ![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)
  6. 4. 主轴添加属性`MainAxisAlignment.spaceEvenly`:剩下的空间和小部件一起平均分。
  7. ![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)
  8. 5. 交叉轴也就是y`CrossAxisAlignment`也有属性(start/center/end),除了基本的三个之外还有一个`CrossAxisAlignment.baseline`,使用Text组件的时候可以看的比较明显就是文字底部对齐,一般搭配着`textBaseline `一起使用,不然会报错
  9. ```dart
  10. import 'package:flutter/material.dart';
  11. class LayoutDemo extends StatelessWidget {
  12. @override
  13. Widget build(BuildContext context) {
  14. return Container(
  15. alignment: Alignment(0,0),
  16. child: Row(
  17. mainAxisAlignment: MainAxisAlignment.spaceEvenly, // 已结束位置
  18. crossAxisAlignment: CrossAxisAlignment.baseline,
  19. textBaseline: TextBaseline.alphabetic,
  20. children: [
  21. Container(child: Icon(Icons.add, size: 120,), color: Colors.yellow,),
  22. Container(child: Icon(Icons.sort, size: 90,), color: Colors.red,),
  23. Container(child: Icon(Icons.ac_unit, size: 60,), color: Colors.purpleAccent,),
  24. Container(child: Icon(Icons.access_alarm, size: 30,), color: Colors.greenAccent,),
  25. ],
  26. ),
  27. );
  28. }
  29. }

image.png

Expanded

使用Expand的小组件会占满当前的主轴方法,在主轴方向不会剩下空隙,当横线布局不够用的时候会自动换行。

  1. import 'package:flutter/material.dart';
  2. class LayoutDemo extends StatelessWidget {
  3. @override
  4. Widget build(BuildContext context) {
  5. return Container(
  6. alignment: Alignment(0,0),
  7. child: Row(
  8. children: [
  9. Expanded(child: Container(child: Icon(Icons.add, size: 120,), color: Colors.yellow,)),
  10. Expanded(child: Container(child: Icon(Icons.sort, size: 90,), color: Colors.red,)),
  11. Expanded(child: Container(child: Icon(Icons.ac_unit, size: 60,), color: Colors.purpleAccent,)),
  12. Expanded(child: Container(child: Icon(Icons.access_alarm, size: 30,), color: Colors.greenAccent,)),
  13. ],
  14. ),
  15. );
  16. }
  17. }

image.png

AspectRatio宽高比

  1. class LayoutDemo extends StatelessWidget {
  2. @override
  3. Widget build(BuildContext context){
  4. return Container(
  5. alignment: Alignment(0,0),
  6. child: Container(
  7. color: Colors.greenAccent,
  8. width: 300, // 如果此时设置了高度 那么下面的宽高比设置将会无效
  9. child: AspectRatio(aspectRatio: 1/2,),
  10. ),
  11. );
  12. }
  13. }

image.png