feat: 整体常用控件封装,常用工具类。首页数据对接

This commit is contained in:
chengsiwen committed 2026-06-27 14:15:25 +08:00
1 parent d90d023e85
commit fbcd33ea2b
50 files changed
+4558 -264

No files matched your search

+155
View File
@@ -0,0 +1,155 @@
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'dart:math';
class SkyEncrypt {
static const int bfSize = 2048;
static const String enStrTag = r'$';
// Generates a random salt of size between 16 and 64 bytes
static Uint8List randSalt() {
final rand = Random.secure();
final size = rand.nextInt(49) + 16; // 16..64
return Uint8List.fromList(List.generate(size, (_) => rand.nextInt(256)));
}
// Encrypts the given data with a random salt
static Uint8List en(Uint8List data) {
if (data.isEmpty) return data;
final salt = randSalt();
final encryptedData = enWithSalt(data, salt);
return Uint8List.fromList([salt.length, ...salt, ...encryptedData]);
}
// Decrypts the given encrypted data
static Uint8List de(Uint8List data) {
if (data.isEmpty) return data;
final saltLen = data[0];
final salt = data.sublist(1, 1 + saltLen);
final encrypted = data.sublist(1 + saltLen);
return deWithSalt(encrypted, salt);
}
// Encrypts data with a specified salt
static Uint8List enWithSalt(Uint8List data, Uint8List salt) {
final mixedData = mixSalt(data, salt);
return cxEd(mixedData);
}
// Decrypts data with a specified salt
static Uint8List deWithSalt(Uint8List data, Uint8List salt) {
final decryptedData = cxEd(data);
return removeSalt(decryptedData, salt);
}
// Simple XOR-based encryption/decryption (bitwise negation)
static Uint8List cxEd(Uint8List data) {
return Uint8List.fromList(data.map((b) => b ^ 0xFF).toList());
}
// Apply salt to the data for encryption
static Uint8List mixSalt(Uint8List data, Uint8List salt) {
if (salt.isEmpty) return data;
return Uint8List.fromList(
List.generate(data.length, (i) {
final s = salt[i % salt.length];
return calSalt(data[i], s);
}),
);
}
// Remove salt from the data after decryption
static Uint8List removeSalt(Uint8List data, Uint8List salt) {
if (salt.isEmpty) return data;
return Uint8List.fromList(
List.generate(data.length, (i) {
final s = salt[i % salt.length];
return calRemoveSalt(data[i], s);
}),
);
}
// Calculate a modified value after mixing salt (for encryption)
static int calSalt(int v, int s) {
final r = v ^ 0xFF;
return s > r ? (s - r - 1) & 0xFF : (v + s) & 0xFF;
}
// Reverse the salt mixing process for decryption
static int calRemoveSalt(int v, int s) {
return v >= s ? (v - s) & 0xFF : (0xFF - (s - v) + 1) & 0xFF;
}
// Encrypt a string and return the hex representation
static String cxEStr(String data) {
return cxEStrAsBytes(
data,
).map((b) => b.toRadixString(16).padLeft(2, '0')).join();
}
// Encrypt string and return as bytes
static Uint8List cxEStrAsBytes(String data) {
return cxEd(Uint8List.fromList(utf8.encode(data)));
}
// Decrypt a hex string to a regular string
static String cxDStr(String data) {
final bytes = Uint8List.fromList([
for (int i = 0; i < data.length; i += 2)
int.parse(data.substring(i, i + 2), radix: 16),
]);
return utf8.decode(cxEd(bytes));
}
// Encrypt string data and return in the form of hex string with prefix
static String enStr(String data) {
return enBytesStr(Uint8List.fromList(utf8.encode(data)));
}
// Encrypt bytes and return as hex string with prefix
static String enBytesStr(Uint8List data) {
final encrypted = en(data);
return '$enStrTag${encrypted.map((b) => b.toRadixString(16).padLeft(2, '0')).join()}';
}
// Decrypt a string with encoded prefix (e.g., "$" symbol)
static String deStr(String data) {
return utf8.decode(deStrBytes(data));
}
// Decrypt the byte array extracted from a string
static Uint8List deStrBytes(String data) {
if (!data.startsWith(enStrTag)) {
throw ArgumentError("Invalid encoded string");
}
final hexData = data.substring(1);
final bytes = Uint8List.fromList([
for (int i = 0; i < hexData.length; i += 2)
int.parse(hexData.substring(i, i + 2), radix: 16),
]);
return de(bytes);
}
// Copy encrypted data from a stream to another stream
static Future<int> copy(
Stream<List<int>> reader,
StreamSink<List<int>> writer,
) async {
int total = 0;
await for (final chunk in reader) {
total += chunk.length;
final encrypted = chunk.map((b) => b ^ 0xFF).toList();
writer.add(encrypted);
}
await writer.close();
return total;
}
// Custom encryption with condition (even and odd byte values handled differently)
static Uint8List cxEd1(Uint8List data) {
return Uint8List.fromList(
data.map((b) => (b % 2 == 1) ? (b ^ 0xFF) : b).toList(),
);
}
}
+146
View File
@@ -0,0 +1,146 @@
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:talker_flutter/talker_flutter.dart';
class SkyLog {
SkyLog._();
static final SkyLog _instance = SkyLog._();
static SkyLog get instance => _instance;
final Talker _talker = TalkerFlutter.init();
factory SkyLog() => _instance;
Talker get tLog => _talker;
/// DevTools / 控制台里可按名称过滤。
static const String logName = 'SkyLog';
Future<void> initLogger() async {
/// app 信息
}
/// 仅 [kDebugMode] 输出;内容经安全格式化,不会因无法 JSON 编码而中断业务逻辑。
static void print(
Object? message, {
String? tag,
String name = logName,
}) {
if (!kDebugMode) return;
try {
final text = _stringify(message);
final line = tag != null ? '[debug-tag: $tag] ===== $text' : text;
log(line, name: name);
} catch (e, st) {
log(
'[SkyLog.print failed] $e',
name: name,
error: e,
stackTrace: st,
);
}
}
/// 尽量输出缩进 JSON;无法编码时退回 [toString],不向外抛错。
static String _stringify(Object? value) {
if (value == null) return 'null';
if (value is String) return value;
if (value is num || value is bool) return value.toString();
const encoder = JsonEncoder.withIndent(' ');
if (value is Map) {
try {
return encoder.convert(value);
} catch (_) {
return value.toString();
}
}
if (value is List) {
try {
return encoder.convert(value);
} catch (_) {
return value.toString();
}
}
if (value is Iterable) {
try {
return encoder.convert(value.toList());
} catch (_) {
return value.toString();
}
}
try {
return encoder.convert(value);
} catch (_) {
try {
return '${value.runtimeType}: $value';
} catch (_) {
return '<unprintable ${value.runtimeType}>';
}
}
}
/// 错误日志走 Talker;若 Talker 抛错,在 Debug 下用 [log] 兜底,仍不向外抛。
static void error(
Object? message, {
String? tag,
StackTrace? stackTrace,
}) {
final text = message == null ? 'null' : '$message';
try {
_instance._talker.logCustom(
CustomLog(
text,
settings: _instance._talker.settings,
tag: tag,
logLevel: LogLevel.error,
stackTrace: stackTrace,
),
);
} catch (e, st) {
if (kDebugMode) {
log(
'[SkyLog.error fallback] $text | $e',
name: logName,
error: e,
stackTrace: st,
);
}
}
}
}
class CustomLog extends TalkerLog {
final String? tag;
final TalkerSettings settings;
final LogLevel? _logLevel;
final StackTrace? _stackTrace;
CustomLog(
super.message, {
this.tag,
required this.settings,
LogLevel logLevel = LogLevel.debug,
StackTrace? stackTrace,
}) : _logLevel = logLevel,
_stackTrace = stackTrace;
@override
String? get key => tag;
@override
LogLevel? get logLevel => _logLevel;
@override
StackTrace? get stackTrace => _stackTrace;
@override
AnsiPen? get pen =>
settings.colors[TalkerKey.fromLogLevel(logLevel ?? LogLevel.info)];
}
+35
View File
@@ -0,0 +1,35 @@
import 'package:get/get.dart';
import 'package:skywood/pages/splash/splash_page.dart';
class SkyRouters {
static const String splash = '/splash';
}
class SkyPages {
static const initial = SkyRouters.splash;
static final List<GetPage> routes = [
routePage(name: SkyRouters.splash, page: ()=> SplashPage())
];
}
GetPage routePage({
required String name,
required GetPageBuilder page,
Bindings? binding,
Transition transition = Transition.rightToLeft,
Duration transitionDuration = const Duration(milliseconds: 300),
CustomTransition? customTransition,
bool popGesture = true, // Allow iOS swipe-back gesture by default
}) {
return GetPage(
name: name,
page: page,
binding: binding,
transition: transition,
transitionDuration: transitionDuration,
customTransition: customTransition,
popGesture: popGesture, // Pass through to GetPage
);
}