guru_sdk/guru_ui/lib/widget/image/adaptive_image.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,
);
}
}