Images.dart 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. import 'dart:typed_data';
  2. import 'dart:ui';
  3. import 'package:flutter/foundation.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter_svg/flutter_svg.dart';
  6. import 'package:flutter_svg/svg.dart';
  7. import 'dart:io';
  8. import 'dart:ui' as ui show Codec;
  9. import '../../basic/Common.dart';
  10. import '../../basic/Method.dart';
  11. import '../FilePhotoViewScreen.dart';
  12. // 从本地加载图片
  13. class ResourceFileImageProvider
  14. extends ImageProvider<ResourceFileImageProvider> {
  15. final String path;
  16. final double scale;
  17. ResourceFileImageProvider(this.path, {this.scale = 1.0});
  18. @override
  19. ImageStreamCompleter loadBuffer(
  20. ResourceFileImageProvider key, DecoderBufferCallback decode) {
  21. return MultiFrameImageStreamCompleter(
  22. codec: _loadAsync(key),
  23. scale: key.scale,
  24. );
  25. }
  26. @override
  27. Future<ResourceFileImageProvider> obtainKey(
  28. ImageConfiguration configuration) {
  29. return SynchronousFuture<ResourceFileImageProvider>(this);
  30. }
  31. Future<ui.Codec> _loadAsync(ResourceFileImageProvider key) async {
  32. assert(key == this);
  33. return PaintingBinding.instance
  34. .instantiateImageCodecFromBuffer(await ImmutableBuffer.fromUint8List(await File(path).readAsBytes()));
  35. }
  36. @override
  37. bool operator ==(dynamic other) {
  38. if (other.runtimeType != runtimeType) return false;
  39. final ResourceFileImageProvider typedOther = other;
  40. return path == typedOther.path && scale == typedOther.scale;
  41. }
  42. @override
  43. int get hashCode => Object.hash(path, scale);
  44. @override
  45. String toString() => '$runtimeType('
  46. 'path: ${describeIdentity(path)},'
  47. ' scale: $scale'
  48. ')';
  49. }
  50. // 从本地加载图片
  51. // class ResourceDownloadFileImageProvider
  52. // extends ImageProvider<ResourceDownloadFileImageProvider> {
  53. // final String path;
  54. // final double scale;
  55. //
  56. // ResourceDownloadFileImageProvider(this.path, {this.scale = 1.0});
  57. //
  58. // @override
  59. // ImageStreamCompleter load(
  60. // ResourceDownloadFileImageProvider key, DecoderCallback decode) {
  61. // return MultiFrameImageStreamCompleter(
  62. // codec: _loadAsync(key),
  63. // scale: key.scale,
  64. // );
  65. // }
  66. //
  67. // @override
  68. // Future<ResourceDownloadFileImageProvider> obtainKey(
  69. // ImageConfiguration configuration) {
  70. // return SynchronousFuture<ResourceDownloadFileImageProvider>(this);
  71. // }
  72. //
  73. // Future<ui.Codec> _loadAsync(ResourceDownloadFileImageProvider key) async {
  74. // assert(key == this);
  75. // return PaintingBinding.instance!.instantiateImageCodec(
  76. // await File(await method.downloadImagePath(path)).readAsBytes());
  77. // }
  78. //
  79. // @override
  80. // bool operator ==(dynamic other) {
  81. // if (other.runtimeType != runtimeType) return false;
  82. // final ResourceDownloadFileImageProvider typedOther = other;
  83. // return path == typedOther.path && scale == typedOther.scale;
  84. // }
  85. //
  86. // @override
  87. // int get hashCode => hashValues(path, scale);
  88. //
  89. // @override
  90. // String toString() => '$runtimeType('
  91. // 'path: ${describeIdentity(path)},'
  92. // ' scale: $scale'
  93. // ')';
  94. // }
  95. // 从远端加载图片
  96. // class ResourceRemoteImageProvider
  97. // extends ImageProvider<ResourceRemoteImageProvider> {
  98. // final String fileServer;
  99. // final String path;
  100. // final double scale;
  101. //
  102. // ResourceRemoteImageProvider(this.fileServer, this.path, {this.scale = 1.0});
  103. //
  104. // @override
  105. // ImageStreamCompleter load(
  106. // ResourceRemoteImageProvider key, DecoderCallback decode) {
  107. // return MultiFrameImageStreamCompleter(
  108. // codec: _loadAsync(key),
  109. // scale: key.scale,
  110. // );
  111. // }
  112. //
  113. // @override
  114. // Future<ResourceRemoteImageProvider> obtainKey(
  115. // ImageConfiguration configuration) {
  116. // return SynchronousFuture<ResourceRemoteImageProvider>(this);
  117. // }
  118. //
  119. // Future<ui.Codec> _loadAsync(ResourceRemoteImageProvider key) async {
  120. // assert(key == this);
  121. // var downloadTo = await method.remoteImageData(fileServer, path);
  122. // return PaintingBinding.instance!
  123. // .instantiateImageCodec(await File(downloadTo.finalPath).readAsBytes());
  124. // }
  125. //
  126. // @override
  127. // bool operator ==(dynamic other) {
  128. // if (other.runtimeType != runtimeType) return false;
  129. // final ResourceRemoteImageProvider typedOther = other;
  130. // return fileServer == typedOther.fileServer &&
  131. // path == typedOther.path &&
  132. // scale == typedOther.scale;
  133. // }
  134. //
  135. // @override
  136. // int get hashCode => hashValues(fileServer, path, scale);
  137. //
  138. // @override
  139. // String toString() => '$runtimeType('
  140. // 'fileServer: ${describeIdentity(fileServer)},'
  141. // ' path: ${describeIdentity(path)},'
  142. // ' scale: $scale'
  143. // ')';
  144. // }
  145. // 下载的图片
  146. // class DownloadImage extends StatefulWidget {
  147. // final String path;
  148. // final double? width;
  149. // final double? height;
  150. //
  151. // const DownloadImage({
  152. // Key? key,
  153. // required this.path,
  154. // this.width,
  155. // this.height,
  156. // }) : super(key: key);
  157. //
  158. // @override
  159. // State<StatefulWidget> createState() => _DownloadImageState();
  160. // }
  161. //
  162. // class _DownloadImageState extends State<DownloadImage> {
  163. // late final Future<String> _future = method.downloadImagePath(widget.path);
  164. //
  165. // @override
  166. // Widget build(BuildContext context) {
  167. // return pathFutureImage(
  168. // _future,
  169. // widget.width,
  170. // widget.height,
  171. // context: context,
  172. // );
  173. // }
  174. // }
  175. // 远端图片
  176. class RemoteImage extends StatefulWidget {
  177. final String fileServer;
  178. final String path;
  179. final double? width;
  180. final double? height;
  181. final BoxFit fit;
  182. const RemoteImage({
  183. Key? key,
  184. required this.fileServer,
  185. required this.path,
  186. this.width,
  187. this.height,
  188. this.fit = BoxFit.cover,
  189. }) : super(key: key);
  190. @override
  191. State<StatefulWidget> createState() => _RemoteImageState();
  192. }
  193. class _RemoteImageState extends State<RemoteImage> {
  194. late bool _mock;
  195. late Future<String> _future;
  196. @override
  197. void initState() {
  198. debugPrint("path=${widget.path}");
  199. _mock = widget.fileServer == "" || widget.path == "";
  200. if (!_mock) {
  201. _future = method.downloadImage(widget.path, widget.fileServer).then((value) => value!);
  202. }
  203. super.initState();
  204. }
  205. @override
  206. Widget build(BuildContext context) {
  207. // return buildMock(widget.width, widget.height);
  208. if (_mock) {
  209. return buildMock(widget.width, widget.height);
  210. }
  211. return pathFutureImage(
  212. _future,
  213. widget.width,
  214. widget.height,
  215. fit: widget.fit,
  216. context: context,
  217. );
  218. }
  219. }
  220. Widget pathFutureImage(Future<String> future, double? width, double? height,
  221. {BoxFit fit = BoxFit.cover, BuildContext? context}) {
  222. return FutureBuilder(
  223. future: future,
  224. builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
  225. if (snapshot.hasError) {
  226. print("${snapshot.error}");
  227. print("${snapshot.stackTrace}");
  228. return buildError(width, height);
  229. }
  230. if (snapshot.connectionState != ConnectionState.done) {
  231. return buildLoading(width, height);
  232. }
  233. return buildFile(
  234. snapshot.data!,
  235. width,
  236. height,
  237. fit: fit,
  238. context: context,
  239. );
  240. });
  241. }
  242. // 通用方法
  243. Widget buildSvg(String source, double? width, double? height,
  244. {Color? color, double? margin}) {
  245. var widget = Container(
  246. width: width,
  247. height: height,
  248. padding: margin != null ? const EdgeInsets.all(10) : null,
  249. child: Center(
  250. child: SvgPicture.asset(
  251. source,
  252. width: width,
  253. height: height,
  254. color: color,
  255. ),
  256. ),
  257. );
  258. return GestureDetector(onLongPress: () {}, child: widget);
  259. }
  260. Widget buildMock(double? width, double? height) {
  261. var widget = Container(
  262. width: width,
  263. height: height,
  264. padding: const EdgeInsets.all(10),
  265. child: Center(
  266. child: SvgPicture.asset(
  267. 'lib/assets/unknown.svg',
  268. width: width,
  269. height: height,
  270. color: Colors.grey.shade600,
  271. ),
  272. ),
  273. );
  274. return GestureDetector(onLongPress: () {}, child: widget);
  275. }
  276. Widget buildError(double? width, double? height) {
  277. return Image(
  278. image: const AssetImage('lib/assets/error.png'),
  279. width: width,
  280. height: height,
  281. );
  282. }
  283. Widget buildLoading(double? width, double? height) {
  284. double? size;
  285. if (width != null && height != null) {
  286. size = width < height ? width : height;
  287. }
  288. return SizedBox(
  289. width: width,
  290. height: height,
  291. child: Center(
  292. child: Icon(
  293. Icons.downloading,
  294. size: size,
  295. color: Colors.black12,
  296. ),
  297. ),
  298. );
  299. }
  300. Widget buildFile(String file, double? width, double? height,
  301. {BoxFit fit = BoxFit.cover, BuildContext? context}) {
  302. var image = Image(
  303. image: ResourceFileImageProvider(file),
  304. width: width,
  305. height: height,
  306. errorBuilder: (a, b, c) {
  307. print("$b");
  308. print("$c");
  309. return buildError(width, height);
  310. },
  311. fit: fit,
  312. );
  313. if (context == null) return image;
  314. return GestureDetector(
  315. onLongPress: () async {
  316. String? choose = await chooseListDialog(context, '请选择', ['预览图片', '保存图片']);
  317. switch (choose) {
  318. case '预览图片':
  319. Navigator.of(context).push(MaterialPageRoute(
  320. builder: (context) => FilePhotoViewScreen(file),
  321. ));
  322. break;
  323. case '保存图片':
  324. //TODO: SAVE image
  325. // saveImage(file, context);
  326. break;
  327. }
  328. },
  329. child: image,
  330. );
  331. }