217 lines
8.4 KiB
Dart
217 lines
8.4 KiB
Dart
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<double>? 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,
|
|
);
|
|
}
|
|
} |