Flutter 嵌套过深的解决方案

cubegao 2020-01-03 PM 1742℃ 0条

写代码容易,读代码难。功能是都实现了,但是对维护人员来说,简直就是灾难。

背景

Flutter 注重组合而非继承,要想搭建出 UI,需要组合不同功能的 Widget,如布局 Widget、响应 Widget、控件 Widget 等才能搭建出一个功能完善的 UI 界面,这便导致了嵌套地狱: 在顶级 Widget 的构造器中内嵌众多 Widget

像下面这个例子,其实都不是最多层的,只要你卖力,可以超乎想象的)))))))))))))))))).....

class FrostedGlass extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Stack(
        children: <Widget>[
          new ConstrainedBox(
              constraints: const BoxConstraints.expand(),
              child: new FlutterLogo()),
          new Center(
            child: new ClipRect(
              child: new BackdropFilter(
                filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
                child: Opacity(
                  opacity: 0.5,
                  child: new Container(
                    width: 200.0,
                    height: 200.0,
                    decoration: new BoxDecoration(
                      color: Colors.grey.shade200,
                    ),
                    child: new Center(
                      child: new Text('Frosted',
                          style: Theme.of(context).textTheme.headline2),
                    ),
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

IDE上面看到是这样的。
23FE5157A35842B9E575CC472D30A4E7.jpg

方法一

使用变量、方法与自定义 Widget 缓解嵌套地狱

// 把嵌套严重的 Widget 的一部分提出一个新的 Widget
class A xxxx { 
        // 使用方法抽离部分 Widget
        Widget constructD() {
          return new D(
         child:new E(
            child:new F(
                   child:new G(
                ...
                    ),
                 ),
              ),
            ),
        }

    @override
    Widget build(BuildContext context) {
        return new B(
            child:new C(
                child: constructD()
            ),
        );
    }
}

方法二

widget外面再包一层。请求自己的class,返回封装好的widget。使用时就是链式调用。

import 'package:flutter/material.dart';

///widget装饰器
class WidgetDecoration {
  Widget _widget;

  WidgetDecoration(Widget widget) {
    this._widget = widget;
  }

  Function _onTapFunc;
  Function _onDoubleTapFunc;
  Function _onLongPressFunc;

  ///add padding属性
  WidgetDecoration padding(
      {Key key, double left = 0.0, double top = 0.0, double right = 0.0, double bottom = 0.0}) {
    var padding = EdgeInsets.only(left: left, top: top, right: right, bottom: bottom);
    _widget = new Padding(key: key, padding: padding, child: _widget);
    return this;
  }

  ///增加padingall
  WidgetDecoration paddAll({Key key, double all = 0.0}) {
    var padding = EdgeInsets.all(all);
    _widget = new Padding(key: key, padding: padding, child: _widget);
    return this;
  }

  ///增加align 当前布局相对位置
  ///FractionalOffset.centerRight
  WidgetDecoration align({Key key, AlignmentGeometry alignment = Alignment.center}) {
    _widget = new Align(key: key, alignment: alignment, child: _widget);
    return this;
  }

  ///位置
  WidgetDecoration positioned(
      {Key key,
      double left,
      double top,
      double right,
      double bottom,
      double width,
      double height}) {
    _widget = new Positioned(
        key: key,
        left: left,
        top: top,
        right: right,
        bottom: bottom,
        width: width,
        height: height,
        child: _widget);
    return this;
  }

  ///stack 相当于frameLayout布局

  ///填充布局
  WidgetDecoration expanded({Key key, int flex = 1}) {
    _widget = new Expanded(key: key, flex: flex, child: _widget);
    return this;
  }

  ///是否显示布局 true为不显示 false为显示
  WidgetDecoration offstage({Key key, bool offstage = true}) {
    _widget = new Offstage(key: key, offstage: offstage, child: _widget);
    return this;
  }

  ///透明度 0 是完全透明 1 完全不透明
  WidgetDecoration opacity({Key key, @required double opacity, alwaysIncludeSemantics = false}) {
    _widget = new Opacity(
        key: key, opacity: opacity, alwaysIncludeSemantics: alwaysIncludeSemantics, child: _widget);
    return this;
  }

  ///基准线布局
  WidgetDecoration baseline({
    Key key,
    @required double baseline,
    @required TextBaseline baselineType,
  }) {
    _widget =
        new Baseline(key: key, baseline: baseline, baselineType: baselineType, child: _widget);
    return this;
  }

  ///设置宽高比
  WidgetDecoration aspectRatio({Key key, @required double aspectRatio}) {
    _widget = new AspectRatio(key: key, aspectRatio: aspectRatio, child: _widget);
    return this;
  }

  ///矩阵转换
  WidgetDecoration transform({
    Key key,
    @required Matrix4 transform,
    origin,
    alignment,
    transformHitTests = true,
  }) {
    _widget = new Transform(
        key: key,
        transform: transform,
        origin: origin,
        alignment: alignment,
        transformHitTests: transformHitTests,
        child: _widget);
    return this;
  }

  ///居中 todo: center
  WidgetDecoration center({Key key, double widthFactor, double heightFactor}) {
    _widget =
        new Center(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: _widget);
    return this;
  }

  ///布局容器
  WidgetDecoration container({
    Key key,
    alignment,
    padding,
    Color color,
    Decoration decoration,
    foregroundDecoration,
    double width,
    double height,
    BoxConstraints constraints,
    margin,
    transform,
  }) {
    _widget = new Container(
        key: key,
        alignment: alignment,
        padding: padding,
        color: color,
        decoration: decoration,
        foregroundDecoration: foregroundDecoration,
        width: width,
        height: height,
        constraints: constraints,
        margin: margin,
        transform: transform,
        child: _widget);
    return this;
  }

  ///设置具体尺寸
  WidgetDecoration sizedBox({Key key, double width, double height}) {
    _widget = new SizedBox(key: key, width: width, height: height, child: _widget);
    return this;
  }

  ///设置最大最小宽高布局
  WidgetDecoration constrainedBox({
    Key key,
    minWidth = 0.0,
    maxWidth = double.infinity,
    minHeight = 0.0,
    maxHeight = double.infinity,
  }) {
    BoxConstraints constraints = new BoxConstraints(
        minWidth: minWidth, maxWidth: maxWidth, minHeight: minHeight, maxHeight: maxHeight);
    _widget = new ConstrainedBox(key: key, constraints: constraints, child: _widget);
    return this;
  }

  ///限定最大宽高布局
  WidgetDecoration limitedBox({
    Key key,
    maxWidth = double.infinity,
    maxHeight = double.infinity,
  }) {
    _widget = new LimitedBox(key: key, maxWidth: maxWidth, maxHeight: maxHeight, child: _widget);
    return this;
  }

  ///百分比布局
  WidgetDecoration fractionallySizedBox(
      {Key key, alignment = Alignment.center, double widthFactor, double heightFactor}) {
    _widget = new FractionallySizedBox(
        key: key,
        alignment: alignment,
        widthFactor: widthFactor,
        heightFactor: heightFactor,
        child: _widget);
    return this;
  }

  ///缩放布局
  WidgetDecoration fittedBox({Key key, fit = BoxFit.contain, alignment = Alignment.center}) {
    _widget = new FittedBox(key: key, fit: fit, alignment: alignment, child: _widget);
    return this;
  }

  ///旋转盒子 1次是90度
  WidgetDecoration rotatedBox({
    Key key,
    @required int quarterTurns,
  }) {
    _widget = new RotatedBox(key: key, quarterTurns: quarterTurns, child: _widget);
    return this;
  }

  ///装饰盒子 细节往外抛 decoration 编写放在外面
  WidgetDecoration decoratedBox({
    Key key,
    @required Decoration decoration,
    position = DecorationPosition.background,
  }) {
    _widget =
        new DecoratedBox(key: key, decoration: decoration, position: position, child: _widget);
    return this;
  }

  ///圆形剪裁
  WidgetDecoration clipOval(
      {Key key, CustomClipper<Rect> clipper, Clip clipBehavior = Clip.antiAlias}) {
    _widget = new ClipOval(key: key, clipper: clipper, clipBehavior: clipBehavior, child: _widget);
    return this;
  }

  ///圆角矩形剪裁
  WidgetDecoration clipRRect(
      {Key key,
      @required BorderRadius borderRadius,
      CustomClipper<RRect> clipper,
      Clip clipBehavior = Clip.antiAlias}) {
    _widget = new ClipRRect(
        key: key,
        borderRadius: borderRadius,
        clipper: clipper,
        clipBehavior: clipBehavior,
        child: _widget);
    return this;
  }

  ///矩形剪裁  todo: 需要自定义clipper 否则无效果
  WidgetDecoration clipRect(
      {Key key, @required CustomClipper<Rect> clipper, Clip clipBehavior = Clip.hardEdge}) {
    _widget = new ClipRect(key: key, clipper: clipper, clipBehavior: clipBehavior, child: _widget);
    return this;
  }

  ///路径剪裁  todo: 需要自定义clipper 否则无效果
  WidgetDecoration clipPath(
      {Key key, @required CustomClipper<Path> clipper, Clip clipBehavior = Clip.antiAlias}) {
    _widget = new ClipPath(key: key, clipper: clipper, clipBehavior: clipBehavior, child: _widget);
    return this;
  }

  ///animatedOpacity 淡入淡出
  WidgetDecoration animatedOpacity({
    Key key,
    @required double opacity,
    Curve curve = Curves.linear,
    @required Duration duration,
  }) {
    _widget = new AnimatedOpacity(
        key: key, opacity: opacity, curve: curve, duration: duration, child: _widget);
    return this;
  }

  ///页面简单切换效果
  WidgetDecoration hero({Key key, @required Object tag}) {
    _widget = new Hero(key: key, tag: tag, child: _widget);
    return this;
  }

  ///点击事件
  WidgetDecoration onClick({Key key, onTap, onDoubleTap, onLongPress}) {
    _widget = new GestureDetector(
      key: key,
      child: _widget,
      onTap: onTap ?? _onTapFunc,
      onDoubleTap: onDoubleTap ?? _onDoubleTapFunc,
      onLongPress: onLongPress ?? _onLongPressFunc,
    );
    return this;
  }

  ///添加点击事件
  WidgetDecoration onTap(Function func, {Key key}) {
    _onTapFunc = func;
    _widget = new GestureDetector(
      key: key,
      child: _widget,
      onTap: _onTapFunc,
      onDoubleTap: _onDoubleTapFunc,
      onLongPress: _onLongPressFunc,
    );
    return this;
  }

  ///双击
  WidgetDecoration onDoubleTap(Function func, {Key key}) {
    _onDoubleTapFunc = func;
    _widget = new GestureDetector(
      key: key,
      child: _widget,
      onTap: _onTapFunc,
      onDoubleTap: _onDoubleTapFunc,
      onLongPress: _onLongPressFunc,
    );
    return this;
  }

  ///长按
  WidgetDecoration onLongPress(Function func, {Key key}) {
    _onLongPressFunc = func;
    _widget = new GestureDetector(
      key: key,
      child: _widget,
      onTap: _onTapFunc,
      onDoubleTap: _onDoubleTapFunc,
      onLongPress: _onLongPressFunc,
    );
    return this;
  }

  Widget build() {
    return _widget;
  }
}

方法三

用扩展函数来给系统widget添加一个child属性。
使用时也支持链式调用。

extension WidgetExt on Widget {

  Container intoContainer({
      //复制Container构造函数的所有参数(除了child字段)
    Key key,
    AlignmentGeometry alignment,
    EdgeInsetsGeometry padding,
    Color color,
    Decoration decoration,
    Decoration foregroundDecoration,
    double width,
    double height,
    BoxConstraints constraints,
    EdgeInsetsGeometry margin,
    Matrix4 transform,
  }) {
      //调用Container的构造函数,并将当前widget对象作为child参数
    return Container(
      key: key,
      alignment: alignment,
      padding: padding,
      color: color,
      decoration: decoration,
      foregroundDecoration: foregroundDecoration,
      width: width,
      height: height,
      constraints: constraints,
      margin: margin,
      transform: transform,
      child: this,
    );
  }
}
标签: Flutter

非特殊说明,本博所有文章均为博主原创。

评论啦~