feat: 整体常用控件封装,常用工具类。首页数据对接
This commit is contained in:
1 parent
d90d023e85
commit
fbcd33ea2b
50 files changed
+4558
-264
No files matched your search
@@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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)];
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user