flutter开发实战-实现css线性渐变转换flutter渐变LinearGradient功能

news2024/11/14 22:08:30

flutter开发实战-实现css线性渐变转换flutter渐变LinearGradient功能

在之前项目开发中,遇到更换样式,由于从服务器端获取的样式均为css属性值,需要将其转换成flutter类对应的属性值。这里只处理线性渐变linear-gradient
比如渐变
“linear-gradient(to right, rgba(246, 12, 112, .6) 30%, rgba(46, 112, 112, .8) 30%, rgba(46, 12, 145, .8) 30%)”
需要将其转换成LinearGradient属性值。

下面先看效果图

在这里插入图片描述

一、css渐变

linear-gradient() 函数用于创建一个表示两种或多种颜色线性渐变的图片。
创建一个线性渐变,需要指定两种颜色,还可以实现不同方向(指定为一个角度)的渐变效果,如果不指定方向,默认从上到下渐变。

/* 从上到下,蓝色渐变到红色 */
linear-gradient(blue, red);
 
/* 渐变轴为45度,从蓝色渐变到红色 */
linear-gradient(45deg, blue, red);
 
/* 从右下到左上、从蓝色渐变到红色 */
linear-gradient(to left top, blue, red);
 
/* 从下到上,从蓝色开始渐变、到高度40%位置是绿色渐变开始、最后以红色结束 */
linear-gradient(0deg, blue, green 40%, red);

background-image: linear-gradient(direction, color-stop1, color-stop2, …);

direction:用角度值指定渐变的方向(或角度)。
color-stop1, color-stop2,…:用于指定渐变的起止颜色。

请参考查看
https://blog.csdn.net/gloryFlow/article/details/131713101

二、将css的属性值转换成flutter类LinearGradient

LinearGradient一个用于创建线性渐变颜色装饰

下面实现以下将css渐变转换成flutter中的LinearGradient类属性值的过程。

看下css,如linear-gradient(180deg, #f34cf6 20%, #ea3e7f, #ea3e7f 100%)

效果如下

  • 首先,第一个direction是180deg,或者to left,这里暂时只处理这两种格式。
  • 第二,之后是颜色颜色值和stop

我们需要将其转换成flutter的线性渐变对应的类。我们定义类如下

// 渐变方向
enum SkinGradientTo {
  top, // 向上
  bottom, // 向下
  left, // 向左
  right, // 向右
}

// 渐变类
class SkinGradient {
  double? degree; // 角度
  SkinGradientTo? gradientTo; // 方向
  List<Color>? colors; // 颜色
  List<double>? stops;
}

我们进行以下步骤。

2.1 处理css线性渐变的direction

现将css线性渐变字符串valueString,去除多余空格,最后原先有空格的每个值之间值保留一个空格。

代码如下

  // 方式就是遍历两个空格,将其替换成一个空格
  // 处理字符串中多个空格
  // red___50%转red_50%,_代表空格,处理多个连续空格
  static String dealMultContinuousBlank(String originString) {
    String twoBlank = "  ";
    bool hasTwoContinuousBlank = originString.contains(twoBlank);
    while(hasTwoContinuousBlank) {
      originString = originString.replaceAll(twoBlank, " ");
      hasTwoContinuousBlank = originString.contains(twoBlank);
      loggerDebug("originString:${originString}");
    }

    return originString;
  }

转换后的valueString结果格式为:linear-gradient(180deg, #f34cf6 20%, #ea3e7f, #ea3e7f 100%)

2.2 统一颜色处理,将css中的rgb、rgba全部转换成#FFFFFFFF或者#FFFFFF格式

由于需要根据逗号“,”进行拆分字符串,生成数组:[“180deg”," #f34cf6 20%“,” #ea3e7f"," #ea3e7f 100%"]
这里需要转换一下颜色格式,将css中的rgb、rgba全部转换成#FFFFFFFF或者#FFFFFF格式。
代码如下

 // 处理rgba字符串转成Color
  static Color? rgbaToColor(String rgbaString) {
    String start = "rgba";
    if (rgbaString.startsWith(start)) {
      // rgba(152, 69, 255, 0.75)
      String subColorStr = rgbaString.substring(start.length);
      String noBlank = subColorStr.replaceAll(RegExp(r"\s*"), "");

      String noLeftBrackets = noBlank.replaceAll("(", "");
      String gDgColor = noLeftBrackets.replaceAll(")", "");
      List<String> colors = gDgColor.split(",");

      if (colors != null && colors.length == 4) {
        String redStr = colors[0];
        String greenStr = colors[1];
        String blueStr = colors[2];
        String alphaStr = colors[3];
        int? red = int.parse(redStr);
        int? green = int.parse(greenStr);
        int? blue = int.parse(blueStr);
        double? alpha = double.parse(alphaStr);

        Color color = Color.fromRGBO(red, green, blue, alpha);
        return color;
      }
    }

    String rgbStart = "rgb";
    if (rgbaString.startsWith(rgbStart)) {
      // rgba(152, 69, 255)
      String subColorStr = rgbaString.substring(rgbStart.length);
      String noBlank = subColorStr.replaceAll(RegExp(r"\s*"), "");

      String noLeftBrackets = noBlank.replaceAll("(", "");
      String gDgColor = noLeftBrackets.replaceAll(")", "");
      List<String> colors = gDgColor.split(",");

      if (colors != null && colors.length == 3) {
        String redStr = colors[0];
        String greenStr = colors[1];
        String blueStr = colors[2];
        int? red = int.parse(redStr);
        int? green = int.parse(greenStr);
        int? blue = int.parse(blueStr);
        double alpha = 1.0;

        Color color = Color.fromRGBO(red, green, blue, alpha);
        return color;
      }
    }

    return null;
  }

  /// 颜色检测只保存 #RRGGBB格式 FF透明度
  /// [color] 格式可能是材料风/十六进制/string字符串
  /// 返回[String] #rrggbb 字符串
  static String? color2HEX(Color color) {
    // 0xFFFFFFFF
    //将十进制转换成为16进制 返回字符串但是没有0x开头
    String temp = color.value.toRadixString(16);
    if (temp.isNotEmpty) {
      loggerDebug("color2HEX temp:${temp}");
      if (temp.length == 8) {
        String hexColor = "#" + temp.substring(2, 8);
        return hexColor.toLowerCase();
      } else {
        String hexColor = "#" + temp;
        return hexColor.toLowerCase();
      }
    }

    return null;
  }

通过代码转换后生成数组

生成数组:[“180deg”," #f34cf6 20%“,” #ea3e7f"," #ea3e7f 100%"]

2.3 获得SkinGradientTo或者degree

生成的数组中第一个为direction,需要将其转换成SkinGradientTo或者degree,这里暂时只处理四个方向的
to

top, // 向上
  bottom, // 向下
  left, // 向左
  right, // 向右

四个方向。如果第一个元素包括deg时候,表示为角度

代码如下

String first = colors.first.trim().toLowerCase();

List<double> gStops = [];
if (first.contains("deg")) {
  // 角度
  String aFirst = first.replaceAll(RegExp(r"\s*"), "");

  String dgs = aFirst.replaceAll("deg", "");
  double? dgDouble = double.parse(dgs);
  if (dgDouble != null) {
    // 为整型值
    gradient.degree = dgDouble;
  }
} else if (first.startsWith("to")) {
  // 方向
  String aFirst = first.replaceAll(RegExp(r"\s*"), "");

  String toStart = "to";
  String toStr = aFirst.substring(toStart.length);
  SkinGradientTo? gradientTo;
  if ("left" == toStr) {
    gradientTo = SkinGradientTo.left;
  } else if ("right" == toStr) {
    gradientTo = SkinGradientTo.right;
  } else if ("top" == toStr) {
    gradientTo = SkinGradientTo.top;
  } else if ("bottom" == toStr) {
    gradientTo = SkinGradientTo.bottom;
  }

  gradient.gradientTo = gradientTo;

}

如果第一个元素不包括"to"也不包括"deg",则表示direction为缺省值,采取默认即可。

2.4 颜色colors与stops,

由于flutter中的线性渐变类,如果存在stops,则stops与colors的length必须一直,所以需要进行转换。

// 处理渐变的Color, 处理这种字符#280069 50%
  static Map<String, dynamic> handleGradientColor(String colorString) {
    List<String> contentList = colorString.split(" ");
    Map<String, dynamic> mapData = {};
    String? colorHex;
    if (contentList.isNotEmpty) {
      for (String str in contentList) {
        if (str.isNotEmpty) {
          if (str.startsWith("#")) {
            colorHex = str;
            break;
          } else if (str.contains("transparent")) {
            colorHex = str;
            break;
          } else {
            Color? aColor = kColorNameMap[str];
            if (aColor != null) {
              colorHex = str;
            }
          }
        }
      }
    }

    if (colorHex != null) {
      // 颜色的占比
      String colorRatio = findFirstRatio(colorString);
      if (colorRatio.isNotEmpty) {
        // 百分比,如50%
        String ratioStr = colorRatio.replaceAll("%", "");
        double ratio = double.parse(ratioStr) / 100.0;

        mapData["ratio"] = ratio;

        Color? color;

        Color? aColor = kColorNameMap[colorHex];
        if (aColor != null) {
          color = aColor;
        } else {
          color = hexColorString(colorHex);
        }
        mapData["color"] = color;
      } else {
        Color? color;
        Color? aColor = kColorNameMap[colorHex];
        if (aColor != null) {
          color = aColor;
        } else {
          color = hexColorString(colorHex);
        }

        // 没有设置颜色值Stop的百分比,则默认为-1
        double ratio = -1;
        mapData["color"] = color;
        mapData["ratio"] = ratio;
      }
    }
    loggerDebug("mapData:${mapData}");

    return mapData;
  }

// 颜色名称对应的color类,如red对应Colors.red
Map<String, Color> kColorNameMap = {
  'transparent':Colors.transparent,
  'black':Colors.black,
  'white':Colors.white,
  'gray':Colors.grey,
  'red':Colors.red,
  'green':Colors.green,
  'blue':Colors.blue,
  'cyan':Colors.cyan,
  'brown':Colors.brown,
  'lightblue':Colors.lightBlue,
  'lightgreen':Colors.lightGreen,
  'purple':Colors.purple,
  'yellow':Colors.yellow,
  'aqua':ColorUtil.hexColorString("00FFFF"),
  'fuchsia':ColorUtil.hexColorString("FF00FF"),
  'maroon':ColorUtil.hexColorString("800000"),
  'olive':ColorUtil.hexColorString("808000"),
  'navy':ColorUtil.hexColorString("000080"),
  'silver':ColorUtil.hexColorString("C0C0C0"),
  'teal':ColorUtil.hexColorString("C0C0C0"),
};

处理后,每个颜色的每一条均为{‘color’:“#FF808000”,‘stop’:“-1”},或者stop有值{‘color’:“#FF808000”,‘stop’:“0.3”}

2.5 处理stops,将color的stop填充完整

由于出现缺省的color stop,缺省是默认值为-1,需要将其改成正确的值。

stops中如果第一条缺省时候,则设置为0;最后一条缺省,则设置为1,
目的是将#f34cf6 0%,#ff0100,#ff0200,#ea3e7f 50%,#0000ff,#000fff, #ea3e7f 100%

代码如下

 // 处理gStops值,将gStops中为-1的值转换成正确的值,-1只是占位值
  static List<double> dealGradientStops(List<double> stops) {
    // 如果stops值超过两个的时候
    int startIdx = -1;
    int endIdx = -1;

    int allNum = stops.length;

    if (stops.length >= 2) {
      int firstIdx = 0;
      int lastIdx = allNum - 1;

      double first = stops[firstIdx];
      double last = stops[lastIdx];
      if (first == -1) {
        stops.replaceRange(0, 1, [0.0]);
      }

      if (last == -1) {
        stops.replaceRange(lastIdx, allNum, [1.0]);
      }

      print("before stops:${stops}, num:${stops.length}");

      bool isHasDefaultValue = checkGradientStopsHasDefault(stops);
      while(isHasDefaultValue) {

        // 进入循环重新设置值
        startIdx = -1;
        endIdx = -1;

        // 判断出startIdx
        for(int index = 0; index < allNum; index++) {
          double value = stops[index];
          int nextIdx = index+1;

          if(nextIdx < allNum) {
            double nextValue = stops[nextIdx];

            if (value != -1.0 && nextValue == -1.0) {
              startIdx = index;
              break;
            }
          }
        }

        // 判断出endIdx
        for(int index = 0; index < allNum; index++) {
          double value = stops[index];

          if (value != -1.0 && index > startIdx) {
            endIdx = index;
            break;
          }
        }

        print("step stops:${stops}, num:${stops.length}");

        if(endIdx >= startIdx) {
          print("startIdx:${startIdx}, endIdx:${endIdx}");

          int betweenNum = endIdx - startIdx;
          double startValue = stops[startIdx];
          double endValue = stops[endIdx];

          double aTValue = endValue - startValue;
          double perValue = aTValue/(betweenNum);

          List<double> replacements = [];
          double beginValue = startValue;
          for(int i = startIdx+1; i < endIdx; i++) {
            double value = beginValue + perValue;
            replacements.add(value);
            beginValue = value;
          }
          stops.replaceRange(startIdx+1, endIdx, replacements);
        }

        isHasDefaultValue = checkGradientStopsHasDefault(stops);
      }

    } else if (stops.length == 1) {
      double first = stops[0];
      if (first == -1) {
        stops.replaceRange(0, 1, [0.0]);
      }
    }

    return stops;
  }

2.5 得到所需数据类SkinGradient属性值

这时候得到完所需要的数据degree、gradientTo、colors、stops

将其转换为LinearGradient

LoggerManager().debug(
“decorationGradient gradient colors: g r a d i e n t . c o l o r s , s t o p s : {gradient.colors}, stops: gradient.colors,stops:{gradient.stops}, gradientTo:${gradient.gradientTo}”);

Alignment begin = Alignment.centerLeft;
Alignment end = Alignment.centerRight;
GradientRotation? transform;
if (gradient.gradientTo != null) {
if (SkinGradientTo.top == gradient.gradientTo) {
// 向上
begin = Alignment.bottomCenter;
end = Alignment.topCenter;
} else if (SkinGradientTo.bottom == gradient.gradientTo) {
// 向下
begin = Alignment.topCenter;
end = Alignment.bottomCenter;
} else if (SkinGradientTo.left == gradient.gradientTo) {
// 向左
begin = Alignment.centerRight;
end = Alignment.centerLeft;
} else if (SkinGradientTo.right == gradient.gradientTo) {
// 向右
begin = Alignment.centerLeft;
end = Alignment.centerRight;
}
} else {
// 角度
// 计算角度
if (gradient.degree != null && gradient.degree != 0) {
double p = 180.0;
double radians = (math.pi / p) * gradient.degree!;
transform = GradientRotation(radians);

  // 和css角度,方向保持一致
  begin = Alignment.bottomCenter;
  end = Alignment.topCenter;
}

}

LinearGradient linearGradient;
if (gradient.colors != null &&
gradient.stops != null &&
gradient.colors!.length == gradient.stops!.length) {
linearGradient = LinearGradient(
colors: gradient.colors!,
stops: gradient.stops!,
begin: begin,
end: end,
transform: transform,
);
} else {
linearGradient = LinearGradient(
colors: gradient.colors ?? [],
begin: begin,
end: end,
transform: transform,
);
}

至此转换完成了,先看下效果图

完整代码如下

import 'dart:ui';

import 'package:flutter/material.dart';

import 'color_util.dart';
import 'dart:math' as math;

// 颜色名称对应的color类,如red对应Colors.red
Map<String, Color> kColorNameMap = {
  'transparent':Colors.transparent,
  'black':Colors.black,
  'white':Colors.white,
  'gray':Colors.grey,
  'red':Colors.red,
  'green':Colors.green,
  'blue':Colors.blue,
  'cyan':Colors.cyan,
  'brown':Colors.brown,
  'lightblue':Colors.lightBlue,
  'lightgreen':Colors.lightGreen,
  'purple':Colors.purple,
  'yellow':Colors.yellow,
  'aqua':ColorUtil.hexColorString("00FFFF"),
  'fuchsia':ColorUtil.hexColorString("FF00FF"),
  'maroon':ColorUtil.hexColorString("800000"),
  'olive':ColorUtil.hexColorString("808000"),
  'navy':ColorUtil.hexColorString("000080"),
  'silver':ColorUtil.hexColorString("C0C0C0"),
  'teal':ColorUtil.hexColorString("C0C0C0"),
};

// 渐变样式
enum SkinGradientStyle {
  line, // 线性渐变
}

// 渐变方向
enum SkinGradientTo {
  top, // 向上
  bottom, // 向下
  left, // 向左
  right, // 向右
}

// 渐变
class SkinGradient {
  SkinGradientStyle? style; // 样式
  double? degree; // 角度
  SkinGradientTo? gradientTo; // 方向
  List<Color>? colors; // 颜色
  List<double>? stops;
}

// 描边
class SkinDecorationStroke {
  double? width;
  Color? color;
}


// 主题皮肤配置
class SkinThemeConfig {
  /// 转换css到具体的element类
  static SkinGradient? transformThemeGradient(
      { required String value}) {

    try {
        if (value != null && value.isNotEmpty) {
          value = dealMultContinuousBlank(value);
          value = value.trimLeft().trimRight().toLowerCase();
          // 渐变

          SkinGradient? gradient = transformGradient(value);
          if (gradient != null) {
            return gradient;
          }

          return null;
        }
    } catch (e) {
      return null;
    }
  }

  /// 转换css到具体的element类
  static LinearGradient? transformLineGradient(
      {SkinGradient? gradient}) {

    try {
      if (gradient != null) {
        Alignment begin = Alignment.centerLeft;
        Alignment end = Alignment.centerRight;
        GradientRotation? transform;
        if (gradient.gradientTo != null) {
          if (SkinGradientTo.top == gradient.gradientTo) {
            // 向上
            begin = Alignment.bottomCenter;
            end = Alignment.topCenter;
          } else if (SkinGradientTo.bottom == gradient.gradientTo) {
            // 向下
            begin = Alignment.topCenter;
            end = Alignment.bottomCenter;
          } else if (SkinGradientTo.left == gradient.gradientTo) {
            // 向左
            begin = Alignment.centerRight;
            end = Alignment.centerLeft;
          } else if (SkinGradientTo.right == gradient.gradientTo) {
            // 向右
            begin = Alignment.centerLeft;
            end = Alignment.centerRight;
          }
        } else {
          // 角度
          // 计算角度
          if (gradient.degree != null && gradient.degree != 0) {
            double p = 180.0;
            double radians = (math.pi / p) * gradient.degree!;
            transform = GradientRotation(radians);

            // 和css角度,方向保持一致
            begin = Alignment.bottomCenter;
            end = Alignment.topCenter;
          }
        }

        LinearGradient linearGradient;
        if (gradient.colors != null &&
            gradient.stops != null &&
            gradient.colors!.length == gradient.stops!.length) {
          linearGradient = LinearGradient(
            colors: gradient.colors!,
            stops: gradient.stops!,
            begin: begin,
            end: end,
            transform: transform,
          );
        } else {
          linearGradient = LinearGradient(
            colors: gradient.colors ?? [],
            begin: begin,
            end: end,
            transform: transform,
          );
        }
        return linearGradient;
      }
    } catch (e) {
      return null;
    }
  }

  // 处理渐变,转换成渐变的类
  static SkinGradient? transformGradient(String valueString) {
    // value: linear-gradient(180deg, #ff34c6, #fea3e7)

    try {
      String value = valueString.toLowerCase();

      SkinGradient gradient = SkinGradient();
      gradient.style = SkinGradientStyle.line;

      String start = "linear-gradient";
      String subGradientValue =
      value.substring(start.length); // (180deg, #ff34c6, #fea3e7)

      // [rgba(119, 77, 255, .8), rgba(119, 177, 255, .8)]
      List<String> rgbaList = findRegRgbaColorList(subGradientValue);
      if (rgbaList.isNotEmpty) {
        // 将rgba(255, 77, 255, .8)转换成#ff34c6格式
        for (String rgbaString in rgbaList) {
          Color? rgbaColor = rgbaToColor(rgbaString);
          if (rgbaColor != null) {
            String? colorHex = ColorUtil.color2HEX(rgbaColor);
            subGradientValue =
                subGradientValue.replaceAll(rgbaString, colorHex ?? "");
          }
        }
      } else {
        List<String> rgbList = findRegRgbColorList(value);
        if (rgbList.isNotEmpty) {
          // 将rgb(255, 77, 255)转换成#ff34c6格式
          for (String rgbString in rgbList) {
            Color? rgbColor = rgbaToColor(rgbString);
            if (rgbColor != null) {
              String? colorHex = ColorUtil.color2HEX(rgbColor);
              subGradientValue =
                  subGradientValue.replaceAll(rgbString, colorHex ?? "");
            }
          }
        }
      }

      String noLeftBrackets =
      subGradientValue.replaceAll("(", ""); //180deg,#ff34c6,#fea3e7)
      String gDgColor =
      noLeftBrackets.replaceAll(")", ""); //180deg,#ff34c6,#fea3e7
      List<String> colors = gDgColor.split(",");

      print("gDgColor:${gDgColor} colors:${colors}");

      if (colors.isNotEmpty) {
        List<Color> gColors = [];

        String first = colors.first.trimLeft().trimRight().toLowerCase();

        List<double> gStops = [];
        if (first.contains("deg")) {
          // 角度
          String aFirst = first.replaceAll(RegExp(r"\s*"), "");

          String dgs = aFirst.replaceAll("deg", "");
          double? dgDouble = double.parse(dgs);
          if (dgDouble != null) {
            // 为整型值
            gradient.degree = dgDouble;
          }
        } else if (first.startsWith("to")) {
          // 方向
          String aFirst = first.replaceAll(RegExp(r"\s*"), "");

          String toStart = "to";
          String toStr = aFirst.substring(toStart.length);
          SkinGradientTo? gradientTo;
          if ("left" == toStr) {
            gradientTo = SkinGradientTo.left;
          } else if ("right" == toStr) {
            gradientTo = SkinGradientTo.right;
          } else if ("top" == toStr) {
            gradientTo = SkinGradientTo.top;
          } else if ("bottom" == toStr) {
            gradientTo = SkinGradientTo.bottom;
          }

          gradient.gradientTo = gradientTo;
        } else {
          Map<String, dynamic> mapData = handleGradientColor(first);

          double? ratio = mapData["ratio"];
          Color? color = mapData["color"];

          if (color != null) {
            gColors.add(color);
          }

          if (ratio != null) {
            gStops.add(ratio);
          }
        }

        // 遍历之后的
        for (int index = 1; index < colors.length; index++) {
          String colorString = colors[index].trimLeft().trimRight().toLowerCase();
          Map<String, dynamic> mapData = handleGradientColor(colorString);

          double? ratio = mapData["ratio"];
          Color? color = mapData["color"];

          if (color != null) {
            gColors.add(color);
          }

          if (ratio != null) {
            gStops.add(ratio);
          }
        }

        gradient.colors = gColors;

        // 处理后的新的stop数组
        List<double> gGdStops = dealGradientStops(gStops);
        gradient.stops = gGdStops;

        print("to:${gradient.gradientTo},degree:${gradient.degree},colors:${gradient.colors} gStops:${gradient.stops}");

        return gradient;
      }
    } catch (e) {
      return null;
    }
    return null;
  }

  // 处理渐变的Color, 处理这种字符#280069 50px
  static Map<String, dynamic> handleGradientColor(String colorString) {
    List<String> contentList = colorString.split(" ");
    Map<String, dynamic> mapData = {};
    String? colorHex;
    if (contentList.isNotEmpty) {
      for (String str in contentList) {
        if (str.isNotEmpty) {
          if (str.startsWith("#")) {
            colorHex = str;
            break;
          } else if (str.contains("transparent")) {
            colorHex = str;
            break;
          } else {
            Color? aColor = kColorNameMap[str];
            if (aColor != null) {
              colorHex = str;
            }
          }
        }
      }
    }

    if (colorHex != null) {
      // 颜色的占比
      String colorRatio = findFirstRatio(colorString);
      if (colorRatio.isNotEmpty) {
        // 百分比,如50%
        String ratioStr = colorRatio.replaceAll("%", "");
        double ratio = double.parse(ratioStr) / 100.0;

        mapData["ratio"] = ratio;

        Color? color;

        Color? aColor = kColorNameMap[colorHex];
        if (aColor != null) {
          color = aColor;
        } else {
          color = hexColorString(colorHex);
        }
        mapData["color"] = color;
      } else {
        Color? color;
        Color? aColor = kColorNameMap[colorHex];
        if (aColor != null) {
          color = aColor;
        } else {
          color = hexColorString(colorHex);
        }

        // 没有设置颜色值Stop的百分比,则默认为-1
        double ratio = -1;
        mapData["color"] = color;
        mapData["ratio"] = ratio;
      }
    }
    print("mapData:${mapData}");

    return mapData;
  }

  // 处理rgba字符串转成Color
  static Color? rgbaToColor(String rgbaString) {
    String start = "rgba";
    if (rgbaString.startsWith(start)) {
      // rgba(152, 69, 255, 0.75)
      String subColorStr = rgbaString.substring(start.length);
      String noBlank = subColorStr.replaceAll(RegExp(r"\s*"), "");

      String noLeftBrackets = noBlank.replaceAll("(", "");
      String gDgColor = noLeftBrackets.replaceAll(")", "");
      List<String> colors = gDgColor.split(",");

      if (colors != null && colors.length == 4) {
        String redStr = colors[0];
        String greenStr = colors[1];
        String blueStr = colors[2];
        String alphaStr = colors[3];
        int? red = int.parse(redStr);
        int? green = int.parse(greenStr);
        int? blue = int.parse(blueStr);
        double? alpha = double.parse(alphaStr);

        Color color = Color.fromRGBO(red, green, blue, alpha);
        return color;
      }
    }

    String rgbStart = "rgb";
    if (rgbaString.startsWith(rgbStart)) {
      // rgba(152, 69, 255)
      String subColorStr = rgbaString.substring(rgbStart.length);
      String noBlank = subColorStr.replaceAll(RegExp(r"\s*"), "");

      String noLeftBrackets = noBlank.replaceAll("(", "");
      String gDgColor = noLeftBrackets.replaceAll(")", "");
      List<String> colors = gDgColor.split(",");

      if (colors != null && colors.length == 3) {
        String redStr = colors[0];
        String greenStr = colors[1];
        String blueStr = colors[2];
        int? red = int.parse(redStr);
        int? green = int.parse(greenStr);
        int? blue = int.parse(blueStr);
        double alpha = 1.0;

        Color color = Color.fromRGBO(red, green, blue, alpha);
        return color;
      }
    }

    return null;
  }

  // 找到字符串中的第一个百分比百分比
  // 'abc rgba(119, 77, 255, .8) 88% , rgba(119, 177, 255, .8) 21%'
  // => [88%, 21%]
  static String findFirstRatio(String str) {
    RegExp exp = RegExp(r'[\d.]+%');
    RegExpMatch? match = exp.firstMatch(str);
    return match?.group(0) ?? '';
  }

  // 处理字符串中多个空格
  // red___50%转red_50%,_代表空格,处理多个连续空格
  static String dealMultContinuousBlank(String originString) {
    String twoBlank = "  ";
    bool hasTwoContinuousBlank = originString.contains(twoBlank);
    while(hasTwoContinuousBlank) {
      originString = originString.replaceAll(twoBlank, " ");
      hasTwoContinuousBlank = originString.contains(twoBlank);
      print("originString:${originString}");
    }

    return originString;
  }

  // 处理gStops值,将gStops中为-1的值转换成正确的值,-1只是占位值
  static List<double> dealGradientStops(List<double> stops) {
    // 如果stops值超过两个的时候
    int startIdx = -1;
    int endIdx = -1;

    int allNum = stops.length;

    if (stops.length >= 2) {
      int firstIdx = 0;
      int lastIdx = allNum - 1;

      double first = stops[firstIdx];
      double last = stops[lastIdx];
      if (first == -1) {
        stops.replaceRange(0, 1, [0.0]);
      }

      if (last == -1) {
        stops.replaceRange(lastIdx, allNum, [1.0]);
      }

      print("before stops:${stops}, num:${stops.length}");

      bool isHasDefaultValue = checkGradientStopsHasDefault(stops);
      while(isHasDefaultValue) {

        // 进入循环重新设置值
        startIdx = -1;
        endIdx = -1;

        // 判断出startIdx
        for(int index = 0; index < allNum; index++) {
          double value = stops[index];
          int nextIdx = index+1;

          if(nextIdx < allNum) {
            double nextValue = stops[nextIdx];

            if (value != -1.0 && nextValue == -1.0) {
              startIdx = index;
              break;
            }
          }
        }

        // 判断出endIdx
        for(int index = 0; index < allNum; index++) {
          double value = stops[index];

          if (value != -1.0 && index > startIdx) {
            endIdx = index;
            break;
          }
        }

        print("step stops:${stops}, num:${stops.length}");

        if(endIdx >= startIdx) {
          print("startIdx:${startIdx}, endIdx:${endIdx}");

          int betweenNum = endIdx - startIdx;
          double startValue = stops[startIdx];
          double endValue = stops[endIdx];

          double aTValue = endValue - startValue;
          double perValue = aTValue/(betweenNum);

          List<double> replacements = [];
          double beginValue = startValue;
          for(int i = startIdx+1; i < endIdx; i++) {
            double value = beginValue + perValue;
            replacements.add(value);
            beginValue = value;
          }
          stops.replaceRange(startIdx+1, endIdx, replacements);
        }

        isHasDefaultValue = checkGradientStopsHasDefault(stops);
      }

    } else if (stops.length == 1) {
      double first = stops[0];
      if (first == -1) {
        stops.replaceRange(0, 1, [0.0]);
      }
    }

    return stops;
  }


  // 判断数组中是否有占位的-1值
  static bool checkGradientStopsHasDefault(List<double> list) {
    for (int index = 0; index < list.length; index++) {
      double value = list[index];
      if (value == -1) {
        return true;
      }
    }

    return false;
  }
}

在这里插入图片描述
在这里插入图片描述

三、小结

flutter开发实战-实现css线性渐变转换flutter渐变LinearGradient功能

在之前项目开发中,遇到更换样式,由于从服务器端获取的样式均为css属性值,需要将其转换成flutter类对应的属性值。

学习记录,每天不停进步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/833441.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

macOS install redis遇到的bug(tar包,homebrew安装,守护进程redis.conf配置)

官网下载tar包再make install 首先是sudo make test的时候一直报 !!! WARNING The following tests failed: *** [err]: trim on SET with big value in tests/unit/type/string.tcl Expected [r memory usage key] < 42000 (context: type source line 478 file /usr/loca…

【二开】前端项目二开技巧 组件相关dependencies

【二开】前端项目二开技巧 组件相关dependencies 二开 依赖的组件 dependencies 方案一 1.直接去 项目下 node_modules目录里改对应的组件代码 2.清理缓存 rimraf node_modules/.cache/ && rimraf node_modules/.vite 3.启动项目生效 方案二&#xff08;推荐&#xf…

接口测试常用技能:Jmeter操作数据库

01、配置JDBC Request 1、添加需要的驱动jar包 使用不同的数据库&#xff0c;我们需要引入不同的jar包。 方式1&#xff1a;直接将jar包复制到jmeter的lib目录 mysql数据库&#xff1a; 下载mysql-connector-java-5.1.28.jar放到 jmeter根目录的lib目录下 sql server 数据…

从零开始学极狐GitLab|02 基本功能使用

目录 0. 如何查看当前版本&#xff1f; 1. 群组创建与删除 1.1 新建群组 1.2 删除群组 2. 项目创建与删除 2.1 新建项目 2.2 删除项目 3. 用户管理 3.1 新建用户 3.2 权限管理 3.3 关闭用户注册 4. 项目管理 4.1 修改 Git 地址 4.2 分支 ➤ 新建分支 ➤ 分支保…

【eNSP】静态路由

【eNSP】静态路由 原理网关路由表 实验根据图片连接模块配置路由器设备R1R2R3R4 配置PC的IP地址、掩码、网关PC1PC2PC3 配置静态路由查看路由表R1R2R3R4测试能否通信 原理 网关 网关与路由器地址相同&#xff0c;一般路由地址为.1或.254。 网关是当电脑发送的数据的目标IP不在…

2023ChinaJoy,撕歌APP实力突围多元化娱乐市场

ChinaJoy作为中国最大的数字娱乐展览会之一&#xff0c;以及亚洲最具影响力的数字娱乐盛会之一&#xff0c;自2003首次举办以来&#xff0c;ChinaJoy不断拓展创新&#xff0c;早已经成为了数字娱乐行业的风向标和重要交流平台&#xff0c;吸引了国内外众多知名企业和品牌参与。…

基于Java的校园二手交易平台设计与实现

基于Java的JSP校园二手交易平台&#xff0c;不仅减少了学生资源的浪费&#xff0c;还能达到资金“回血”的目的。简洁方便、易用。 页面展示 1 登录页面 2 注册页面 3 主页页面 4 商品页面 5 卖家一览页面 6 求购信息页面 7 商城介绍页面 8 商城资讯页面 9 留言信息页面 10…

AcWing244. 谜一样的牛(树状数组)

输入样例&#xff1a; 5 1 2 1 0输出样例&#xff1a; 2 4 5 3 1 解析&#xff1a; 从后往前遍历&#xff0c;每次需要在剩余的数中&#xff0c;找到第 h[ i ]1 大的数即为当前牛的身高。 每次二分&#xff0c;然后求前缀和。 #include<bits/stdc.h> using namespace …

Scratch 之 “2000年至今的天数”积木 的用法

2000年至今的天数 看起来&#xff0c;这块积木好像没有什么用&#xff0c;2000年还是固定的一个值&#xff0c;不能输入。点一下它&#xff0c;可以看到返回了一个小数位数特别多的数。 但实际上&#xff0c;这块积木有着非常多的作用。在很多自制积木包中都用到了这块积木。…

穷举深搜暴搜回溯剪枝(3)

一)字母大小写全排列 784. 字母大小写全排列 - 力扣&#xff08;LeetCode&#xff09; 1)从每一个字符开始进行枚举&#xff0c;如果枚举的是一个数字字符&#xff0c;直接忽视 如果是字母的话&#xff0c;进行选择是变还是不变 2)当进行遍历到叶子结点的时候&#xff0c;直接将…

【Android】在AndroidStudio开发工具运行Java程序

在Android Studio开发工具中&#xff0c;Android系统开始就是用java语言开发的&#xff0c;还可以java代码来写程序&#xff0c;控制台&#xff0c;桌面应用&#xff0c;还可以写可调用的模块&#xff0c;这里讲一下创建Java程序步骤&#xff0c;方便入门java语言开发。 新建一…

数字化采购:提升效率、优化供应链的新趋势

随着信息技术的快速发展&#xff0c;数字化采购正成为企业追求效率和优化供应链的新趋势。数字化采购是利用数字技术和互联网平台&#xff0c;实现采购流程的自动化和在线化。本文将围绕数字化采购的应用场景&#xff0c;探讨其在采购环节中带来的效益与优势。 一、在线供应商…

家居行业解决方案 | 君子签电子签约助力家居企业减负增效

过去&#xff0c;家居行业因供需两端碎片化、服务链条较长等因素&#xff0c;导致线上发展较为缓慢&#xff0c;近年来&#xff0c;互联网的发展推动直播电商、兴趣电商兴起&#xff0c;促使家居行业数字化建设需求越来越为迫切。 合同管理作为家居行业企业经营的一项重要管理…

wordpress发表文章时报错: rest_cannot_create,抱歉,您不能为此用户创建文章(已解决)

使用wordpress 的rest api发布文章&#xff0c;首先使用wp-json/jwt-auth/v1/token接口获取token&#xff0c;然后再使用/wp-json/wp/v2/posts 接口发表文章&#xff0c;但是使用axios请求时&#xff0c;却报错&#xff1a; 但是&#xff0c;我在postman上却是可以的&#xff0…

RunnerGo五种压测模式你会配置吗?

我们在做性能测试时需要根据性能需求配置不同的压测模式如&#xff1a;阶梯模式。使用jmeter时我们需要安装插件来配置测试模式&#xff0c;为了方便用户使用&#xff0c;RunnerGo内嵌了压测模式这一选项&#xff0c;今天给大家介绍一下RunnerGo的几种压测模式和怎么根据性能需…

【原创科普教程】如何更改bangumi一键跳转脚本视频教程

【原创科普教程】如何更改bangumi一键跳转脚本视频教程 【原创科普教程】如何更改bangumi一键跳转脚本视频教程 - AcFun弹幕视频网 - 认真你就输啦 (?ω?)ノ- ( ゜- ゜)つロ【原创科普教程】如何更改bangumi一键跳转脚本视频教程,bangumi,bgm,,脚本安装地址: https://greasy…

浅谈新电改背景下电网企业综合能源服务商业模式研究及发展方向

安科瑞 华楠 摘要: 新电改方案实施后&#xff0c;由于输配电价的改革和售电侧的放开&#xff0c;电网企业的盈利模式也随之发生了变化。这就要求电网企业转变服务理念与经营方式&#xff0c;来寻求竞争优势。基于“魏朱六要素商业模式”模型&#xff0c;对电网企业综合能源服务…

《皮囊》阅读笔记

《皮囊》阅读笔记 2023年8月2号在杭州小屋读完&#xff0c;该书共收录14篇散文&#xff0c;内容大致分为两部分&#xff1a;前半部分讲述作者的阿太&#xff08;外婆的母亲&#xff09;、母亲、父亲关于生活哲学、房子、疾病与信仰的故事&#xff0c;后半部分讲述生活在小镇的张…

国标GB28181安防视频平台EasyGBS大批量通道接入后,创建角色接口未响应的排查

国标GB28181协议视频平台EasyGBS是基于国标GB28181协议的视频云服务平台&#xff0c;支持多路设备同时接入&#xff0c;并对多平台、多终端分发出RTSP、RTMP、FLV、HLS、WebRTC等格式的视频流。平台可提供视频监控直播、云端录像、云存储、检索回放、智能告警、语音对讲、平台级…

JavaSE【继承和多态】(2)(向上转型、向下转型、重写、动态绑定、静态绑定、多态的运用)

一、多态 1.多态的概念 多态的概念&#xff1a;通俗来说&#xff0c;就是多种形态&#xff0c; 具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会产生出不同 的状 态。 简单说就是&#xff0c;同一件事情&#xff0c;发生在不同对象身上&#xff0c;就会产生不同…