import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:guru_utils/http/http_ex.dart'; import 'package:guru_utils/uri/uri_utils.dart'; /// Created by Haoyi on 2022/9/7 class AdaptiveImage extends StatelessWidget { final String uri; /// If non-null, require the image to have this width. /// /// If null, the image will pick a size that best preserves its intrinsic /// aspect ratio. /// /// It is strongly recommended that either both the [width] and the [height] /// be specified, or that the widget be placed in a context that sets tight /// layout constraints, so that the image does not change size as it loads. /// Consider using [fit] to adapt the image's rendering to fit the given width /// and height if the exact image dimensions are not known in advance. final double? width; /// If non-null, require the image to have this height. /// /// If null, the image will pick a size that best preserves its intrinsic /// aspect ratio. /// /// It is strongly recommended that either both the [width] and the [height] /// be specified, or that the widget be placed in a context that sets tight /// layout constraints, so that the image does not change size as it loads. /// Consider using [fit] to adapt the image's rendering to fit the given width /// and height if the exact image dimensions are not known in advance. final double? height; /// If non-null, this color is blended with each image pixel using [colorBlendMode]. final Color? color; /// If non-null, the value from the [Animation] is multiplied with the opacity /// of each image pixel before painting onto the canvas. /// /// This is more efficient than using [FadeTransition] to change the opacity /// of an image, since this avoids creating a new composited layer. Composited /// layers may double memory usage as the image is painted onto an offscreen /// render target. /// /// See also: /// /// * [AlwaysStoppedAnimation], which allows you to create an [Animation] /// from a single opacity value. final Animation? opacity; /// The rendering quality of the image. /// /// If the image is of a high quality and its pixels are perfectly aligned /// with the physical screen pixels, extra quality enhancement may not be /// necessary. If so, then [FilterQuality.none] would be the most efficient. /// /// If the pixels are not perfectly aligned with the screen pixels, or if the /// image itself is of a low quality, [FilterQuality.none] may produce /// undesirable artifacts. Consider using other [FilterQuality] values to /// improve the rendered image quality in this case. Pixels may be misaligned /// with the screen pixels as a result of transforms or scaling. /// /// See also: /// /// * [FilterQuality], the enum containing all possible filter quality /// options. final FilterQuality filterQuality; /// Used to combine [color] with this image. /// /// The default is [BlendMode.srcIn]. In terms of the blend mode, [color] is /// the source and this image is the destination. /// /// See also: /// /// * [BlendMode], which includes an illustration of the effect of each blend mode. final BlendMode? colorBlendMode; /// How to inscribe the image into the space allocated during layout. /// /// The default varies based on the other fields. See the discussion at /// [paintImage]. final BoxFit? fit; /// How to align the image within its bounds. /// /// The alignment aligns the given position in the image to the given position /// in the layout bounds. For example, an [Alignment] alignment of (-1.0, /// -1.0) aligns the image to the top-left corner of its layout bounds, while an /// [Alignment] alignment of (1.0, 1.0) aligns the bottom right of the /// image with the bottom right corner of its layout bounds. Similarly, an /// alignment of (0.0, 1.0) aligns the bottom middle of the image with the /// middle of the bottom edge of its layout bounds. /// /// To display a subpart of an image, consider using a [CustomPainter] and /// [Canvas.drawImageRect]. /// /// If the [alignment] is [TextDirection]-dependent (i.e. if it is a /// [AlignmentDirectional]), then an ambient [Directionality] widget /// must be in scope. /// /// Defaults to [Alignment.center]. /// /// See also: /// /// * [Alignment], a class with convenient constants typically used to /// specify an [AlignmentGeometry]. /// * [AlignmentDirectional], like [Alignment] for specifying alignments /// relative to text direction. final AlignmentGeometry alignment; /// A builder that specifies the widget to display to the user while an image /// is still loading. /// /// If this is null, and the image is loaded incrementally (e.g. over a /// network), the user will receive no indication of the progress as the /// bytes of the image are loaded. /// /// For more information on how to interpret the arguments that are passed to /// this builder, see the documentation on [ImageLoadingBuilder]. /// /// ## Performance implications /// /// If a [loadingBuilder] is specified for an image, the [Image] widget is /// likely to be rebuilt on every /// [rendering pipeline frame](rendering/RendererBinding/drawFrame.html) until /// the image has loaded. This is useful for cases such as displaying a loading /// progress indicator, but for simpler cases such as displaying a placeholder /// widget that doesn't depend on the loading progress (e.g. static "loading" /// text), [frameBuilder] will likely work and not incur as much cost. /// /// ## Chaining with [frameBuilder] /// /// If a [frameBuilder] has _also_ been specified for an image, the two /// builders will be chained together: the `child` argument to this /// builder will contain the _result_ of the [frameBuilder]. For example, /// consider the following builders used in conjunction: /// /// {@macro flutter.widgets.Image.frameBuilder.chainedBuildersExample} /// /// {@tool dartpad} /// The following sample uses [loadingBuilder] to show a /// [CircularProgressIndicator] while an image loads over the network. /// /// ** See code in examples/api/lib/widgets/image/image.loading_builder.0.dart ** /// {@end-tool} /// /// Run against a real-world image on a slow network, the previous example /// renders the following loading progress indicator while the image loads /// before rendering the completed image. /// /// {@animation 400 400 https://flutter.github.io/assets-for-api-docs/assets/widgets/loading_progress_image.mp4} final ImageLoadingBuilder? loadingBuilder; /// A builder function that is called if an error occurs during image loading. /// /// If this builder is not provided, any exceptions will be reported to /// [FlutterError.onError]. If it is provided, the caller should either handle /// the exception by providing a replacement widget, or rethrow the exception. /// /// {@tool dartpad} /// The following sample uses [errorBuilder] to show a '😢' in place of the /// image that fails to load, and prints the error to the console. /// /// ** See code in examples/api/lib/widgets/image/image.error_builder.0.dart ** /// {@end-tool} final ImageErrorWidgetBuilder? errorBuilder; final UriType uriType; AdaptiveImage(this.uri, {Key? key, this.width, this.height, this.loadingBuilder, this.errorBuilder, this.color, this.opacity, this.colorBlendMode, this.fit, this.alignment = Alignment.center, this.filterQuality = FilterQuality.low}) : uriType = UriUtils.parseUriType(uri), super(key: key); @override Widget build(BuildContext context) { late ImageProvider _provider; switch (uriType) { case UriType.assets: _provider = AssetImage(uri); break; case UriType.file: _provider = FileImage(File(uri)); break; default: _provider = CachedNetworkImageProvider(HttpEx.selectCdn(Uri.parse(uri)).uri.toString()); break; } return Image( image: _provider, width: width, height: height, color: color, opacity: opacity, colorBlendMode: colorBlendMode, fit: fit, alignment: alignment, filterQuality: filterQuality, loadingBuilder: loadingBuilder, errorBuilder: errorBuilder, ); } }