utils.dart 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:io';
  4. import 'dart:math' as math;
  5. import 'dart:ui' as ui;
  6. import 'package:crypto/crypto.dart';
  7. import 'package:flutter/material.dart';
  8. import 'package:flutter/services.dart';
  9. // import 'package:fluttertoast/fluttertoast.dart';
  10. import 'package:logging/logging.dart';
  11. import 'package:path_provider/path_provider.dart';
  12. import 'package:share_plus/share_plus.dart';
  13. T? tryCast<T>(dynamic object) => object is T ? object : null;
  14. final Logger _log = Logger('utils.dart');
  15. Future<ui.Image> loadUiImageFromFile(String filePath) async {
  16. final file = await localFile(filePath);
  17. final data = await file.readAsBytes();
  18. final Completer<ui.Image> completer = Completer();
  19. ui.decodeImageFromList(data, (ui.Image img) {
  20. return completer.complete(img);
  21. });
  22. return completer.future;
  23. }
  24. Future<ui.Image> loadUiImageFromXFile(XFile xfile) async {
  25. final file = File(xfile.path);
  26. final data = await file.readAsBytes();
  27. final Completer<ui.Image> completer = Completer();
  28. ui.decodeImageFromList(data, (ui.Image img) {
  29. return completer.complete(img);
  30. });
  31. return completer.future;
  32. }
  33. Future<ui.Image> loadUiImageFromAsset(String assetPath) async {
  34. final ByteData data = await rootBundle.load(assetPath);
  35. final Completer<ui.Image> completer = Completer();
  36. ui.decodeImageFromList(Uint8List.view(data.buffer), (ui.Image img) {
  37. return completer.complete(img);
  38. });
  39. return completer.future;
  40. }
  41. Future<ui.Image> loadUiImageFromNetwork(String url) async {
  42. final ByteData data = await NetworkAssetBundle(Uri.parse(url)).load('');
  43. final Completer<ui.Image> completer = Completer();
  44. ui.decodeImageFromList(Uint8List.view(data.buffer), (ui.Image img) {
  45. return completer.complete(img);
  46. });
  47. return completer.future;
  48. }
  49. Future<Map<String, dynamic>> loadJSONFromAsset(String assetPath) async {
  50. final jsonStr = await rootBundle.loadString(assetPath);
  51. final Map<String, dynamic> json = jsonDecode(jsonStr);
  52. return json;
  53. }
  54. Future<Uint8List> loadFileDataFromAsset(String assetPath) async {
  55. final ByteData data = await rootBundle.load(assetPath);
  56. return Uint8List.view(data.buffer);
  57. }
  58. Future<List<String>> listAssets(String path) async {
  59. final manifestContent = await rootBundle.loadString('AssetManifest.json');
  60. final Map<String, dynamic> manifestMap = jsonDecode(manifestContent);
  61. // _log.info('manifestMap: $manifestMap');
  62. final paths = manifestMap.keys.where((String key) => key.contains(path)).toList();
  63. return paths;
  64. }
  65. Future<String> localDir() async {
  66. final Directory dir = await getApplicationDocumentsDirectory();
  67. return dir.path;
  68. }
  69. Future<File> localFile(String filePath) async {
  70. final String path = await localDir();
  71. return File('$path/$filePath');
  72. }
  73. // Future<Map<String, dynamic>> loadJson(String filePath) async {
  74. Future<Object> loadJson(String filePath) async {
  75. File file = await localFile(filePath);
  76. final String str = await file.readAsString();
  77. final json = jsonDecode(str);
  78. _log.info('json: ${json.runtimeType}');
  79. return json;
  80. }
  81. Future<bool> ensureDirectory(File file) async {
  82. Directory directory = file.parent;
  83. if (!await directory.exists()) {
  84. await directory.create(recursive: true);
  85. return true;
  86. }
  87. return true;
  88. }
  89. Future<bool> saveBytes(String filePath, Uint8List data) async {
  90. File file = await localFile(filePath);
  91. _log.info('saveImage: file=$file');
  92. await ensureDirectory(file);
  93. await file.writeAsBytes(data);
  94. return true;
  95. }
  96. Future<bool> saveImage(String filePath, ui.Image image) async {
  97. final bytes = await image.toByteData(format: ui.ImageByteFormat.png);
  98. if (bytes == null) return false;
  99. return saveBytes(filePath, bytes.buffer.asUint8List());
  100. }
  101. Future<bool> saveJson(String filePath, dynamic data) async {
  102. File file = await localFile(filePath);
  103. await ensureDirectory(file);
  104. String str = jsonEncode(data);
  105. await file.writeAsString(str);
  106. return true;
  107. }
  108. Future<bool> saveString(String filePath, String data) async {
  109. File file = await localFile(filePath);
  110. await ensureDirectory(file);
  111. await file.writeAsString(data);
  112. return true;
  113. }
  114. Future<ui.Image> mosaicImage(ui.Image image) async {
  115. ui.PictureRecorder recorder = ui.PictureRecorder();
  116. ui.Canvas canvas = ui.Canvas(recorder);
  117. ui.Paint paint = ui.Paint();
  118. canvas.drawImageRect(image, Rect.fromLTWH(0, 0, image.width.toDouble(), image.height.toDouble()), const Rect.fromLTWH(0, 0, 8, 8), paint);
  119. ui.Image mosaicImage = await recorder.endRecording().toImage(8, 8);
  120. return mosaicImage;
  121. }
  122. Future<ui.Image> mosaicImage2(ui.Image image) async {
  123. const int piece = 8;
  124. final data = await image.toByteData();
  125. final pixels = data!.buffer.asUint32List();
  126. ui.PictureRecorder recorder = ui.PictureRecorder();
  127. ui.Canvas canvas = ui.Canvas(recorder);
  128. ui.Paint paint = ui.Paint();
  129. double width = image.width / piece;
  130. double height = image.height / piece;
  131. for (int i = 0; i < piece; i++) {
  132. for (int j = 0; j < piece; j++) {
  133. Rect rect = Rect.fromLTWH(i * width, j * height, width, height);
  134. int pixel32 = pixels[rect.center.dy.toInt() * image.width + rect.center.dx.toInt()];
  135. int hex = abgrToArgb(pixel32);
  136. Color color = Color(hex);
  137. canvas.drawRect(rect, paint..color = color);
  138. }
  139. }
  140. ui.Image mosaicImage = await recorder.endRecording().toImage(image.width, image.height);
  141. return mosaicImage;
  142. }
  143. int abgrToArgb(int argbColor) {
  144. int r = (argbColor >> 16) & 0xFF;
  145. int b = argbColor & 0xFF;
  146. return (argbColor & 0xFF00FF00) | (b << 16) | r;
  147. }
  148. class TimingLogSplit {
  149. String tag;
  150. Duration duration;
  151. TimingLogSplit(this.tag, this.duration);
  152. @override
  153. String toString() => '$tag: ${duration.inMilliseconds}';
  154. }
  155. class TimingLog {
  156. final String label;
  157. final DateTime _start = DateTime.now();
  158. DateTime? _lastSplit;
  159. final List<TimingLogSplit> splits = [];
  160. TimingLog(this.label);
  161. TimingLog addSplit(String tag) {
  162. _lastSplit ??= _start;
  163. final current = DateTime.now();
  164. splits.add(TimingLogSplit('$label.$tag', Duration(microseconds: (current.microsecondsSinceEpoch - _lastSplit!.microsecondsSinceEpoch))));
  165. _lastSplit = current;
  166. return this;
  167. }
  168. TimingLog stop() {
  169. final current = DateTime.now();
  170. splits.add(TimingLogSplit('$label.total', Duration(microseconds: (current.microsecondsSinceEpoch - _start.microsecondsSinceEpoch))));
  171. return this;
  172. }
  173. @override
  174. String toString() {
  175. return splits.join('\n');
  176. }
  177. }
  178. String md5Hash(String str) {
  179. return md5.convert(utf8.encode(str)).toString();
  180. }
  181. String niceBytes(bytes, {decimals = 2}) {
  182. if (!bytes) return '0 Bytes';
  183. const k = 1024;
  184. final dm = decimals < 0 ? 0 : decimals;
  185. final sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
  186. final i = (math.log(bytes) / math.log(k)).floor();
  187. return '${((bytes / math.pow(k, i)).toFixed(dm))} ${sizes[i]}';
  188. }
  189. String timeSpentFormat(int seconds) {
  190. int hour = seconds ~/ 3600;
  191. int minute = seconds % 3600 ~/ 60;
  192. int second = seconds % 60;
  193. if (hour > 0) {
  194. return "${formatNum(hour)}:${formatNum(minute)}:${formatNum(second)}";
  195. } else {
  196. return "${formatNum(minute)}:${formatNum(second)}";
  197. }
  198. }
  199. String formatNum(int num) {
  200. return num < 10 ? "0$num" : "$num";
  201. }
  202. void checkMemory() {
  203. ImageCache imageCache = PaintingBinding.instance.imageCache;
  204. debugPrint("liveImageCount = ${imageCache.liveImageCount}");
  205. if (imageCache.liveImageCount >= 100) {
  206. debugPrint("liveImageCount = ${imageCache.liveImageCount}, clear!");
  207. imageCache.clear();
  208. imageCache.clearLiveImages();
  209. }
  210. }
  211. // 辅助扩展:解决 Dart 缺少 firstWhereOrNull 的问题
  212. extension IterableExtension<T> on Iterable<T> {
  213. T? firstWhereOrNull(bool Function(T element) test) {
  214. for (final element in this) {
  215. if (test(element)) {
  216. return element;
  217. }
  218. }
  219. return null;
  220. }
  221. }