Flutter入门-03-封装简单复用组件

前言

  首先我们知道Flutter的语法是嵌套型的,这种语法的最直观的感受就是代码多,嵌套层次深的话可能导致代码阅读困难,其实,这也是Flutter的一项不完美的地方吧。

正文

  面对这个问题,我们可以采用代码抽取,复用的方式来减少在一个文件中堆叠大量代码;采用这种方式的好处如下:

  • 减少了使用处代码的代码量和层级,代码精简直观
  • 抽取出的代码可以多处复用,不限于当前页面,方便灵活拓展
  • 减少了代码量的同时,也方便了后续维护者的阅读与编写,便于维护

一:代码抽取

  在日常的开发中,我们首先会拿到UI的设计稿,在脑子里已经排版布局,然后就疯狂的码代码。例如UI小姐姐拿出了下面常用设计稿


我的
  1. 经过分析,这是一个除了图标和文字,其他都一样的组件
  2. 开始编写其中一条的代码,比如我的订单
  3. 写完一条后后粘贴复制,修改成其他剩余的icon和文字就搞定了

那我就直接拿出某一条代码,这是从Android studio里直接copy的,行数我看了下是41行,具体原始代码如下:

GestureDetector(
              child: Container(
                  color: Color(0xffffffff),
                  padding: EdgeInsets.only(left: 20, right: 16),
                  child: SizedBox(
                    child: Row(
                      crossAxisAlignment: CrossAxisAlignment.stretch,
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        Wrap(
                          runAlignment: WrapAlignment.center,
                          crossAxisAlignment: WrapCrossAlignment.center,
                          children: [
                            Image.asset(
                              "resource/imgs/icon_my_card.png",
                              width: 18,
                              height: 18,
                              fit: BoxFit.contain,
                            ),
                            Padding(
                                padding: EdgeInsetsDirectional.only(start: 22)),
                            Text("银行卡01"),
                          ],
                        ),
                        Wrap(
                          runAlignment: WrapAlignment.center,
                          crossAxisAlignment: WrapCrossAlignment.center,
                          children: [
                            Text(""),
                            Padding(
                              padding: EdgeInsetsDirectional.only(start: 10),
                              child:
                                  Image.asset("resource/imgs/arrow_right.png"),
                            )
                          ],
                        ),
                      ],
                    ),
                    height: 48,
                  )),
              onTap: () {},
            )

  按照我们原来的偷懒计划,直接粘贴复制,一顿操作后发现近300行代码,心中窃喜(这下统计代码量,老子吊打一切,- _-)。当然开玩笑,我们写代码不是为了多,而是完成一个功能,使用的代码越少越好。

二:改造代码,多处复用

  我们上面发现,只是实现了7个相同的item,没有优化的情况下就编写了200多行代码,这实在是太多了,看起来也是眼花缭乱的。不行,我们作为有责任感,有逼格的程序员怎么能看的下去呢,我们需要对这坨代码进行优化,大脑在思考:

1. 其实大多数代码都一样,只是图标和文字不同
2. 是否能够抽取公共部分,把变量部分暴露出来给用户自己动态传递呢

答案当然是可以的,且思路也是正确的。那么我们用什么来存储这些公共部分呢,有两种方式:

1. 继承statelessWidget或者statefulWidget,重新渲染公共部分
2. 直接写个公共方法,在return里直接返回公共部分组件

两种方式都是可以的,看需求而定;如果你的公共部分不需要独立的动态渲染,只是提供静态的公共组件,那直接选用第二种将更方便;这里我们看,我们的公共部分,只是静态的显示,不涉及动态变化的东西,我们就直接用第二种,抽取的过程我就不多说了,下面看抽取出来的代码:

///通用item左右都带有有个icon和text的通用item,常用于我的页面等item条目
///
///[iconLeft] 左边icon的widget
///
///[textLeft] 左边文本的Widget
///
///[iconRight] 右边icon的widget
///
///[textRight] 右边文本的Widget
///
///[heightItem] item高度
///
///[paddingLeftRight] 左边Image距离右边文本的间距
///
///[paddingRightLeft] 右边Image距离左边文本的间距
///
///[importance] 左侧文本是否显示必填*号
///
///[onPress] item点击事件函数回调
iconTextItem(
    {Widget iconLeft,
    Widget iconRight,
    double heightItem = 50,
    double paddingLeftRight = 8,
    double paddingRightLeft = 15,
    importance = false,
    Color backgroundColor,
    double leftPadding,
    double rightPadding,
    @required Widget textLeft,
    Widget textRight,
    VoidCallback onPress,
    Key key}) {
  return GestureDetector(
    child: Container(
        key: key,
        color: backgroundColor ?? Color(0xffffffff),
        padding:
            EdgeInsets.only(left: leftPadding ?? 0, right: rightPadding ?? 0),
        child: SizedBox(
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Wrap(
                runAlignment: WrapAlignment.center,
                crossAxisAlignment: WrapCrossAlignment.center,
                children: [
                  iconLeft ?? Text(""),
                  Padding(
                      padding: EdgeInsetsDirectional.only(
                          start: iconLeft != null ? paddingLeftRight : 0),
                      child:
                          importance ? _getImportanceText(textLeft) : textLeft),
                ],
              ),
              Wrap(
                runAlignment: WrapAlignment.center,
                crossAxisAlignment: WrapCrossAlignment.center,
                children: [
                  textRight ?? Text(""),
                  Padding(
                    padding:
                        EdgeInsetsDirectional.only(start: paddingRightLeft),
                    child: iconRight ??
                        Image.asset("resource/imgs/arrow_right.png"),
                  )
                ],
              ),
            ],
          ),
          height: heightItem,
        )),
    onTap: onPress,
  );
}

/// 返回带有必填*的文本,*号默认红色
_getImportanceText(Text text, {Color color}) {
  assert(text != null);
  return Wrap(
    crossAxisAlignment: WrapCrossAlignment.center,
    children: [
      text,
      Text(
        "*",
        style: TextStyle(color: color ?? colorRed23681731, fontSize: 15),
      ),
    ],
  );
}

代码行数为59行,为啥反而多了呢,因为这里我又兼容了如下的设计稿,通过传入参数可以控制红色的*是否显示,也就是达到了是否必须的设计效果


必填项

那既然我们都封装好了,如何在代码里使用呢,使用方式在目标页面中,直接放入即可,具体引入处代码如下:

iconTextItem(
              onPress: () {
                print("我的银行卡");
                Navigator.pushNamed(context, "/mybankcard");
              },
              leftPadding: 20,
              rightPadding: 14,
//            backgroundColor: Colors.greenAccent,
              paddingLeftRight: 22,
              iconLeft: Image.asset(
                "resource/imgs/icon_my_card.png",
                width: 18,
                height: 18,
                fit: BoxFit.contain,
              ),
              textLeft: Text(
                "我的银行卡",
                style: TextStyle(color: colorBlackFF444C63, fontSize: 15),
              ),
            )

我们把图标和文字声明在方法参数中,同时提供部分默认值,使用处传参即可。我复制过来是19行,原来41行,当前文件节省了21行,不仅如此还减少了层叠情况,看起来更清晰了。现在完成第一个设计稿只需要19*7=133行,比原来(42*7=294)优化了161行,这种效果还是看得见的

总结:

  本片文章主要介绍了通过方法来抽取公共部分的代码加以复用,方便日后的维护拓展。这里还有个自己轻度封装简洁的库,是一个左右widget的package,同样可以达到上述效果,给用户留了更大的自定义空间,大家有兴趣的可以了解使用支持下

上一篇:Flutter入门-02-Dart语言简讲
下一篇:Android开发之WebView(一)配置&小技巧

你可能感兴趣的:(Flutter入门-03-封装简单复用组件)