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

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字符串转成Colorstatic 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的百分比,则默认为-1double 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;// 判断出startIdxfor(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;}}}// 判断出endIdxfor(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,#fea3e7List<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 50pxstatic 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的百分比,则默认为-1double ratio = -1;mapData["color"] = color;mapData["ratio"] = ratio;}}print("mapData:${mapData}");return mapData;}// 处理rgba字符串转成Colorstatic 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;// 判断出startIdxfor(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;}}}// 判断出endIdxfor(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.mzph.cn/news/21218.shtml

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

相关文章

【Linux操作系统】网络配置详解:从原理到实践(详细通俗讲明DNS)

导语&#xff1a;网络配置是Linux系统中的一项重要任务&#xff0c;合理的网络配置可以保证计算机与其他设备的正常通信。本文将详细介绍Linux网络配置的原理和实践&#xff0c;包括网络配置原理、查看网络IP和网关、测试网络连通性、网络环境配置、设置主机名和hosts映射以及主…

win10笔记本显示器根据页面显示亮度自动调节亮度的问题

系统是win10企业版&#xff0c;针对这个问题查了很多种方法&#xff0c;比如&#xff1a; 1、控制面板->硬件和声音->电源选项->点击当前电源计划的更改计划设置->更改高级电源设置->显示->启用自适应亮度 但是我发现我的电源计划只有平衡这一种&#xff0c…

申请科技型中小企业的好处有哪些?

科技型中小企业&#xff0c;这是由国家出台的&#xff0c;科技部认定的&#xff0c;对中小型企业的一种荣誉。这种企业是有一定数量的科技人员从事科技研究开发&#xff0c;有了一定成果并转化为高新技术产品或服务&#xff0c;实现可持续发展的中小企业。 申请科技型中小企业有…

【雕爷学编程】 MicroPython动手做(38)——控制触摸屏

MixPY——让爱(AI)触手可及 MixPY布局 主控芯片&#xff1a;K210&#xff08;64位双核带硬件FPU和卷积加速器的 RISC-V CPU&#xff09; 显示屏&#xff1a;LCD_2.8寸 320*240分辨率&#xff0c;支持电阻触摸 摄像头&#xff1a;OV2640&#xff0c;200W像素 扬声器&#…

【Golang 接口自动化08】使用标准库httptest完成HTTP请求的Mock测试

目录 前言 http包的HandleFunc函数 http.Request/http.ResponseWriter httptest 定义被测接口 测试代码 测试执行 总结 资料获取方法 前言 Mock是一个做自动化测试永远绕不过去的话题。本文主要介绍使用标准库net/http/httptest完成HTTP请求的Mock的测试方法。 可能有…

【安装】阿里云轻量服务器安装Ubuntu图形化界面(端口号/灰屏问题)

阿里云官网链接 https://help.aliyun.com/zh/simple-application-server/use-cases/use-vnc-to-build-guis-on-ubuntu-18-04-and-20-04 网上搜了很多教程&#xff0c;但是我没在界面看到有vnc连接&#xff0c;后面才发现官网有教程。 其实官网很详细了&#xff0c;不过这里还是…

题解 | #1001.Count# 2023杭电暑期多校6

1001.Count 签到 题目大意 给定 n , m , k n,m,k n,m,k &#xff0c;构造长度为 n n n 的整数序列&#xff0c;元素大小范围为 a i ∈ [ 1 , m ] a_i\in [1,m] ai​∈[1,m] &#xff0c;并且需要保证前 k k k 个元素和后 k k k 个元素对应相同 求可以构造出的序列数量 …

Flink Windows(窗口)详解

Windows&#xff08;窗口&#xff09; Windows是流计算的核心。Windows将流分成有限大小的“buckets”&#xff0c;我们可以在其上应用聚合计算&#xff08;ProcessWindowFunction&#xff0c;ReduceFunction&#xff0c;AggregateFunction或FoldFunction&#xff09;等。在Fl…

【java安全】无Commons-Collections的Shiro550反序列化利用

文章目录 【java安全】无Commons-Collections的Shiro550反序列化利用Shiro550利用的难点CommonsBeanutils1是否可以Shiro中&#xff1f;什么是serialVersionUID&#xff1f;W 无依赖的Shiro反序列化利用链POC 【java安全】无Commons-Collections的Shiro550反序列化利用 Shiro5…

整数拆分——力扣343

文章目录 题目描述法一 动态规划法二 动态规划优化法三 数学 题目描述 法一 动态规划 int integerBreak(int n) {vector<int> dp(n1);for(int i2;i<n;i){int curMax 0;for(int j1;j<i;j){curMax max(curMax, max(j*(i-j), j*dp[i-j]));}dp[i] curMax;} return d…

AI赋能转型升级 助力打造“数智辽宁”——首次大模型研讨沙龙在沈成功举行

当前&#xff0c;以“ChatGPT”为代表的大模型正在引领新一轮全球人工智能技术发展浪潮&#xff0c;推动人工智能从以专用小模型定制训练为主的“手工作坊时代”&#xff0c;迈入以通用大模型预训练为主的“工业化时代”&#xff0c;正不断加速实体经济智能化升级&#xff0c;深…

量子机器学习解决方案新鲜出炉!Rigetti和ADIALab签署合作

​ &#xff08;图片来源&#xff1a;网络&#xff09; 近日&#xff0c;美国全栈量子经典计算的先驱Rigetti Computing宣布与阿拉伯联合酋长国的ADIA Lab签署了合作协议&#xff0c;双方将共同设计、构建、运行和优化量子计算解决方案&#xff0c;旨在解决概率分布分类问题&am…

spring通过文件属性注入bean和基于xml的bean的自动装配以及spring-eel表达式的使用加代码合集

&#x1f600;前言 本章是spring基于XML 配置bean系类中第7篇讲解spring通过文件属性注入bean和基于xml的bean的自动装配以及spring-eel表达式的使用加代码合集 &#x1f3e0;个人主页&#xff1a;尘觉主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是尘觉&…

echarts图表渐变色 + 每个柱子不同颜色设置

echarts柱状图&#xff0c;默认所有柱子都是同一个颜色&#xff0c;显示效果差强人意&#xff0c;本文介绍如果修改成为每个柱子添加不同的颜色&#xff0c;以及如何添加渐变色&#xff0c;丰富图表的显示鲜果。先看效果&#xff1a; 每个柱子颜色不同 每个柱子都有自己的渐变…

Redis的缓存穿透、缓存击穿和缓存雪崩

目录 一、解释说明 二、缓存穿透 1. 什么是缓存穿透&#xff1f; 2. 常见的两种解决方案 &#xff08;1&#xff09;缓存空对象 &#xff08;2&#xff09;布隆过滤 3. 编码解决商品查询的缓存穿透问题 三、缓存雪崩 1. 什么是缓存雪崩&#xff1f; 2. 缓存雪崩解决方…

力扣 416. 分割等和子集

题目来源&#xff1a;https://leetcode.cn/problems/partition-equal-subset-sum/description/ C题解&#xff08;思路来源代码随想录&#xff09; &#xff1a; 背包问题有多种背包方式&#xff0c;常见的有&#xff1a;01背包、完全背包、多重背包、分组背包和混合背包等等。…

Prometheus实现系统监控报警邮件

Prometheus实现系统监控报警邮件 简介 Prometheus将数据采集和报警分成了两个模块。报警规则配置在Prometheus Servers上&#xff0c; 然后发送报警信息到AlertManger&#xff0c;然后我们的AlertManager就来管理这些报警信息&#xff0c;聚合报警信息过后通过email、PagerDu…

linux多线程

文章目录 linux多线程1. 相关概念1.1 线程概念详解线程 VS 进程线程的优点线程的缺点线程异常线程用途 1.2 页表详解 2. 线程控制2.0 POSIX线程库深入理解线程库使用C多线程接口在Linux环境创建多线程 深入理解线程id线程局部存储 2.1 线程的创建 - pthread_create线程创建时参…

计算机毕设 深度学习疫情社交安全距离检测算法 - python opencv cnn

文章目录 0 前言1 课题背景2 实现效果3 相关技术3.1 YOLOV43.2 基于 DeepSort 算法的行人跟踪 4 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两…

OC与Swift的相互调用

OC调用Swift方法 1、在 Build Settings 搜索 Packaging &#xff0c;设置 Defines Module 为 YES 2、新建 LottieBridge.swift 文件&#xff0c;自动生成桥 ProductName-Bridging-Header.h 3、在 LottieBridge.swift 中&#xff0c;定义Swift类继承于OC类&#xff0c;声明 obj…