117 lines
3.4 KiB
Dart
117 lines
3.4 KiB
Dart
import 'package:flutter/material.dart';
|
||
|
||
class GradientLayer {
|
||
final Gradient gradient;
|
||
final BlendMode blendMode;
|
||
|
||
const GradientLayer({
|
||
required this.gradient,
|
||
this.blendMode = BlendMode.srcIn, // 默认第一层用 srcIn 打底
|
||
});
|
||
}
|
||
|
||
class GradientText extends StatelessWidget {
|
||
final String data;
|
||
final TextStyle? style;
|
||
final TextAlign? textAlign;
|
||
final TextDirection? textDirection;
|
||
final bool softWrap;
|
||
final TextOverflow? overflow;
|
||
final double? textScaleFactor;
|
||
final int? maxLines;
|
||
final Locale? locale;
|
||
final String? semanticsLabel;
|
||
final TextWidthBasis? textWidthBasis;
|
||
final TextHeightBehavior? textHeightBehavior;
|
||
final List<GradientLayer> gradientLayers;
|
||
|
||
const GradientText({
|
||
Key? key,
|
||
required this.data,
|
||
this.style,
|
||
this.textAlign,
|
||
this.textDirection,
|
||
this.softWrap = true,
|
||
this.overflow,
|
||
this.textScaleFactor,
|
||
this.maxLines,
|
||
this.locale,
|
||
this.semanticsLabel,
|
||
this.textWidthBasis,
|
||
this.textHeightBehavior,
|
||
this.gradientLayers = const [],
|
||
}) : super(key: key);
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
if (gradientLayers.isEmpty) {
|
||
return Text(
|
||
data,
|
||
style: style,
|
||
textAlign: textAlign,
|
||
textDirection: textDirection,
|
||
softWrap: softWrap,
|
||
overflow: overflow,
|
||
textScaleFactor: textScaleFactor,
|
||
maxLines: maxLines,
|
||
locale: locale,
|
||
semanticsLabel: semanticsLabel,
|
||
textWidthBasis: textWidthBasis,
|
||
textHeightBehavior: textHeightBehavior,
|
||
);
|
||
}
|
||
|
||
final baseStyle = style ?? const TextStyle();
|
||
|
||
return Stack(
|
||
children: [
|
||
// 1. 最底层:纯色文本(可选,防止所有渐变都透明时看不见)
|
||
// 如果第一层渐变是 srcIn,这一层其实可以被遮挡,但保留它作为“备用底色”
|
||
Opacity(
|
||
opacity: 0.0, // 隐藏,仅占位
|
||
child: _buildText(baseStyle.copyWith(color: Colors.black)),
|
||
),
|
||
|
||
// 2. 叠加渐变层
|
||
...gradientLayers.asMap().entries.map((entry) {
|
||
final index = entry.key;
|
||
final layer = entry.value;
|
||
|
||
// 关键逻辑:
|
||
// 如果是第一层且用户没改 blendMode,默认用 srcIn 确保文字有颜色
|
||
// 如果是后续层,通常希望融合,所以建议用户传入 overlay/plus 等
|
||
|
||
return ShaderMask(
|
||
shaderCallback: (Rect bounds) {
|
||
return layer.gradient.createShader(bounds);
|
||
},
|
||
blendMode: layer.blendMode,
|
||
child: _buildText(
|
||
// 重要:为了融合效果,内部文本通常是白色或浅色
|
||
// 如果 blendMode 是 srcIn,颜色不重要(会被渐变替换)
|
||
// 如果 blendMode 是 overlay/plus,白色能让渐变颜色最纯正地显示
|
||
baseStyle.copyWith(color: Colors.white),
|
||
),
|
||
);
|
||
}).toList(),
|
||
],
|
||
);
|
||
}
|
||
|
||
Widget _buildText(TextStyle effectiveStyle) {
|
||
return Text(
|
||
data,
|
||
style: effectiveStyle,
|
||
textAlign: textAlign,
|
||
textDirection: textDirection,
|
||
softWrap: softWrap,
|
||
overflow: overflow,
|
||
textScaleFactor: textScaleFactor,
|
||
maxLines: maxLines,
|
||
locale: locale,
|
||
semanticsLabel: semanticsLabel,
|
||
textWidthBasis: textWidthBasis,
|
||
textHeightBehavior: textHeightBehavior,
|
||
);
|
||
}
|
||
} |