图文并茂的Flutter布局和状态管理~
抽取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,)), ], ), ); } } 复制代码
作者:weak_PG
链接:https://juejin.cn/post/7023684597003059231