parent
428be90768
commit
3b887bec47
@ -1,7 +1,7 @@
|
|||||||
<<skip until matching line>>
|
<<skip until matching line>>
|
||||||
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
|
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
|
||||||
The following assertion was thrown building Listener
|
The following assertion was thrown building Listener
|
||||||
'package:flutter\/src\/painting\/basic_types\.dart': Failed assertion: line 225 pos 10: 'textDirection
|
'package:flutter\/src\/painting\/basic_types\.dart': Failed assertion: line 222 pos 10: 'textDirection
|
||||||
!= null': is not true\.
|
!= null': is not true\.
|
||||||
|
|
||||||
Either the assertion indicates an error in the framework itself, or we should provide substantially
|
Either the assertion indicates an error in the framework itself, or we should provide substantially
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<<skip until matching line>>
|
<<skip until matching line>>
|
||||||
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
|
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
|
||||||
The following assertion was thrown building Listener
|
The following assertion was thrown building Listener
|
||||||
'package:flutter\/src\/painting\/basic_types\.dart': Failed assertion: line 225 pos 10: 'textDirection
|
'package:flutter\/src\/painting\/basic_types\.dart': Failed assertion: line 222 pos 10: 'textDirection
|
||||||
!= null': is not true\.
|
!= null': is not true\.
|
||||||
|
|
||||||
Either the assertion indicates an error in the framework itself, or we should provide substantially
|
Either the assertion indicates an error in the framework itself, or we should provide substantially
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// @dart = 2.8
|
|
||||||
|
|
||||||
import 'dart:ui' as ui show lerpDouble;
|
import 'dart:ui' as ui show lerpDouble;
|
||||||
|
|
||||||
@ -88,12 +87,12 @@ abstract class AlignmentGeometry {
|
|||||||
/// into a concrete [Alignment] using [resolve].
|
/// into a concrete [Alignment] using [resolve].
|
||||||
///
|
///
|
||||||
/// {@macro dart.ui.shadow.lerp}
|
/// {@macro dart.ui.shadow.lerp}
|
||||||
static AlignmentGeometry lerp(AlignmentGeometry a, AlignmentGeometry b, double t) {
|
static AlignmentGeometry? lerp(AlignmentGeometry? a, AlignmentGeometry? b, double t) {
|
||||||
assert(t != null);
|
assert(t != null);
|
||||||
if (a == null && b == null)
|
if (a == null && b == null)
|
||||||
return null;
|
return null;
|
||||||
if (a == null)
|
if (a == null)
|
||||||
return b * t;
|
return b! * t;
|
||||||
if (b == null)
|
if (b == null)
|
||||||
return a * (1.0 - t);
|
return a * (1.0 - t);
|
||||||
if (a is Alignment && b is Alignment)
|
if (a is Alignment && b is Alignment)
|
||||||
@ -101,9 +100,9 @@ abstract class AlignmentGeometry {
|
|||||||
if (a is AlignmentDirectional && b is AlignmentDirectional)
|
if (a is AlignmentDirectional && b is AlignmentDirectional)
|
||||||
return AlignmentDirectional.lerp(a, b, t);
|
return AlignmentDirectional.lerp(a, b, t);
|
||||||
return _MixedAlignment(
|
return _MixedAlignment(
|
||||||
ui.lerpDouble(a._x, b._x, t),
|
ui.lerpDouble(a._x, b._x, t)!,
|
||||||
ui.lerpDouble(a._start, b._start, t),
|
ui.lerpDouble(a._start, b._start, t)!,
|
||||||
ui.lerpDouble(a._y, b._y, t),
|
ui.lerpDouble(a._y, b._y, t)!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +115,7 @@ abstract class AlignmentGeometry {
|
|||||||
/// * [Alignment], for which this is a no-op (returns itself).
|
/// * [Alignment], for which this is a no-op (returns itself).
|
||||||
/// * [AlignmentDirectional], which flips the horizontal direction
|
/// * [AlignmentDirectional], which flips the horizontal direction
|
||||||
/// based on the `direction` argument.
|
/// based on the `direction` argument.
|
||||||
Alignment resolve(TextDirection direction);
|
Alignment resolve(TextDirection? direction);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
@ -333,19 +332,19 @@ class Alignment extends AlignmentGeometry {
|
|||||||
/// If either is null, this function interpolates from [Alignment.center].
|
/// If either is null, this function interpolates from [Alignment.center].
|
||||||
///
|
///
|
||||||
/// {@macro dart.ui.shadow.lerp}
|
/// {@macro dart.ui.shadow.lerp}
|
||||||
static Alignment lerp(Alignment a, Alignment b, double t) {
|
static Alignment? lerp(Alignment? a, Alignment? b, double t) {
|
||||||
assert(t != null);
|
assert(t != null);
|
||||||
if (a == null && b == null)
|
if (a == null && b == null)
|
||||||
return null;
|
return null;
|
||||||
if (a == null)
|
if (a == null)
|
||||||
return Alignment(ui.lerpDouble(0.0, b.x, t), ui.lerpDouble(0.0, b.y, t));
|
return Alignment(ui.lerpDouble(0.0, b!.x, t)!, ui.lerpDouble(0.0, b.y, t)!);
|
||||||
if (b == null)
|
if (b == null)
|
||||||
return Alignment(ui.lerpDouble(a.x, 0.0, t), ui.lerpDouble(a.y, 0.0, t));
|
return Alignment(ui.lerpDouble(a.x, 0.0, t)!, ui.lerpDouble(a.y, 0.0, t)!);
|
||||||
return Alignment(ui.lerpDouble(a.x, b.x, t), ui.lerpDouble(a.y, b.y, t));
|
return Alignment(ui.lerpDouble(a.x, b.x, t)!, ui.lerpDouble(a.y, b.y, t)!);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Alignment resolve(TextDirection direction) => this;
|
Alignment resolve(TextDirection? direction) => this;
|
||||||
|
|
||||||
static String _stringify(double x, double y) {
|
static String _stringify(double x, double y) {
|
||||||
if (x == -1.0 && y == -1.0)
|
if (x == -1.0 && y == -1.0)
|
||||||
@ -514,27 +513,26 @@ class AlignmentDirectional extends AlignmentGeometry {
|
|||||||
/// If either is null, this function interpolates from [AlignmentDirectional.center].
|
/// If either is null, this function interpolates from [AlignmentDirectional.center].
|
||||||
///
|
///
|
||||||
/// {@macro dart.ui.shadow.lerp}
|
/// {@macro dart.ui.shadow.lerp}
|
||||||
static AlignmentDirectional lerp(AlignmentDirectional a, AlignmentDirectional b, double t) {
|
static AlignmentDirectional? lerp(AlignmentDirectional? a, AlignmentDirectional? b, double t) {
|
||||||
assert(t != null);
|
assert(t != null);
|
||||||
if (a == null && b == null)
|
if (a == null && b == null)
|
||||||
return null;
|
return null;
|
||||||
if (a == null)
|
if (a == null)
|
||||||
return AlignmentDirectional(ui.lerpDouble(0.0, b.start, t), ui.lerpDouble(0.0, b.y, t));
|
return AlignmentDirectional(ui.lerpDouble(0.0, b!.start, t)!, ui.lerpDouble(0.0, b.y, t)!);
|
||||||
if (b == null)
|
if (b == null)
|
||||||
return AlignmentDirectional(ui.lerpDouble(a.start, 0.0, t), ui.lerpDouble(a.y, 0.0, t));
|
return AlignmentDirectional(ui.lerpDouble(a.start, 0.0, t)!, ui.lerpDouble(a.y, 0.0, t)!);
|
||||||
return AlignmentDirectional(ui.lerpDouble(a.start, b.start, t), ui.lerpDouble(a.y, b.y, t));
|
return AlignmentDirectional(ui.lerpDouble(a.start, b.start, t)!, ui.lerpDouble(a.y, b.y, t)!);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Alignment resolve(TextDirection direction) {
|
Alignment resolve(TextDirection? direction) {
|
||||||
assert(direction != null, 'Cannot resolve $runtimeType without a TextDirection.');
|
assert(direction != null, 'Cannot resolve $runtimeType without a TextDirection.');
|
||||||
switch (direction) {
|
switch (direction!) {
|
||||||
case TextDirection.rtl:
|
case TextDirection.rtl:
|
||||||
return Alignment(-start, y);
|
return Alignment(-start, y);
|
||||||
case TextDirection.ltr:
|
case TextDirection.ltr:
|
||||||
return Alignment(start, y);
|
return Alignment(start, y);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static String _stringify(double start, double y) {
|
static String _stringify(double start, double y) {
|
||||||
@ -622,15 +620,14 @@ class _MixedAlignment extends AlignmentGeometry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Alignment resolve(TextDirection direction) {
|
Alignment resolve(TextDirection? direction) {
|
||||||
assert(direction != null, 'Cannot resolve $runtimeType without a TextDirection.');
|
assert(direction != null, 'Cannot resolve $runtimeType without a TextDirection.');
|
||||||
switch (direction) {
|
switch (direction!) {
|
||||||
case TextDirection.rtl:
|
case TextDirection.rtl:
|
||||||
return Alignment(_x - _start, _y);
|
return Alignment(_x - _start, _y);
|
||||||
case TextDirection.ltr:
|
case TextDirection.ltr:
|
||||||
return Alignment(_x + _start, _y);
|
return Alignment(_x + _start, _y);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -652,7 +649,7 @@ class _MixedAlignment extends AlignmentGeometry {
|
|||||||
class TextAlignVertical {
|
class TextAlignVertical {
|
||||||
/// Creates a TextAlignVertical from any y value between -1.0 and 1.0.
|
/// Creates a TextAlignVertical from any y value between -1.0 and 1.0.
|
||||||
const TextAlignVertical({
|
const TextAlignVertical({
|
||||||
@required this.y,
|
required this.y,
|
||||||
}) : assert(y != null),
|
}) : assert(y != null),
|
||||||
assert(y >= -1.0 && y <= 1.0);
|
assert(y >= -1.0 && y <= 1.0);
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// @dart = 2.8
|
|
||||||
|
|
||||||
import 'dart:ui' show TextDirection;
|
import 'dart:ui' show TextDirection;
|
||||||
|
|
||||||
@ -143,7 +142,6 @@ Axis flipAxis(Axis direction) {
|
|||||||
case Axis.vertical:
|
case Axis.vertical:
|
||||||
return Axis.horizontal;
|
return Axis.horizontal;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A direction in which boxes flow vertically.
|
/// A direction in which boxes flow vertically.
|
||||||
@ -214,7 +212,6 @@ Axis axisDirectionToAxis(AxisDirection axisDirection) {
|
|||||||
case AxisDirection.right:
|
case AxisDirection.right:
|
||||||
return Axis.horizontal;
|
return Axis.horizontal;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the [AxisDirection] in which reading occurs in the given [TextDirection].
|
/// Returns the [AxisDirection] in which reading occurs in the given [TextDirection].
|
||||||
@ -229,7 +226,6 @@ AxisDirection textDirectionToAxisDirection(TextDirection textDirection) {
|
|||||||
case TextDirection.ltr:
|
case TextDirection.ltr:
|
||||||
return AxisDirection.right;
|
return AxisDirection.right;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the opposite of the given [AxisDirection].
|
/// Returns the opposite of the given [AxisDirection].
|
||||||
@ -253,7 +249,6 @@ AxisDirection flipAxisDirection(AxisDirection axisDirection) {
|
|||||||
case AxisDirection.left:
|
case AxisDirection.left:
|
||||||
return AxisDirection.right;
|
return AxisDirection.right;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether traveling along the given axis direction visits coordinates
|
/// Returns whether traveling along the given axis direction visits coordinates
|
||||||
@ -271,5 +266,4 @@ bool axisDirectionIsReversed(AxisDirection axisDirection) {
|
|||||||
case AxisDirection.right:
|
case AxisDirection.right:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// @dart = 2.8
|
|
||||||
|
|
||||||
import 'dart:typed_data' show Uint8List;
|
import 'dart:typed_data' show Uint8List;
|
||||||
import 'dart:ui' as ui show instantiateImageCodec, Codec;
|
import 'dart:ui' as ui show instantiateImageCodec, Codec;
|
||||||
@ -23,14 +22,12 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
|
|||||||
super.initInstances();
|
super.initInstances();
|
||||||
_instance = this;
|
_instance = this;
|
||||||
_imageCache = createImageCache();
|
_imageCache = createImageCache();
|
||||||
if (shaderWarmUp != null) {
|
shaderWarmUp?.execute();
|
||||||
shaderWarmUp.execute();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The current [PaintingBinding], if one has been created.
|
/// The current [PaintingBinding], if one has been created.
|
||||||
static PaintingBinding get instance => _instance;
|
static PaintingBinding? get instance => _instance;
|
||||||
static PaintingBinding _instance;
|
static PaintingBinding? _instance;
|
||||||
|
|
||||||
/// [ShaderWarmUp] to be executed during [initInstances].
|
/// [ShaderWarmUp] to be executed during [initInstances].
|
||||||
///
|
///
|
||||||
@ -53,7 +50,7 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
|
|||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
/// * [ShaderWarmUp], the interface of how this warm up works.
|
/// * [ShaderWarmUp], the interface of how this warm up works.
|
||||||
static ShaderWarmUp shaderWarmUp = const DefaultShaderWarmUp();
|
static ShaderWarmUp? shaderWarmUp = const DefaultShaderWarmUp();
|
||||||
|
|
||||||
/// The singleton that implements the Flutter framework's image cache.
|
/// The singleton that implements the Flutter framework's image cache.
|
||||||
///
|
///
|
||||||
@ -62,8 +59,8 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
|
|||||||
///
|
///
|
||||||
/// The image cache is created during startup by the [createImageCache]
|
/// The image cache is created during startup by the [createImageCache]
|
||||||
/// method.
|
/// method.
|
||||||
ImageCache get imageCache => _imageCache;
|
ImageCache? get imageCache => _imageCache;
|
||||||
ImageCache _imageCache;
|
ImageCache? _imageCache;
|
||||||
|
|
||||||
/// Creates the [ImageCache] singleton (accessible via [imageCache]).
|
/// Creates the [ImageCache] singleton (accessible via [imageCache]).
|
||||||
///
|
///
|
||||||
@ -90,8 +87,8 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
|
|||||||
/// above its native resolution should prefer scaling the canvas the image is
|
/// above its native resolution should prefer scaling the canvas the image is
|
||||||
/// drawn into.
|
/// drawn into.
|
||||||
Future<ui.Codec> instantiateImageCodec(Uint8List bytes, {
|
Future<ui.Codec> instantiateImageCodec(Uint8List bytes, {
|
||||||
int cacheWidth,
|
int? cacheWidth,
|
||||||
int cacheHeight,
|
int? cacheHeight,
|
||||||
bool allowUpscaling = false,
|
bool allowUpscaling = false,
|
||||||
}) {
|
}) {
|
||||||
assert(cacheWidth == null || cacheWidth > 0);
|
assert(cacheWidth == null || cacheWidth > 0);
|
||||||
@ -108,8 +105,8 @@ mixin PaintingBinding on BindingBase, ServicesBinding {
|
|||||||
@override
|
@override
|
||||||
void evict(String asset) {
|
void evict(String asset) {
|
||||||
super.evict(asset);
|
super.evict(asset);
|
||||||
imageCache.clear();
|
imageCache!.clear();
|
||||||
imageCache.clearLiveImages();
|
imageCache!.clearLiveImages();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -170,4 +167,4 @@ class _SystemFontsNotifier extends Listenable {
|
|||||||
///
|
///
|
||||||
/// The image cache is created during startup by the [PaintingBinding]'s
|
/// The image cache is created during startup by the [PaintingBinding]'s
|
||||||
/// [PaintingBinding.createImageCache] method.
|
/// [PaintingBinding.createImageCache] method.
|
||||||
ImageCache get imageCache => PaintingBinding.instance.imageCache;
|
ImageCache? get imageCache => PaintingBinding.instance!.imageCache;
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// @dart = 2.8
|
|
||||||
|
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
import 'dart:ui' show Color, lerpDouble, hashValues;
|
import 'dart:ui' show Color, lerpDouble, hashValues;
|
||||||
@ -10,7 +9,7 @@ import 'dart:ui' show Color, lerpDouble, hashValues;
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
double _getHue(double red, double green, double blue, double max, double delta) {
|
double _getHue(double red, double green, double blue, double max, double delta) {
|
||||||
double hue;
|
late double hue;
|
||||||
if (max == 0.0) {
|
if (max == 0.0) {
|
||||||
hue = 0.0;
|
hue = 0.0;
|
||||||
} else if (max == red) {
|
} else if (max == red) {
|
||||||
@ -199,19 +198,19 @@ class HSVColor {
|
|||||||
/// {@macro dart.ui.shadow.lerp}
|
/// {@macro dart.ui.shadow.lerp}
|
||||||
///
|
///
|
||||||
/// Values outside of the valid range for each channel will be clamped.
|
/// Values outside of the valid range for each channel will be clamped.
|
||||||
static HSVColor lerp(HSVColor a, HSVColor b, double t) {
|
static HSVColor? lerp(HSVColor? a, HSVColor? b, double t) {
|
||||||
assert(t != null);
|
assert(t != null);
|
||||||
if (a == null && b == null)
|
if (a == null && b == null)
|
||||||
return null;
|
return null;
|
||||||
if (a == null)
|
if (a == null)
|
||||||
return b._scaleAlpha(t);
|
return b!._scaleAlpha(t);
|
||||||
if (b == null)
|
if (b == null)
|
||||||
return a._scaleAlpha(1.0 - t);
|
return a._scaleAlpha(1.0 - t);
|
||||||
return HSVColor.fromAHSV(
|
return HSVColor.fromAHSV(
|
||||||
lerpDouble(a.alpha, b.alpha, t).clamp(0.0, 1.0) as double,
|
lerpDouble(a.alpha, b.alpha, t)!.clamp(0.0, 1.0) as double,
|
||||||
lerpDouble(a.hue, b.hue, t) % 360.0,
|
lerpDouble(a.hue, b.hue, t)! % 360.0,
|
||||||
lerpDouble(a.saturation, b.saturation, t).clamp(0.0, 1.0) as double,
|
lerpDouble(a.saturation, b.saturation, t)!.clamp(0.0, 1.0) as double,
|
||||||
lerpDouble(a.value, b.value, t).clamp(0.0, 1.0) as double,
|
lerpDouble(a.value, b.value, t)!.clamp(0.0, 1.0) as double,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -383,19 +382,19 @@ class HSLColor {
|
|||||||
///
|
///
|
||||||
/// Values for `t` are usually obtained from an [Animation<double>], such as
|
/// Values for `t` are usually obtained from an [Animation<double>], such as
|
||||||
/// an [AnimationController].
|
/// an [AnimationController].
|
||||||
static HSLColor lerp(HSLColor a, HSLColor b, double t) {
|
static HSLColor? lerp(HSLColor? a, HSLColor? b, double t) {
|
||||||
assert(t != null);
|
assert(t != null);
|
||||||
if (a == null && b == null)
|
if (a == null && b == null)
|
||||||
return null;
|
return null;
|
||||||
if (a == null)
|
if (a == null)
|
||||||
return b._scaleAlpha(t);
|
return b!._scaleAlpha(t);
|
||||||
if (b == null)
|
if (b == null)
|
||||||
return a._scaleAlpha(1.0 - t);
|
return a._scaleAlpha(1.0 - t);
|
||||||
return HSLColor.fromAHSL(
|
return HSLColor.fromAHSL(
|
||||||
lerpDouble(a.alpha, b.alpha, t).clamp(0.0, 1.0) as double,
|
lerpDouble(a.alpha, b.alpha, t)!.clamp(0.0, 1.0) as double,
|
||||||
lerpDouble(a.hue, b.hue, t) % 360.0,
|
lerpDouble(a.hue, b.hue, t)! % 360.0,
|
||||||
lerpDouble(a.saturation, b.saturation, t).clamp(0.0, 1.0) as double,
|
lerpDouble(a.saturation, b.saturation, t)!.clamp(0.0, 1.0) as double,
|
||||||
lerpDouble(a.lightness, b.lightness, t).clamp(0.0, 1.0) as double,
|
lerpDouble(a.lightness, b.lightness, t)!.clamp(0.0, 1.0) as double,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,7 +440,7 @@ class ColorSwatch<T> extends Color {
|
|||||||
final Map<T, Color> _swatch;
|
final Map<T, Color> _swatch;
|
||||||
|
|
||||||
/// Returns an element of the swatch table.
|
/// Returns an element of the swatch table.
|
||||||
Color operator [](T index) => _swatch[index];
|
Color? operator [](T index) => _swatch[index];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
@ -468,9 +467,9 @@ class ColorProperty extends DiagnosticsProperty<Color> {
|
|||||||
/// The [showName], [style], and [level] arguments must not be null.
|
/// The [showName], [style], and [level] arguments must not be null.
|
||||||
ColorProperty(
|
ColorProperty(
|
||||||
String name,
|
String name,
|
||||||
Color value, {
|
Color? value, {
|
||||||
bool showName = true,
|
bool showName = true,
|
||||||
Object defaultValue = kNoDefaultValue,
|
Object? defaultValue = kNoDefaultValue,
|
||||||
DiagnosticsTreeStyle style = DiagnosticsTreeStyle.singleLine,
|
DiagnosticsTreeStyle style = DiagnosticsTreeStyle.singleLine,
|
||||||
DiagnosticLevel level = DiagnosticLevel.info,
|
DiagnosticLevel level = DiagnosticLevel.info,
|
||||||
}) : assert(showName != null),
|
}) : assert(showName != null),
|
||||||
@ -484,14 +483,14 @@ class ColorProperty extends DiagnosticsProperty<Color> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, Object> toJsonMap(DiagnosticsSerializationDelegate delegate) {
|
Map<String, Object?> toJsonMap(DiagnosticsSerializationDelegate delegate) {
|
||||||
final Map<String, Object> json = super.toJsonMap(delegate);
|
final Map<String, Object?> json = super.toJsonMap(delegate);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
json['valueProperties'] = <String, Object>{
|
json['valueProperties'] = <String, Object>{
|
||||||
'red': value.red,
|
'red': value!.red,
|
||||||
'green': value.green,
|
'green': value!.green,
|
||||||
'blue': value.blue,
|
'blue': value!.blue,
|
||||||
'alpha': value.alpha,
|
'alpha': value!.alpha,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return json;
|
return json;
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// @dart = 2.8
|
|
||||||
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:ui' show Size, hashValues;
|
import 'dart:ui' show Size, hashValues;
|
||||||
@ -30,7 +29,7 @@ typedef HttpClientProvider = HttpClient Function();
|
|||||||
/// a mock client that hasn't been affected by other tests.
|
/// a mock client that hasn't been affected by other tests.
|
||||||
///
|
///
|
||||||
/// This value is ignored in non-debug builds.
|
/// This value is ignored in non-debug builds.
|
||||||
HttpClientProvider debugNetworkImageHttpClientProvider;
|
HttpClientProvider? debugNetworkImageHttpClientProvider;
|
||||||
|
|
||||||
typedef PaintImageCallback = void Function(ImageSizeInfo);
|
typedef PaintImageCallback = void Function(ImageSizeInfo);
|
||||||
|
|
||||||
@ -44,20 +43,20 @@ class ImageSizeInfo {
|
|||||||
/// This class is used by the framework when it paints an image to a canvas
|
/// This class is used by the framework when it paints an image to a canvas
|
||||||
/// to report to `dart:developer`'s [postEvent], as well as to the
|
/// to report to `dart:developer`'s [postEvent], as well as to the
|
||||||
/// [debugOnPaintImage] callback if it is set.
|
/// [debugOnPaintImage] callback if it is set.
|
||||||
const ImageSizeInfo({this.source, this.displaySize, this.imageSize});
|
const ImageSizeInfo({this.source, this.displaySize, required this.imageSize});
|
||||||
|
|
||||||
/// A unique identifier for this image, for example its asset path or network
|
/// A unique identifier for this image, for example its asset path or network
|
||||||
/// URL.
|
/// URL.
|
||||||
final String source;
|
final String? source;
|
||||||
|
|
||||||
/// The size of the area the image will be rendered in.
|
/// The size of the area the image will be rendered in.
|
||||||
final Size displaySize;
|
final Size? displaySize;
|
||||||
|
|
||||||
/// The size the image has been decoded to.
|
/// The size the image has been decoded to.
|
||||||
final Size imageSize;
|
final Size imageSize;
|
||||||
|
|
||||||
/// The number of bytes needed to render the image without scaling it.
|
/// The number of bytes needed to render the image without scaling it.
|
||||||
int get displaySizeInBytes => _sizeToBytes(displaySize);
|
int get displaySizeInBytes => _sizeToBytes(displaySize!);
|
||||||
|
|
||||||
/// The number of bytes used by the image in memory.
|
/// The number of bytes used by the image in memory.
|
||||||
int get decodedSizeInBytes => _sizeToBytes(imageSize);
|
int get decodedSizeInBytes => _sizeToBytes(imageSize);
|
||||||
@ -69,14 +68,15 @@ class ImageSizeInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a JSON encodable representation of this object.
|
/// Returns a JSON encodable representation of this object.
|
||||||
Map<String, Object> toJson() {
|
Map<String, Object?> toJson() {
|
||||||
return <String, Object>{
|
return <String, Object?>{
|
||||||
'source': source,
|
'source': source,
|
||||||
'displaySize': <String, double>{
|
if (displaySize != null)
|
||||||
'width': displaySize.width,
|
'displaySize': <String, Object?>{
|
||||||
'height': displaySize.height,
|
'width': displaySize!.width,
|
||||||
},
|
'height': displaySize!.height,
|
||||||
'imageSize': <String, double>{
|
},
|
||||||
|
'imageSize': <String, Object?>{
|
||||||
'width': imageSize.width,
|
'width': imageSize.width,
|
||||||
'height': imageSize.height,
|
'height': imageSize.height,
|
||||||
},
|
},
|
||||||
@ -125,7 +125,7 @@ class ImageSizeInfo {
|
|||||||
/// a higher resolution while animating, but it would be problematic to have
|
/// a higher resolution while animating, but it would be problematic to have
|
||||||
/// a grid or list of such thumbnails all be at the full resolution at the same
|
/// a grid or list of such thumbnails all be at the full resolution at the same
|
||||||
/// time.
|
/// time.
|
||||||
PaintImageCallback debugOnPaintImage;
|
PaintImageCallback? debugOnPaintImage;
|
||||||
|
|
||||||
/// If true, the framework will color invert and horizontally flip images that
|
/// If true, the framework will color invert and horizontally flip images that
|
||||||
/// have been decoded to a size taking at least [debugImageOverheadAllowance]
|
/// have been decoded to a size taking at least [debugImageOverheadAllowance]
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// @dart = 2.8
|
|
||||||
|
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
import 'dart:ui' show hashValues;
|
import 'dart:ui' show hashValues;
|
||||||
@ -103,7 +102,7 @@ class ImageCache {
|
|||||||
assert(value >= 0);
|
assert(value >= 0);
|
||||||
if (value == maximumSize)
|
if (value == maximumSize)
|
||||||
return;
|
return;
|
||||||
TimelineTask timelineTask;
|
TimelineTask? timelineTask;
|
||||||
if (!kReleaseMode) {
|
if (!kReleaseMode) {
|
||||||
timelineTask = TimelineTask()..start(
|
timelineTask = TimelineTask()..start(
|
||||||
'ImageCache.setMaximumSize',
|
'ImageCache.setMaximumSize',
|
||||||
@ -117,7 +116,7 @@ class ImageCache {
|
|||||||
_checkCacheSize(timelineTask);
|
_checkCacheSize(timelineTask);
|
||||||
}
|
}
|
||||||
if (!kReleaseMode) {
|
if (!kReleaseMode) {
|
||||||
timelineTask.finish();
|
timelineTask!.finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +141,7 @@ class ImageCache {
|
|||||||
assert(value >= 0);
|
assert(value >= 0);
|
||||||
if (value == _maximumSizeBytes)
|
if (value == _maximumSizeBytes)
|
||||||
return;
|
return;
|
||||||
TimelineTask timelineTask;
|
TimelineTask? timelineTask;
|
||||||
if (!kReleaseMode) {
|
if (!kReleaseMode) {
|
||||||
timelineTask = TimelineTask()..start(
|
timelineTask = TimelineTask()..start(
|
||||||
'ImageCache.setMaximumSizeBytes',
|
'ImageCache.setMaximumSizeBytes',
|
||||||
@ -156,7 +155,7 @@ class ImageCache {
|
|||||||
_checkCacheSize(timelineTask);
|
_checkCacheSize(timelineTask);
|
||||||
}
|
}
|
||||||
if (!kReleaseMode) {
|
if (!kReleaseMode) {
|
||||||
timelineTask.finish();
|
timelineTask!.finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,10 +238,10 @@ class ImageCache {
|
|||||||
// will never complete, e.g. it was loaded in a FakeAsync zone.
|
// will never complete, e.g. it was loaded in a FakeAsync zone.
|
||||||
// In such a case, we need to make sure subsequent calls to
|
// In such a case, we need to make sure subsequent calls to
|
||||||
// putIfAbsent don't return this image that may never complete.
|
// putIfAbsent don't return this image that may never complete.
|
||||||
final _LiveImage image = _liveImages.remove(key);
|
final _LiveImage? image = _liveImages.remove(key);
|
||||||
image?.removeListener();
|
image?.removeListener();
|
||||||
}
|
}
|
||||||
final _PendingImage pendingImage = _pendingImages.remove(key);
|
final _PendingImage? pendingImage = _pendingImages.remove(key);
|
||||||
if (pendingImage != null) {
|
if (pendingImage != null) {
|
||||||
if (!kReleaseMode) {
|
if (!kReleaseMode) {
|
||||||
Timeline.instantSync('ImageCache.evict', arguments: <String, dynamic>{
|
Timeline.instantSync('ImageCache.evict', arguments: <String, dynamic>{
|
||||||
@ -252,7 +251,7 @@ class ImageCache {
|
|||||||
pendingImage.removeListener();
|
pendingImage.removeListener();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
final _CachedImage image = _cache.remove(key);
|
final _CachedImage? image = _cache.remove(key);
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
if (!kReleaseMode) {
|
if (!kReleaseMode) {
|
||||||
Timeline.instantSync('ImageCache.evict', arguments: <String, dynamic>{
|
Timeline.instantSync('ImageCache.evict', arguments: <String, dynamic>{
|
||||||
@ -260,7 +259,7 @@ class ImageCache {
|
|||||||
'sizeInBytes': image.sizeBytes,
|
'sizeInBytes': image.sizeBytes,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_currentSizeBytes -= image.sizeBytes;
|
_currentSizeBytes -= image.sizeBytes!;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!kReleaseMode) {
|
if (!kReleaseMode) {
|
||||||
@ -276,13 +275,13 @@ class ImageCache {
|
|||||||
///
|
///
|
||||||
/// Resizes the cache as appropriate to maintain the constraints of
|
/// Resizes the cache as appropriate to maintain the constraints of
|
||||||
/// [maximumSize] and [maximumSizeBytes].
|
/// [maximumSize] and [maximumSizeBytes].
|
||||||
void _touch(Object key, _CachedImage image, TimelineTask timelineTask) {
|
void _touch(Object key, _CachedImage image, TimelineTask? timelineTask) {
|
||||||
// TODO(dnfield): Some customers test in release mode with asserts enabled.
|
// TODO(dnfield): Some customers test in release mode with asserts enabled.
|
||||||
// This is bound to cause problems, b/150295238 is tracking that. For now,
|
// This is bound to cause problems, b/150295238 is tracking that. For now,
|
||||||
// avoid this being a point of failure.
|
// avoid this being a point of failure.
|
||||||
assert(kReleaseMode || timelineTask != null);
|
assert(kReleaseMode || timelineTask != null);
|
||||||
if (image.sizeBytes != null && image.sizeBytes <= maximumSizeBytes) {
|
if (image.sizeBytes != null && image.sizeBytes! <= maximumSizeBytes) {
|
||||||
_currentSizeBytes += image.sizeBytes;
|
_currentSizeBytes += image.sizeBytes!;
|
||||||
_cache[key] = image;
|
_cache[key] = image;
|
||||||
_checkCacheSize(timelineTask);
|
_checkCacheSize(timelineTask);
|
||||||
}
|
}
|
||||||
@ -310,11 +309,11 @@ class ImageCache {
|
|||||||
/// `onError` is also provided. When an exception is caught resolving an image,
|
/// `onError` is also provided. When an exception is caught resolving an image,
|
||||||
/// no completers are cached and `null` is returned instead of a new
|
/// no completers are cached and `null` is returned instead of a new
|
||||||
/// completer.
|
/// completer.
|
||||||
ImageStreamCompleter putIfAbsent(Object key, ImageStreamCompleter loader(), { ImageErrorListener onError }) {
|
ImageStreamCompleter? putIfAbsent(Object key, ImageStreamCompleter loader(), { ImageErrorListener? onError }) {
|
||||||
assert(key != null);
|
assert(key != null);
|
||||||
assert(loader != null);
|
assert(loader != null);
|
||||||
TimelineTask timelineTask;
|
TimelineTask? timelineTask;
|
||||||
TimelineTask listenerTask;
|
TimelineTask? listenerTask;
|
||||||
if (!kReleaseMode) {
|
if (!kReleaseMode) {
|
||||||
timelineTask = TimelineTask()..start(
|
timelineTask = TimelineTask()..start(
|
||||||
'ImageCache.putIfAbsent',
|
'ImageCache.putIfAbsent',
|
||||||
@ -323,11 +322,11 @@ class ImageCache {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ImageStreamCompleter result = _pendingImages[key]?.completer;
|
ImageStreamCompleter? result = _pendingImages[key]?.completer;
|
||||||
// Nothing needs to be done because the image hasn't loaded yet.
|
// Nothing needs to be done because the image hasn't loaded yet.
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
if (!kReleaseMode) {
|
if (!kReleaseMode) {
|
||||||
timelineTask.finish(arguments: <String, dynamic>{'result': 'pending'});
|
timelineTask!.finish(arguments: <String, dynamic>{'result': 'pending'});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -335,10 +334,10 @@ class ImageCache {
|
|||||||
// recently used position below.
|
// recently used position below.
|
||||||
// Don't use _touch here, which would trigger a check on cache size that is
|
// Don't use _touch here, which would trigger a check on cache size that is
|
||||||
// not needed since this is just moving an existing cache entry to the head.
|
// not needed since this is just moving an existing cache entry to the head.
|
||||||
final _CachedImage image = _cache.remove(key);
|
final _CachedImage? image = _cache.remove(key);
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
if (!kReleaseMode) {
|
if (!kReleaseMode) {
|
||||||
timelineTask.finish(arguments: <String, dynamic>{'result': 'keepAlive'});
|
timelineTask!.finish(arguments: <String, dynamic>{'result': 'keepAlive'});
|
||||||
}
|
}
|
||||||
// The image might have been keptAlive but had no listeners (so not live).
|
// The image might have been keptAlive but had no listeners (so not live).
|
||||||
// Make sure the cache starts tracking it as live again.
|
// Make sure the cache starts tracking it as live again.
|
||||||
@ -347,11 +346,11 @@ class ImageCache {
|
|||||||
return image.completer;
|
return image.completer;
|
||||||
}
|
}
|
||||||
|
|
||||||
final _CachedImage liveImage = _liveImages[key];
|
final _CachedImage? liveImage = _liveImages[key];
|
||||||
if (liveImage != null) {
|
if (liveImage != null) {
|
||||||
_touch(key, liveImage, timelineTask);
|
_touch(key, liveImage, timelineTask);
|
||||||
if (!kReleaseMode) {
|
if (!kReleaseMode) {
|
||||||
timelineTask.finish(arguments: <String, dynamic>{'result': 'keepAlive'});
|
timelineTask!.finish(arguments: <String, dynamic>{'result': 'keepAlive'});
|
||||||
}
|
}
|
||||||
return liveImage.completer;
|
return liveImage.completer;
|
||||||
}
|
}
|
||||||
@ -361,7 +360,7 @@ class ImageCache {
|
|||||||
_trackLiveImage(key, _LiveImage(result, null, () => _liveImages.remove(key)));
|
_trackLiveImage(key, _LiveImage(result, null, () => _liveImages.remove(key)));
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stackTrace) {
|
||||||
if (!kReleaseMode) {
|
if (!kReleaseMode) {
|
||||||
timelineTask.finish(arguments: <String, dynamic>{
|
timelineTask!.finish(arguments: <String, dynamic>{
|
||||||
'result': 'error',
|
'result': 'error',
|
||||||
'error': error.toString(),
|
'error': error.toString(),
|
||||||
'stackTrace': stackTrace.toString(),
|
'stackTrace': stackTrace.toString(),
|
||||||
@ -387,12 +386,12 @@ class ImageCache {
|
|||||||
// will have to listen to the image at least once so we don't leak it in
|
// will have to listen to the image at least once so we don't leak it in
|
||||||
// the live image tracking.
|
// the live image tracking.
|
||||||
// If the cache is disabled, this variable will be set.
|
// If the cache is disabled, this variable will be set.
|
||||||
_PendingImage untrackedPendingImage;
|
_PendingImage? untrackedPendingImage;
|
||||||
void listener(ImageInfo info, bool syncCall) {
|
void listener(ImageInfo? info, bool syncCall) {
|
||||||
// Images that fail to load don't contribute to cache size.
|
// Images that fail to load don't contribute to cache size.
|
||||||
final int imageSize = info?.image == null ? 0 : info.image.height * info.image.width * 4;
|
final int imageSize = info == null || info.image == null ? 0 : info.image.height * info.image.width * 4;
|
||||||
|
|
||||||
final _CachedImage image = _CachedImage(result, imageSize);
|
final _CachedImage image = _CachedImage(result!, imageSize);
|
||||||
|
|
||||||
_trackLiveImage(
|
_trackLiveImage(
|
||||||
key,
|
key,
|
||||||
@ -403,7 +402,7 @@ class ImageCache {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final _PendingImage pendingImage = untrackedPendingImage ?? _pendingImages.remove(key);
|
final _PendingImage? pendingImage = untrackedPendingImage ?? _pendingImages.remove(key);
|
||||||
if (pendingImage != null) {
|
if (pendingImage != null) {
|
||||||
pendingImage.removeListener();
|
pendingImage.removeListener();
|
||||||
}
|
}
|
||||||
@ -413,11 +412,11 @@ class ImageCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!kReleaseMode && !listenedOnce) {
|
if (!kReleaseMode && !listenedOnce) {
|
||||||
listenerTask.finish(arguments: <String, dynamic>{
|
listenerTask!.finish(arguments: <String, dynamic>{
|
||||||
'syncCall': syncCall,
|
'syncCall': syncCall,
|
||||||
'sizeInBytes': imageSize,
|
'sizeInBytes': imageSize,
|
||||||
});
|
});
|
||||||
timelineTask.finish(arguments: <String, dynamic>{
|
timelineTask!.finish(arguments: <String, dynamic>{
|
||||||
'currentSizeBytes': currentSizeBytes,
|
'currentSizeBytes': currentSizeBytes,
|
||||||
'currentSize': currentSize,
|
'currentSize': currentSize,
|
||||||
});
|
});
|
||||||
@ -481,9 +480,9 @@ class ImageCache {
|
|||||||
|
|
||||||
// Remove images from the cache until both the length and bytes are below
|
// Remove images from the cache until both the length and bytes are below
|
||||||
// maximum, or the cache is empty.
|
// maximum, or the cache is empty.
|
||||||
void _checkCacheSize(TimelineTask timelineTask) {
|
void _checkCacheSize(TimelineTask? timelineTask) {
|
||||||
final Map<String, dynamic> finishArgs = <String, dynamic>{};
|
final Map<String, dynamic> finishArgs = <String, dynamic>{};
|
||||||
TimelineTask checkCacheTask;
|
TimelineTask? checkCacheTask;
|
||||||
if (!kReleaseMode) {
|
if (!kReleaseMode) {
|
||||||
checkCacheTask = TimelineTask(parent: timelineTask)..start('checkCacheSize');
|
checkCacheTask = TimelineTask(parent: timelineTask)..start('checkCacheSize');
|
||||||
finishArgs['evictedKeys'] = <String>[];
|
finishArgs['evictedKeys'] = <String>[];
|
||||||
@ -492,8 +491,8 @@ class ImageCache {
|
|||||||
}
|
}
|
||||||
while (_currentSizeBytes > _maximumSizeBytes || _cache.length > _maximumSize) {
|
while (_currentSizeBytes > _maximumSizeBytes || _cache.length > _maximumSize) {
|
||||||
final Object key = _cache.keys.first;
|
final Object key = _cache.keys.first;
|
||||||
final _CachedImage image = _cache[key];
|
final _CachedImage image = _cache[key]!;
|
||||||
_currentSizeBytes -= image.sizeBytes;
|
_currentSizeBytes -= image.sizeBytes!;
|
||||||
_cache.remove(key);
|
_cache.remove(key);
|
||||||
if (!kReleaseMode) {
|
if (!kReleaseMode) {
|
||||||
finishArgs['evictedKeys'].add(key.toString());
|
finishArgs['evictedKeys'].add(key.toString());
|
||||||
@ -502,7 +501,7 @@ class ImageCache {
|
|||||||
if (!kReleaseMode) {
|
if (!kReleaseMode) {
|
||||||
finishArgs['endSize'] = currentSize;
|
finishArgs['endSize'] = currentSize;
|
||||||
finishArgs['endSizeBytes'] = currentSizeBytes;
|
finishArgs['endSizeBytes'] = currentSizeBytes;
|
||||||
checkCacheTask.finish(arguments: finishArgs);
|
checkCacheTask!.finish(arguments: finishArgs);
|
||||||
}
|
}
|
||||||
assert(_currentSizeBytes >= 0);
|
assert(_currentSizeBytes >= 0);
|
||||||
assert(_cache.length <= maximumSize);
|
assert(_cache.length <= maximumSize);
|
||||||
@ -585,11 +584,11 @@ class _CachedImage {
|
|||||||
_CachedImage(this.completer, this.sizeBytes);
|
_CachedImage(this.completer, this.sizeBytes);
|
||||||
|
|
||||||
final ImageStreamCompleter completer;
|
final ImageStreamCompleter completer;
|
||||||
int sizeBytes;
|
int? sizeBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LiveImage extends _CachedImage {
|
class _LiveImage extends _CachedImage {
|
||||||
_LiveImage(ImageStreamCompleter completer, int sizeBytes, this.handleRemove)
|
_LiveImage(ImageStreamCompleter completer, int? sizeBytes, this.handleRemove)
|
||||||
: super(completer, sizeBytes);
|
: super(completer, sizeBytes);
|
||||||
|
|
||||||
final VoidCallback handleRemove;
|
final VoidCallback handleRemove;
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// @dart = 2.8
|
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:ui' as ui show Image, Codec, FrameInfo;
|
import 'dart:ui' as ui show Image, Codec, FrameInfo;
|
||||||
@ -22,7 +21,7 @@ class ImageInfo {
|
|||||||
/// Both the image and the scale must not be null.
|
/// Both the image and the scale must not be null.
|
||||||
///
|
///
|
||||||
/// The tag may be used to identify the source of this image.
|
/// The tag may be used to identify the source of this image.
|
||||||
const ImageInfo({ @required this.image, this.scale = 1.0, this.debugLabel })
|
const ImageInfo({ required this.image, this.scale = 1.0, this.debugLabel })
|
||||||
: assert(image != null),
|
: assert(image != null),
|
||||||
assert(scale != null);
|
assert(scale != null);
|
||||||
|
|
||||||
@ -45,7 +44,7 @@ class ImageInfo {
|
|||||||
final double scale;
|
final double scale;
|
||||||
|
|
||||||
/// A string used for debugging purpopses to identify the source of this image.
|
/// A string used for debugging purpopses to identify the source of this image.
|
||||||
final String debugLabel;
|
final String? debugLabel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => '${debugLabel != null ? '$debugLabel ' : ''}$image @ ${debugFormatDouble(scale)}x';
|
String toString() => '${debugLabel != null ? '$debugLabel ' : ''}$image @ ${debugFormatDouble(scale)}x';
|
||||||
@ -112,13 +111,13 @@ class ImageStreamListener {
|
|||||||
/// This callback may also continue to fire after the [onImage] callback has
|
/// This callback may also continue to fire after the [onImage] callback has
|
||||||
/// fired (e.g. for multi-frame images that continue to load after the first
|
/// fired (e.g. for multi-frame images that continue to load after the first
|
||||||
/// frame is available).
|
/// frame is available).
|
||||||
final ImageChunkListener onChunk;
|
final ImageChunkListener? onChunk;
|
||||||
|
|
||||||
/// Callback for getting notified when an error occurs while loading an image.
|
/// Callback for getting notified when an error occurs while loading an image.
|
||||||
///
|
///
|
||||||
/// If an error occurs during loading, [onError] will be called instead of
|
/// If an error occurs during loading, [onError] will be called instead of
|
||||||
/// [onImage].
|
/// [onImage].
|
||||||
final ImageErrorListener onError;
|
final ImageErrorListener? onError;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => hashValues(onImage, onChunk, onError);
|
int get hashCode => hashValues(onImage, onChunk, onError);
|
||||||
@ -155,7 +154,7 @@ typedef ImageChunkListener = void Function(ImageChunkEvent event);
|
|||||||
///
|
///
|
||||||
/// Used in [ImageStreamListener], as well as by [ImageCache.putIfAbsent] and
|
/// Used in [ImageStreamListener], as well as by [ImageCache.putIfAbsent] and
|
||||||
/// [precacheImage], to report errors.
|
/// [precacheImage], to report errors.
|
||||||
typedef ImageErrorListener = void Function(dynamic exception, StackTrace stackTrace);
|
typedef ImageErrorListener = void Function(dynamic exception, StackTrace? stackTrace);
|
||||||
|
|
||||||
/// An immutable notification of image bytes that have been incrementally loaded.
|
/// An immutable notification of image bytes that have been incrementally loaded.
|
||||||
///
|
///
|
||||||
@ -170,8 +169,8 @@ typedef ImageErrorListener = void Function(dynamic exception, StackTrace stackTr
|
|||||||
class ImageChunkEvent with Diagnosticable {
|
class ImageChunkEvent with Diagnosticable {
|
||||||
/// Creates a new chunk event.
|
/// Creates a new chunk event.
|
||||||
const ImageChunkEvent({
|
const ImageChunkEvent({
|
||||||
@required this.cumulativeBytesLoaded,
|
required this.cumulativeBytesLoaded,
|
||||||
@required this.expectedTotalBytes,
|
required this.expectedTotalBytes,
|
||||||
}) : assert(cumulativeBytesLoaded >= 0),
|
}) : assert(cumulativeBytesLoaded >= 0),
|
||||||
assert(expectedTotalBytes == null || expectedTotalBytes >= 0);
|
assert(expectedTotalBytes == null || expectedTotalBytes >= 0);
|
||||||
|
|
||||||
@ -189,7 +188,7 @@ class ImageChunkEvent with Diagnosticable {
|
|||||||
/// When this value is null, the chunk event may still be useful as an
|
/// When this value is null, the chunk event may still be useful as an
|
||||||
/// indication that data is loading (and how much), but it cannot represent a
|
/// indication that data is loading (and how much), but it cannot represent a
|
||||||
/// loading completion percentage.
|
/// loading completion percentage.
|
||||||
final int expectedTotalBytes;
|
final int? expectedTotalBytes;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
@ -229,10 +228,10 @@ class ImageStream with Diagnosticable {
|
|||||||
/// The completer that has been assigned to this image stream.
|
/// The completer that has been assigned to this image stream.
|
||||||
///
|
///
|
||||||
/// Generally there is no need to deal with the completer directly.
|
/// Generally there is no need to deal with the completer directly.
|
||||||
ImageStreamCompleter get completer => _completer;
|
ImageStreamCompleter? get completer => _completer;
|
||||||
ImageStreamCompleter _completer;
|
ImageStreamCompleter? _completer;
|
||||||
|
|
||||||
List<ImageStreamListener> _listeners;
|
List<ImageStreamListener>? _listeners;
|
||||||
|
|
||||||
/// Assigns a particular [ImageStreamCompleter] to this [ImageStream].
|
/// Assigns a particular [ImageStreamCompleter] to this [ImageStream].
|
||||||
///
|
///
|
||||||
@ -246,9 +245,9 @@ class ImageStream with Diagnosticable {
|
|||||||
assert(_completer == null);
|
assert(_completer == null);
|
||||||
_completer = value;
|
_completer = value;
|
||||||
if (_listeners != null) {
|
if (_listeners != null) {
|
||||||
final List<ImageStreamListener> initialListeners = _listeners;
|
final List<ImageStreamListener> initialListeners = _listeners!;
|
||||||
_listeners = null;
|
_listeners = null;
|
||||||
initialListeners.forEach(_completer.addListener);
|
initialListeners.forEach(_completer!.addListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,9 +271,9 @@ class ImageStream with Diagnosticable {
|
|||||||
/// {@endtemplate}
|
/// {@endtemplate}
|
||||||
void addListener(ImageStreamListener listener) {
|
void addListener(ImageStreamListener listener) {
|
||||||
if (_completer != null)
|
if (_completer != null)
|
||||||
return _completer.addListener(listener);
|
return _completer!.addListener(listener);
|
||||||
_listeners ??= <ImageStreamListener>[];
|
_listeners ??= <ImageStreamListener>[];
|
||||||
_listeners.add(listener);
|
_listeners!.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stops listening for events from this stream's [ImageStreamCompleter].
|
/// Stops listening for events from this stream's [ImageStreamCompleter].
|
||||||
@ -283,11 +282,11 @@ class ImageStream with Diagnosticable {
|
|||||||
/// instance of the listener.
|
/// instance of the listener.
|
||||||
void removeListener(ImageStreamListener listener) {
|
void removeListener(ImageStreamListener listener) {
|
||||||
if (_completer != null)
|
if (_completer != null)
|
||||||
return _completer.removeListener(listener);
|
return _completer!.removeListener(listener);
|
||||||
assert(_listeners != null);
|
assert(_listeners != null);
|
||||||
for (int i = 0; i < _listeners.length; i += 1) {
|
for (int i = 0; i < _listeners!.length; i += 1) {
|
||||||
if (_listeners[i] == listener) {
|
if (_listeners![i] == listener) {
|
||||||
_listeners.removeAt(i);
|
_listeners!.removeAt(i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -334,11 +333,11 @@ class ImageStream with Diagnosticable {
|
|||||||
/// configure it with the right [ImageStreamCompleter] when possible.
|
/// configure it with the right [ImageStreamCompleter] when possible.
|
||||||
abstract class ImageStreamCompleter with Diagnosticable {
|
abstract class ImageStreamCompleter with Diagnosticable {
|
||||||
final List<ImageStreamListener> _listeners = <ImageStreamListener>[];
|
final List<ImageStreamListener> _listeners = <ImageStreamListener>[];
|
||||||
ImageInfo _currentImage;
|
ImageInfo? _currentImage;
|
||||||
FlutterErrorDetails _currentError;
|
FlutterErrorDetails? _currentError;
|
||||||
|
|
||||||
/// A string identifying the source of the underlying image.
|
/// A string identifying the source of the underlying image.
|
||||||
String debugLabel;
|
String? debugLabel;
|
||||||
|
|
||||||
/// Whether any listeners are currently registered.
|
/// Whether any listeners are currently registered.
|
||||||
///
|
///
|
||||||
@ -372,7 +371,7 @@ abstract class ImageStreamCompleter with Diagnosticable {
|
|||||||
_listeners.add(listener);
|
_listeners.add(listener);
|
||||||
if (_currentImage != null) {
|
if (_currentImage != null) {
|
||||||
try {
|
try {
|
||||||
listener.onImage(_currentImage, true);
|
listener.onImage(_currentImage!, true);
|
||||||
} catch (exception, stack) {
|
} catch (exception, stack) {
|
||||||
reportError(
|
reportError(
|
||||||
context: ErrorDescription('by a synchronously-called image listener'),
|
context: ErrorDescription('by a synchronously-called image listener'),
|
||||||
@ -383,7 +382,7 @@ abstract class ImageStreamCompleter with Diagnosticable {
|
|||||||
}
|
}
|
||||||
if (_currentError != null && listener.onError != null) {
|
if (_currentError != null && listener.onError != null) {
|
||||||
try {
|
try {
|
||||||
listener.onError(_currentError.exception, _currentError.stack);
|
listener.onError!(_currentError!.exception, _currentError!.stack);
|
||||||
} catch (exception, stack) {
|
} catch (exception, stack) {
|
||||||
FlutterError.reportError(
|
FlutterError.reportError(
|
||||||
FlutterErrorDetails(
|
FlutterErrorDetails(
|
||||||
@ -486,10 +485,10 @@ abstract class ImageStreamCompleter with Diagnosticable {
|
|||||||
/// See [FlutterErrorDetails] for further details on these values.
|
/// See [FlutterErrorDetails] for further details on these values.
|
||||||
@protected
|
@protected
|
||||||
void reportError({
|
void reportError({
|
||||||
DiagnosticsNode context,
|
DiagnosticsNode? context,
|
||||||
dynamic exception,
|
dynamic exception,
|
||||||
StackTrace stack,
|
StackTrace? stack,
|
||||||
InformationCollector informationCollector,
|
InformationCollector? informationCollector,
|
||||||
bool silent = false,
|
bool silent = false,
|
||||||
}) {
|
}) {
|
||||||
_currentError = FlutterErrorDetails(
|
_currentError = FlutterErrorDetails(
|
||||||
@ -503,12 +502,12 @@ abstract class ImageStreamCompleter with Diagnosticable {
|
|||||||
|
|
||||||
// Make a copy to allow for concurrent modification.
|
// Make a copy to allow for concurrent modification.
|
||||||
final List<ImageErrorListener> localErrorListeners = _listeners
|
final List<ImageErrorListener> localErrorListeners = _listeners
|
||||||
.map<ImageErrorListener>((ImageStreamListener listener) => listener.onError)
|
.map<ImageErrorListener?>((ImageStreamListener listener) => listener.onError)
|
||||||
.where((ImageErrorListener errorListener) => errorListener != null)
|
.whereType<ImageErrorListener>()
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (localErrorListeners.isEmpty) {
|
if (localErrorListeners.isEmpty) {
|
||||||
FlutterError.reportError(_currentError);
|
FlutterError.reportError(_currentError!);
|
||||||
} else {
|
} else {
|
||||||
for (final ImageErrorListener errorListener in localErrorListeners) {
|
for (final ImageErrorListener errorListener in localErrorListeners) {
|
||||||
try {
|
try {
|
||||||
@ -535,8 +534,8 @@ abstract class ImageStreamCompleter with Diagnosticable {
|
|||||||
if (hasListeners) {
|
if (hasListeners) {
|
||||||
// Make a copy to allow for concurrent modification.
|
// Make a copy to allow for concurrent modification.
|
||||||
final List<ImageChunkListener> localListeners = _listeners
|
final List<ImageChunkListener> localListeners = _listeners
|
||||||
.map<ImageChunkListener>((ImageStreamListener listener) => listener.onChunk)
|
.map<ImageChunkListener?>((ImageStreamListener listener) => listener.onChunk)
|
||||||
.where((ImageChunkListener chunkListener) => chunkListener != null)
|
.whereType<ImageChunkListener>()
|
||||||
.toList();
|
.toList();
|
||||||
for (final ImageChunkListener listener in localListeners) {
|
for (final ImageChunkListener listener in localListeners) {
|
||||||
listener(event);
|
listener(event);
|
||||||
@ -553,7 +552,7 @@ abstract class ImageStreamCompleter with Diagnosticable {
|
|||||||
description.add(ObjectFlagProperty<List<ImageStreamListener>>(
|
description.add(ObjectFlagProperty<List<ImageStreamListener>>(
|
||||||
'listeners',
|
'listeners',
|
||||||
_listeners,
|
_listeners,
|
||||||
ifPresent: '${_listeners?.length} listener${_listeners?.length == 1 ? "" : "s" }',
|
ifPresent: '${_listeners.length} listener${_listeners.length == 1 ? "" : "s" }',
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -575,7 +574,7 @@ class OneFrameImageStreamCompleter extends ImageStreamCompleter {
|
|||||||
/// argument on [FlutterErrorDetails] set to true, meaning that by default the
|
/// argument on [FlutterErrorDetails] set to true, meaning that by default the
|
||||||
/// message is only dumped to the console in debug mode (see [new
|
/// message is only dumped to the console in debug mode (see [new
|
||||||
/// FlutterErrorDetails]).
|
/// FlutterErrorDetails]).
|
||||||
OneFrameImageStreamCompleter(Future<ImageInfo> image, { InformationCollector informationCollector })
|
OneFrameImageStreamCompleter(Future<ImageInfo> image, { InformationCollector? informationCollector })
|
||||||
: assert(image != null) {
|
: assert(image != null) {
|
||||||
image.then<void>(setImage, onError: (dynamic error, StackTrace stack) {
|
image.then<void>(setImage, onError: (dynamic error, StackTrace stack) {
|
||||||
reportError(
|
reportError(
|
||||||
@ -640,11 +639,11 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter {
|
|||||||
/// produced by the stream will be delivered to registered [ImageChunkListener]s
|
/// produced by the stream will be delivered to registered [ImageChunkListener]s
|
||||||
/// (see [addListener]).
|
/// (see [addListener]).
|
||||||
MultiFrameImageStreamCompleter({
|
MultiFrameImageStreamCompleter({
|
||||||
@required Future<ui.Codec> codec,
|
required Future<ui.Codec> codec,
|
||||||
@required double scale,
|
required double scale,
|
||||||
String debugLabel,
|
String? debugLabel,
|
||||||
Stream<ImageChunkEvent> chunkEvents,
|
Stream<ImageChunkEvent>? chunkEvents,
|
||||||
InformationCollector informationCollector,
|
InformationCollector? informationCollector,
|
||||||
}) : assert(codec != null),
|
}) : assert(codec != null),
|
||||||
_informationCollector = informationCollector,
|
_informationCollector = informationCollector,
|
||||||
_scale = scale {
|
_scale = scale {
|
||||||
@ -673,17 +672,17 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.Codec _codec;
|
ui.Codec? _codec;
|
||||||
final double _scale;
|
final double _scale;
|
||||||
final InformationCollector _informationCollector;
|
final InformationCollector? _informationCollector;
|
||||||
ui.FrameInfo _nextFrame;
|
ui.FrameInfo? _nextFrame;
|
||||||
// When the current was first shown.
|
// When the current was first shown.
|
||||||
Duration _shownTimestamp;
|
late Duration _shownTimestamp;
|
||||||
// The requested duration for the current frame;
|
// The requested duration for the current frame;
|
||||||
Duration _frameDuration;
|
Duration? _frameDuration;
|
||||||
// How many frames have been emitted so far.
|
// How many frames have been emitted so far.
|
||||||
int _framesEmitted = 0;
|
int _framesEmitted = 0;
|
||||||
Timer _timer;
|
Timer? _timer;
|
||||||
|
|
||||||
// Used to guard against registering multiple _handleAppFrame callbacks for the same frame.
|
// Used to guard against registering multiple _handleAppFrame callbacks for the same frame.
|
||||||
bool _frameCallbackScheduled = false;
|
bool _frameCallbackScheduled = false;
|
||||||
@ -702,17 +701,17 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter {
|
|||||||
if (!hasListeners)
|
if (!hasListeners)
|
||||||
return;
|
return;
|
||||||
if (_isFirstFrame() || _hasFrameDurationPassed(timestamp)) {
|
if (_isFirstFrame() || _hasFrameDurationPassed(timestamp)) {
|
||||||
_emitFrame(ImageInfo(image: _nextFrame.image, scale: _scale, debugLabel: debugLabel));
|
_emitFrame(ImageInfo(image: _nextFrame!.image, scale: _scale, debugLabel: debugLabel));
|
||||||
_shownTimestamp = timestamp;
|
_shownTimestamp = timestamp;
|
||||||
_frameDuration = _nextFrame.duration;
|
_frameDuration = _nextFrame!.duration;
|
||||||
_nextFrame = null;
|
_nextFrame = null;
|
||||||
final int completedCycles = _framesEmitted ~/ _codec.frameCount;
|
final int completedCycles = _framesEmitted ~/ _codec!.frameCount;
|
||||||
if (_codec.repetitionCount == -1 || completedCycles <= _codec.repetitionCount) {
|
if (_codec!.repetitionCount == -1 || completedCycles <= _codec!.repetitionCount) {
|
||||||
_decodeNextFrameAndSchedule();
|
_decodeNextFrameAndSchedule();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final Duration delay = _frameDuration - (timestamp - _shownTimestamp);
|
final Duration delay = _frameDuration! - (timestamp - _shownTimestamp);
|
||||||
_timer = Timer(delay * timeDilation, () {
|
_timer = Timer(delay * timeDilation, () {
|
||||||
_scheduleAppFrame();
|
_scheduleAppFrame();
|
||||||
});
|
});
|
||||||
@ -723,13 +722,12 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _hasFrameDurationPassed(Duration timestamp) {
|
bool _hasFrameDurationPassed(Duration timestamp) {
|
||||||
assert(_shownTimestamp != null);
|
return timestamp - _shownTimestamp >= _frameDuration!;
|
||||||
return timestamp - _shownTimestamp >= _frameDuration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _decodeNextFrameAndSchedule() async {
|
Future<void> _decodeNextFrameAndSchedule() async {
|
||||||
try {
|
try {
|
||||||
_nextFrame = await _codec.getNextFrame();
|
_nextFrame = await _codec!.getNextFrame();
|
||||||
} catch (exception, stack) {
|
} catch (exception, stack) {
|
||||||
reportError(
|
reportError(
|
||||||
context: ErrorDescription('resolving an image frame'),
|
context: ErrorDescription('resolving an image frame'),
|
||||||
@ -740,10 +738,10 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_codec.frameCount == 1) {
|
if (_codec!.frameCount == 1) {
|
||||||
// This is not an animated image, just return it and don't schedule more
|
// This is not an animated image, just return it and don't schedule more
|
||||||
// frames.
|
// frames.
|
||||||
_emitFrame(ImageInfo(image: _nextFrame.image, scale: _scale, debugLabel: debugLabel));
|
_emitFrame(ImageInfo(image: _nextFrame!.image, scale: _scale, debugLabel: debugLabel));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_scheduleAppFrame();
|
_scheduleAppFrame();
|
||||||
@ -754,7 +752,7 @@ class MultiFrameImageStreamCompleter extends ImageStreamCompleter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_frameCallbackScheduled = true;
|
_frameCallbackScheduled = true;
|
||||||
SchedulerBinding.instance.scheduleFrameCallback(_handleAppFrame);
|
SchedulerBinding.instance!.scheduleFrameCallback(_handleAppFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _emitFrame(ImageInfo imageInfo) {
|
void _emitFrame(ImageInfo imageInfo) {
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// @dart = 2.8
|
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user