import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:pikapika/basic/Common.dart'; import 'package:pikapika/basic/Cross.dart'; import 'package:pikapika/basic/Method.dart'; import 'package:flutter_svg/svg.dart'; import 'package:pikapika/basic/config/ConvertToPNG.dart'; import 'package:pikapika/basic/config/ImageAddress.dart'; import 'dart:io'; import 'dart:ui' as ui show Codec; import '../FilePhotoViewScreen.dart'; Future _loadImageFile(String path) { if (convertToPNG()) { return method.convertToPNG(path); } return File(path).readAsBytes(); } // 从本地加载图片 class ResourceFileImageProvider extends ImageProvider { final String path; final double scale; ResourceFileImageProvider(this.path, {this.scale = 1.0}); @override ImageStreamCompleter load( ResourceFileImageProvider key, DecoderCallback decode) { return MultiFrameImageStreamCompleter( codec: _loadAsync(key), scale: key.scale, ); } @override Future obtainKey( ImageConfiguration configuration) { return SynchronousFuture(this); } Future _loadAsync(ResourceFileImageProvider key) async { assert(key == this); return PaintingBinding.instance! .instantiateImageCodec(await _loadImageFile(path)); } @override bool operator ==(dynamic other) { if (other.runtimeType != runtimeType) return false; final ResourceFileImageProvider typedOther = other; return path == typedOther.path && scale == typedOther.scale; } @override int get hashCode => hashValues(path, scale); @override String toString() => '$runtimeType(' 'path: ${describeIdentity(path)},' ' scale: $scale' ')'; } // 从本地加载图片 class ResourceDownloadFileImageProvider extends ImageProvider { final String path; final double scale; ResourceDownloadFileImageProvider(this.path, {this.scale = 1.0}); @override ImageStreamCompleter load( ResourceDownloadFileImageProvider key, DecoderCallback decode) { return MultiFrameImageStreamCompleter( codec: _loadAsync(key), scale: key.scale, ); } @override Future obtainKey( ImageConfiguration configuration) { return SynchronousFuture(this); } Future _loadAsync(ResourceDownloadFileImageProvider key) async { assert(key == this); return PaintingBinding.instance!.instantiateImageCodec( await File(await method.downloadImagePath(path)).readAsBytes()); } @override bool operator ==(dynamic other) { if (other.runtimeType != runtimeType) return false; final ResourceDownloadFileImageProvider typedOther = other; return path == typedOther.path && scale == typedOther.scale; } @override int get hashCode => hashValues(path, scale); @override String toString() => '$runtimeType(' 'path: ${describeIdentity(path)},' ' scale: $scale' ')'; } // 从远端加载图片 class ResourceRemoteImageProvider extends ImageProvider { final String fileServer; final String path; final double scale; ResourceRemoteImageProvider(this.fileServer, this.path, {this.scale = 1.0}); @override ImageStreamCompleter load( ResourceRemoteImageProvider key, DecoderCallback decode) { return MultiFrameImageStreamCompleter( codec: _loadAsync(key), scale: key.scale, ); } @override Future obtainKey( ImageConfiguration configuration) { return SynchronousFuture(this); } Future _loadAsync(ResourceRemoteImageProvider key) async { assert(key == this); var downloadTo = await method.remoteImageData(fileServer, path); return PaintingBinding.instance! .instantiateImageCodec(await File(downloadTo.finalPath).readAsBytes()); } @override bool operator ==(dynamic other) { if (other.runtimeType != runtimeType) return false; final ResourceRemoteImageProvider typedOther = other; return fileServer == typedOther.fileServer && path == typedOther.path && scale == typedOther.scale; } @override int get hashCode => hashValues(fileServer, path, scale); @override String toString() => '$runtimeType(' 'fileServer: ${describeIdentity(fileServer)},' ' path: ${describeIdentity(path)},' ' scale: $scale' ')'; } // 下载的图片 class DownloadImage extends StatefulWidget { final String path; final double? width; final double? height; const DownloadImage({ Key? key, required this.path, this.width, this.height, }) : super(key: key); @override State createState() => _DownloadImageState(); } class _DownloadImageState extends State { late final Future _future = method.downloadImagePath(widget.path); @override Widget build(BuildContext context) { return pathFutureImage( _future, widget.width, widget.height, context: context, ); } } // 远端图片 class RemoteImage extends StatefulWidget { final String fileServer; final String path; final double? width; final double? height; final BoxFit fit; const RemoteImage({ Key? key, required this.fileServer, required this.path, this.width, this.height, this.fit = BoxFit.cover, }) : super(key: key); @override State createState() => _RemoteImageState(); } class _RemoteImageState extends State { late bool _mock; late Future _future; @override void initState() { _mock = widget.fileServer == "" || (widget.fileServer.contains(".xyz/") && currentImageAddress() < 0); if (!_mock) { _future = method .remoteImageData(widget.fileServer, widget.path) .then((value) => value.finalPath); } super.initState(); } @override Widget build(BuildContext context) { if (_mock) { return buildMock(widget.width, widget.height); } return pathFutureImage( _future, widget.width, widget.height, fit: widget.fit, context: context, ); } } Widget pathFutureImage(Future future, double? width, double? height, {BoxFit fit = BoxFit.cover, BuildContext? context}) { return FutureBuilder( future: future, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasError) { print("${snapshot.error}"); print("${snapshot.stackTrace}"); return buildError(width, height); } if (snapshot.connectionState != ConnectionState.done) { return buildLoading(width, height); } return buildFile( snapshot.data!, width, height, fit: fit, context: context, ); }); } // 通用方法 Widget buildSvg(String source, double? width, double? height, {Color? color, double? margin}) { var widget = Container( width: width, height: height, padding: margin != null ? const EdgeInsets.all(10) : null, child: Center( child: SvgPicture.asset( source, width: width, height: height, color: color, ), ), ); return GestureDetector(onLongPress: () {}, child: widget); } Widget buildMock(double? width, double? height) { var widget = Container( width: width, height: height, padding: const EdgeInsets.all(10), child: Center( child: SvgPicture.asset( 'lib/assets/unknown.svg', width: width, height: height, color: Colors.grey.shade600, ), ), ); return GestureDetector(onLongPress: () {}, child: widget); } Widget buildError(double? width, double? height) { return Image( image: AssetImage('lib/assets/error.png'), width: width, height: height, ); } Widget buildLoading(double? width, double? height) { double? size; if (width != null && height != null) { size = width < height ? width : height; } return SizedBox( width: width, height: height, child: Center( child: Icon( Icons.downloading, size: size, color: Colors.black12, ), ), ); } Widget buildFile(String file, double? width, double? height, {BoxFit fit = BoxFit.cover, BuildContext? context}) { var image = Image( image: ResourceFileImageProvider(file), width: width, height: height, errorBuilder: (a, b, c) { print("$b"); print("$c"); return buildError(width, height); }, fit: fit, ); if (context == null) return image; return GestureDetector( onLongPress: () async { String? choose = await chooseListDialog(context, '请选择', ['预览图片', '保存图片']); switch (choose) { case '预览图片': Navigator.of(context).push(MaterialPageRoute( builder: (context) => FilePhotoViewScreen(file), )); break; case '保存图片': saveImage(file, context); break; } }, child: image, ); }