import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:math' as math; import 'dart:ui' as ui; import 'package:crypto/crypto.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; // import 'package:fluttertoast/fluttertoast.dart'; import 'package:logging/logging.dart'; import 'package:path_provider/path_provider.dart'; import 'package:share_plus/share_plus.dart'; T? tryCast(dynamic object) => object is T ? object : null; final Logger _log = Logger('utils.dart'); Future loadUiImageFromFile(String filePath) async { final file = await localFile(filePath); final data = await file.readAsBytes(); final Completer completer = Completer(); ui.decodeImageFromList(data, (ui.Image img) { return completer.complete(img); }); return completer.future; } Future loadUiImageFromXFile(XFile xfile) async { final file = File(xfile.path); final data = await file.readAsBytes(); final Completer completer = Completer(); ui.decodeImageFromList(data, (ui.Image img) { return completer.complete(img); }); return completer.future; } Future loadUiImageFromAsset(String assetPath) async { final ByteData data = await rootBundle.load(assetPath); final Completer completer = Completer(); ui.decodeImageFromList(Uint8List.view(data.buffer), (ui.Image img) { return completer.complete(img); }); return completer.future; } Future loadUiImageFromNetwork(String url) async { final ByteData data = await NetworkAssetBundle(Uri.parse(url)).load(''); final Completer completer = Completer(); ui.decodeImageFromList(Uint8List.view(data.buffer), (ui.Image img) { return completer.complete(img); }); return completer.future; } Future> loadJSONFromAsset(String assetPath) async { final jsonStr = await rootBundle.loadString(assetPath); final Map json = jsonDecode(jsonStr); return json; } Future loadFileDataFromAsset(String assetPath) async { final ByteData data = await rootBundle.load(assetPath); return Uint8List.view(data.buffer); } Future> listAssets(String path) async { final manifestContent = await rootBundle.loadString('AssetManifest.json'); final Map manifestMap = jsonDecode(manifestContent); // _log.info('manifestMap: $manifestMap'); final paths = manifestMap.keys.where((String key) => key.contains(path)).toList(); return paths; } Future localDir() async { final Directory dir = await getApplicationDocumentsDirectory(); return dir.path; } Future localFile(String filePath) async { final String path = await localDir(); return File('$path/$filePath'); } // Future> loadJson(String filePath) async { Future loadJson(String filePath) async { File file = await localFile(filePath); final String str = await file.readAsString(); final json = jsonDecode(str); _log.info('json: ${json.runtimeType}'); return json; } Future ensureDirectory(File file) async { Directory directory = file.parent; if (!await directory.exists()) { await directory.create(recursive: true); return true; } return true; } Future saveBytes(String filePath, Uint8List data) async { File file = await localFile(filePath); _log.info('saveImage: file=$file'); await ensureDirectory(file); await file.writeAsBytes(data); return true; } Future saveImage(String filePath, ui.Image image) async { final bytes = await image.toByteData(format: ui.ImageByteFormat.png); if (bytes == null) return false; return saveBytes(filePath, bytes.buffer.asUint8List()); } Future saveJson(String filePath, dynamic data) async { File file = await localFile(filePath); await ensureDirectory(file); String str = jsonEncode(data); await file.writeAsString(str); return true; } Future saveString(String filePath, String data) async { File file = await localFile(filePath); await ensureDirectory(file); await file.writeAsString(data); return true; } Future mosaicImage(ui.Image image) async { ui.PictureRecorder recorder = ui.PictureRecorder(); ui.Canvas canvas = ui.Canvas(recorder); ui.Paint paint = ui.Paint(); canvas.drawImageRect(image, Rect.fromLTWH(0, 0, image.width.toDouble(), image.height.toDouble()), const Rect.fromLTWH(0, 0, 8, 8), paint); ui.Image mosaicImage = await recorder.endRecording().toImage(8, 8); return mosaicImage; } Future mosaicImage2(ui.Image image) async { const int piece = 8; final data = await image.toByteData(); final pixels = data!.buffer.asUint32List(); ui.PictureRecorder recorder = ui.PictureRecorder(); ui.Canvas canvas = ui.Canvas(recorder); ui.Paint paint = ui.Paint(); double width = image.width / piece; double height = image.height / piece; for (int i = 0; i < piece; i++) { for (int j = 0; j < piece; j++) { Rect rect = Rect.fromLTWH(i * width, j * height, width, height); int pixel32 = pixels[rect.center.dy.toInt() * image.width + rect.center.dx.toInt()]; int hex = abgrToArgb(pixel32); Color color = Color(hex); canvas.drawRect(rect, paint..color = color); } } ui.Image mosaicImage = await recorder.endRecording().toImage(image.width, image.height); return mosaicImage; } int abgrToArgb(int argbColor) { int r = (argbColor >> 16) & 0xFF; int b = argbColor & 0xFF; return (argbColor & 0xFF00FF00) | (b << 16) | r; } class TimingLogSplit { String tag; Duration duration; TimingLogSplit(this.tag, this.duration); @override String toString() => '$tag: ${duration.inMilliseconds}'; } class TimingLog { final String label; final DateTime _start = DateTime.now(); DateTime? _lastSplit; final List splits = []; TimingLog(this.label); TimingLog addSplit(String tag) { _lastSplit ??= _start; final current = DateTime.now(); splits.add(TimingLogSplit('$label.$tag', Duration(microseconds: (current.microsecondsSinceEpoch - _lastSplit!.microsecondsSinceEpoch)))); _lastSplit = current; return this; } TimingLog stop() { final current = DateTime.now(); splits.add(TimingLogSplit('$label.total', Duration(microseconds: (current.microsecondsSinceEpoch - _start.microsecondsSinceEpoch)))); return this; } @override String toString() { return splits.join('\n'); } } String md5Hash(String str) { return md5.convert(utf8.encode(str)).toString(); } String niceBytes(bytes, {decimals = 2}) { if (!bytes) return '0 Bytes'; const k = 1024; final dm = decimals < 0 ? 0 : decimals; final sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']; final i = (math.log(bytes) / math.log(k)).floor(); return '${((bytes / math.pow(k, i)).toFixed(dm))} ${sizes[i]}'; } String timeSpentFormat(int seconds) { int hour = seconds ~/ 3600; int minute = seconds % 3600 ~/ 60; int second = seconds % 60; if (hour > 0) { return "${formatNum(hour)}:${formatNum(minute)}:${formatNum(second)}"; } else { return "${formatNum(minute)}:${formatNum(second)}"; } } String formatNum(int num) { return num < 10 ? "0$num" : "$num"; } void checkMemory() { ImageCache imageCache = PaintingBinding.instance.imageCache; debugPrint("liveImageCount = ${imageCache.liveImageCount}"); if (imageCache.liveImageCount >= 100) { debugPrint("liveImageCount = ${imageCache.liveImageCount}, clear!"); imageCache.clear(); imageCache.clearLiveImages(); } } // 辅助扩展:解决 Dart 缺少 firstWhereOrNull 的问题 extension IterableExtension on Iterable { T? firstWhereOrNull(bool Function(T element) test) { for (final element in this) { if (test(element)) { return element; } } return null; } }