实操flutter避免嵌套地狱的5种方法

实操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(