import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:pikapi/basic/Method.dart'; import 'package:flutter_svg/svg.dart'; import 'dart:io'; import 'dart:ui' as ui show Codec; // 从本地加载图片 class PicaFileImageProvider extends ImageProvider { final String path; final double scale; PicaFileImageProvider(this.path, {this.scale = 1.0}); @override ImageStreamCompleter load(PicaFileImageProvider key, DecoderCallback decode) { return MultiFrameImageStreamCompleter( codec: _loadAsync(key), scale: key.scale, ); } @override Future obtainKey(ImageConfiguration configuration) { return SynchronousFuture(this); } Future _loadAsync(PicaFileImageProvider key) async { assert(key == this); return PaintingBinding.instance! .instantiateImageCodec(await File(path).readAsBytes()); } @override bool operator ==(dynamic other) { if (other.runtimeType != runtimeType) return false; final PicaFileImageProvider 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 PicaDownloadFileImageProvider extends ImageProvider { final String path; final double scale; PicaDownloadFileImageProvider(this.path, {this.scale = 1.0}); @override ImageStreamCompleter load( PicaDownloadFileImageProvider key, DecoderCallback decode) { return MultiFrameImageStreamCompleter( codec: _loadAsync(key), scale: key.scale, ); } @override Future obtainKey( ImageConfiguration configuration) { return SynchronousFuture(this); } Future _loadAsync(PicaDownloadFileImageProvider 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 PicaDownloadFileImageProvider 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' ')'; } // 从远端加载图片 暂时未使用 (现在都是先获取路径然后再通过file显示) class PicaRemoteImageProvider extends ImageProvider { final String fileServer; final String path; final double scale; PicaRemoteImageProvider(this.fileServer, this.path, {this.scale = 1.0}); @override ImageStreamCompleter load( PicaRemoteImageProvider key, DecoderCallback decode) { return MultiFrameImageStreamCompleter( codec: _loadAsync(key), scale: key.scale, ); } @override Future obtainKey(ImageConfiguration configuration) { return SynchronousFuture(this); } Future _loadAsync(PicaRemoteImageProvider 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 PicaRemoteImageProvider 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 Future _future = method.downloadImagePath(widget.path); @override Widget build(BuildContext context) { return pathFutureImage(_future, widget.width, widget.height); } } // 远端图片 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/"); 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); } } // 通用方法 Widget buildSvg(String source, double? width, double? height, {Color? color, double? margin}) { return Container( width: width, height: height, padding: margin != null ? EdgeInsets.all(10) : null, child: Center( child: SvgPicture.asset( source, width: width, height: height, color: color, ), ), ); } Widget buildMock(double? width, double? height) { return Container( width: width, height: height, padding: EdgeInsets.all(10), child: Center( child: SvgPicture.asset( 'lib/assets/unknown.svg', width: width, height: height, color: Colors.grey.shade600, ), ), ); } Widget buildError(double? width, double? height) { return Image( image: AssetImage('lib/assets/error.png'), width: width, height: height, ); } Widget buildLoading(double? width, double? height) { return Container( width: width, height: height, child: Center( child: Icon( Icons.downloading, size: width, color: Colors.black12, ), ), ); } Widget buildFile(String file, double? width, double? height, {BoxFit fit = BoxFit.cover}) { return Image( image: PicaFileImageProvider(file), width: width, height: height, errorBuilder: (a, b, c) { print("$b"); print("$c"); return buildError(width, height); }, fit: fit, ); } Widget pathFutureImage(Future future, double? width, double? height, {BoxFit fit = BoxFit.cover}) { 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); }); }