initial
This commit is contained in:
50
lib/tool/encryption_util.dart
Normal file
50
lib/tool/encryption_util.dart
Normal file
@@ -0,0 +1,50 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
import 'package:encrypt/encrypt.dart' as enc;
|
||||
import 'package:archive/archive.dart';
|
||||
|
||||
class EncryptionUtil {
|
||||
// 固定密钥(和后端保持一致)
|
||||
static const String _keyStr = ",Lscj312.;[]sc`1dsajcjc;;wislacx";
|
||||
static const String _ivStr = ",>ew:[7890;,wd[2";
|
||||
|
||||
// 对外简易方法(你 Service / Provider 里直接用)
|
||||
static String encrypt(String data) => encryptAndCompress(data);
|
||||
static String decrypt(String encryptedBase64) => decompressAndDecrypt(encryptedBase64);
|
||||
|
||||
/// 加密:字符串 → GZIP → AES → Base64
|
||||
static String encryptAndCompress(String plainText) {
|
||||
// 强制 32位 Key / 16位 IV(AES 标准)
|
||||
final key = enc.Key.fromUtf8(_keyStr.padRight(32).substring(0, 32));
|
||||
final iv = enc.IV.fromUtf8(_ivStr.padRight(16).substring(0, 16));
|
||||
final encrypter = enc.Encrypter(enc.AES(key, mode: enc.AESMode.cbc));
|
||||
|
||||
// GZIP 压缩
|
||||
final utf8Bytes = utf8.encode(plainText);
|
||||
final compressed = GZipEncoder().encode(utf8Bytes)!;
|
||||
|
||||
// AES 加密
|
||||
final encrypted = encrypter.encryptBytes(
|
||||
Uint8List.fromList(compressed),
|
||||
iv: iv,
|
||||
);
|
||||
|
||||
return encrypted.base64;
|
||||
}
|
||||
|
||||
/// 解密:Base64 → AES → GZIP → 字符串
|
||||
static String decompressAndDecrypt(String encryptedBase64) {
|
||||
final key = enc.Key.fromUtf8(_keyStr.padRight(32).substring(0, 32));
|
||||
final iv = enc.IV.fromUtf8(_ivStr.padRight(16).substring(0, 16));
|
||||
final encrypter = enc.Encrypter(enc.AES(key, mode: enc.AESMode.cbc));
|
||||
|
||||
// 解密
|
||||
final encrypted = enc.Encrypted.fromBase64(encryptedBase64);
|
||||
final decryptedBytes = encrypter.decryptBytes(encrypted, iv: iv);
|
||||
|
||||
// GZIP 解压
|
||||
final decompressed = GZipDecoder().decodeBytes(decryptedBytes);
|
||||
|
||||
return utf8.decode(decompressed);
|
||||
}
|
||||
}
|
||||
117
lib/tool/gradientText.dart
Normal file
117
lib/tool/gradientText.dart
Normal file
@@ -0,0 +1,117 @@
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user