实操flutter避免嵌套地狱的5种方法
想入坑flutter的同学,都很担心嵌套地狱,但我实际使用flutter差不多1个月,发现如果用的好,是可以避免嵌套地狱的,我总结为5种方法。
方法一:适当使用“高阶组件”
flutter官方文档没有“高阶组件”的概念,我这里说的“高阶组件”指的是由基础组件搭建的内置组件,如果纯粹用基础组件搭建UI,那嵌套层次必然很多,我们应该了解内置的各类“高阶组件”,而且不要害怕“高阶组件”不够灵活,要知道,组件的child属性、title属性、leading属性等都可以赋值任何其他组件,“高阶组件”一样可以扩展。
举例1: 使用ListTile代替Row+Container
ListTile其实是一个三栏布局组件,如果你有三栏或者两栏的布局需求,而且是两头宽度固定,中间一栏宽度自适应,都可以直接用它实现,而不必要自己用Row+Flex+Container实现。
return ListTile(
leading: CircleAvatar(
//头像半径
radius: 25,
//头像图片 -> NetworkImage网络图片,AssetImage项目资源包图片, FileImage本地存储图片
backgroundImage: NetworkImage('${mModel.headurl}'),
title: Text(
'${mModel.nick}',
style: TextStyle(letterSpacing: 0, color: Colors.black, fontSize: 14),
subtitle: Text(
'${mModel.decs}',
style: TextStyle(
letterSpacing: 0, color: Color(0xff666666), fontSize: 10),
trailing: mFollowBtnWidget(mModel, i - 1),
);
举例2: 使用RichText代替Text
import '../constant.dart';
RichText(
text: TextSpan(
text: '这是标题',
style: Constant.SUB_TITLE
children: <TextSpan>[
TextSpan(
text: '这是链接',
style: Constant.LINK,
TextSpan(
text: '这里淡化',
style: Constant.MUTE_TEXT,
)
举例3: 使用SliverAppBar代替AppBar
SliverAppBar是一个顶部带banner图并且banner图可以滑动收起的AppBar,如果自己实现类似功能,就要写很多嵌套和滚动事件。它的使用方法可以去官方文档查看。
总之,对应官方提供的组件,我们要了解透彻,不要写了半天代码,原来写的是官方都实现了的组件,浪费了时间不说,还增加了嵌套层次。
方法二:封装自定义组件
封装的方法在大多数语言和框架里都有,相信大家都知道,在这里提几个注意的点:
适合封装的组件:
- 顶部的AppBar
- 点赞按钮
- 分享按钮和分享按钮组(比如微信分享、qq分享)
- 个人头像
- 常用的主按钮、默认按钮(类似vue的vant组件库的按钮)
- Loading加载动画
- Dialog对话窗
- 常用的带颜色和大小属性的文本
恰当使用全局常量:
// file: constant.dart
class Constant {
static const COLOR_TITLE = Color(0xFF202020);
static const COLOR_SUBTITLE = Color(0xFF88888A);
static const COLOR_MUTE = Color(0xccccccff);
static const COLOR_WARN = Color(0xFFF59A23);
static const COLOR_ERROR = Color(0xFFFA3651);
static const COLOR_PRIMARY = Color(0xFFA33028);
static const COLOR_LINK = Color(0xCC61ADF3);
static sonst MAIN_TITLE = TextStyle(fontSize: 16, color: COLOR_TITLE);
static sonst SUB_TITLE = TextStyle(fontSize: 12, color: COLOR_SUBTITLE);
static sonst LINK = TextStyle(fontSize: 13, color: COLOR_LINK);
}
使用:
import '../constant.dart';
.....
Container(
margin: EdgeInsets.only(top: 3),
child: Text(
mCneterItem[0].hotdiscuss,
style: Constant.LINK,
)
方法三:使用SizedBox代替Container的margin,Spacer代替Flex
如果我们使用Container仅仅是为了使用的它的margin属性,可以适当的使用SizedBox和Spacer代替。 例如:
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 5),
Input(
SizedBox(height: 5),
Input(
)
Spacer其实就是包装了一个 Expanded 的 SizedBox. 我们可以通过它灵活控制 Row/Column
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
color: Colors.blue,
margin: EdgeInsets.symmetric(horizontal: 5),
height: 50,
width: 50,
Spacer(flex: 2), // 弹性系数为2
Container(
color: Colors.blue,
height: 50,
margin: EdgeInsets.symmetric(horizontal: 5),
width: 50,
Spacer(), // 弹性系数默认为1
Container(
color: Colors.blue,
margin: EdgeInsets.symmetric(horizontal: 5),
height: 50,
width: 50,
)
方法四:使用Map代替if else
有时我们有通过if else来返回不同的组件嵌套,而里面仅有一部分不同,可以用Map或者三元语法返回一个变量,在组件嵌套里面直接使用变量就可以了。 实例:
// 优化前的代码:
if(level == 'level1') {
return Container(
width: (MediaQuery.of(context).size.width - 75) / 4,
height: (MediaQuery.of(context).size.width - 75) / 4,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
image: DecorationImage(
image: NetworkImage('https://hrlweibo-1259131655.cos.ap-beijing.myqcloud.com/pic1.jpg'),
fit: BoxFit.cover,
} else {
return Container(
width: (MediaQuery.of(context).size.width - 75) / 4,
height: (MediaQuery.of(context).size.width - 75) / 4,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
image: DecorationImage(
image: NetworkImage('https://hrlweibo-1259131655.cos.ap-beijing.myqcloud.com/pic2.jpg'),
fit: BoxFit.cover,
// 优化后的代码
urls = {
'level1': 'https://hrlweibo-1259131655.cos.ap-beijing.myqcloud.com/pic1.jpg',
'level2': 'https://hrlweibo-1259131655.cos.ap-beijing.myqcloud.com/pic1.jpg'
return Container(
width: (MediaQuery.of(context).size.width - 75) / 4,
height: (MediaQuery.of(context).size.width - 75) / 4,
decoration: BoxDecoration(