Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
e3d9ecae
Unverified
Commit
e3d9ecae
authored
Jun 12, 2018
by
Jonah Williams
Committed by
GitHub
Jun 12, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add ability to cache images by size (#18135)
parent
dac2ebf0
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
357 additions
and
91 deletions
+357
-91
image_cache.dart
packages/flutter/lib/src/painting/image_cache.dart
+108
-18
image_provider.dart
packages/flutter/lib/src/painting/image_provider.dart
+44
-0
image_cache_resize_test.dart
packages/flutter/test/painting/image_cache_resize_test.dart
+55
-20
image_cache_test.dart
packages/flutter/test/painting/image_cache_test.dart
+82
-46
image_provider_test.dart
packages/flutter/test/painting/image_provider_test.dart
+47
-4
mocks_for_image_cache.dart
packages/flutter/test/painting/mocks_for_image_cache.dart
+18
-0
image_test.dart
packages/flutter/test/widgets/image_test.dart
+3
-3
No files found.
packages/flutter/lib/src/painting/image_cache.dart
View file @
e3d9ecae
...
...
@@ -5,6 +5,7 @@
import
'image_stream.dart'
;
const
int
_kDefaultSize
=
1000
;
const
int
_kDefaultSizeBytes
=
10485760
;
// 10 MiB
/// Class for the [imageCache] object.
///
...
...
@@ -25,7 +26,8 @@ const int _kDefaultSize = 1000;
/// Generally this class is not used directly. The [ImageProvider] class and its
/// subclasses automatically handle the caching of images.
class
ImageCache
{
final
Map
<
Object
,
ImageStreamCompleter
>
_cache
=
<
Object
,
ImageStreamCompleter
>{};
final
Map
<
Object
,
ImageStreamCompleter
>
_pendingImages
=
<
Object
,
ImageStreamCompleter
>{};
final
Map
<
Object
,
_CachedImage
>
_cache
=
<
Object
,
_CachedImage
>{};
/// Maximum number of entries to store in the cache.
///
...
...
@@ -47,20 +49,76 @@ class ImageCache {
_maximumSize
=
value
;
if
(
maximumSize
==
0
)
{
_cache
.
clear
();
_currentSizeBytes
=
0
;
}
else
{
while
(
_cache
.
length
>
maximumSize
)
_cache
.
remove
(
_cache
.
keys
.
first
);
_checkCacheSize
();
}
}
/// The current number of cached entries.
int
get
currentSize
=>
_cache
.
length
;
/// Maximum size of entries to store in the cache in bytes.
///
/// Once more than this amount of bytes have been cached, the
/// least-recently-used entry is evicted until there are fewer than the
/// maximum bytes.
int
get
maximumSizeBytes
=>
_maximumSizeBytes
;
int
_maximumSizeBytes
=
_kDefaultSizeBytes
;
/// Changes the maximum cache bytes.
///
/// If the new size is smaller than the current size in bytes, the
/// extraneous elements are evicted immediately. Setting this to zero and then
/// returning it to its original value will therefore immediately clear the
/// cache.
set
maximumSizeBytes
(
int
value
)
{
assert
(
value
!=
null
);
assert
(
value
>=
0
);
if
(
value
==
_maximumSizeBytes
)
return
;
_maximumSizeBytes
=
value
;
if
(
_maximumSizeBytes
==
0
)
{
_cache
.
clear
();
_currentSizeBytes
=
0
;
}
else
{
_checkCacheSize
();
}
}
/// The current size of cached entries in bytes.
int
get
currentSizeBytes
=>
_currentSizeBytes
;
int
_currentSizeBytes
=
0
;
/// Evicts all entries from the cache.
///
/// This is useful if, for instance, the root asset bundle has been updated
/// and therefore new images must be obtained.
// TODO(ianh): Provide a way to target individual images. This is currently non-trivial
// because by the time we get to the imageCache, the keys we're using are opaque.
///
/// Images which have not finished loading yet will not be removed from the
/// cache, and when they complete they will be inserted as normal.
void
clear
()
{
_cache
.
clear
();
_currentSizeBytes
=
0
;
}
/// Evicts a single entry from the cache, returning true if successful.
///
/// The [key] must be equal to an object used to cache an image in
/// [ImageCache.putIfAbsent].
///
/// If the key is not immediately available, as is common, consider using
/// [ImageProvider.evict] to call this method indirectly instead.
///
/// See also:
///
/// * [ImageProvider], for providing images to the [Image] widget.
bool
evict
(
Object
key
)
{
final
_CachedImage
image
=
_cache
.
remove
(
key
);
if
(
image
!=
null
)
{
_currentSizeBytes
-=
image
.
sizeBytes
;
return
true
;
}
return
false
;
}
/// Returns the previously cached [ImageStream] for the given key, if available;
...
...
@@ -71,21 +129,53 @@ class ImageCache {
ImageStreamCompleter
putIfAbsent
(
Object
key
,
ImageStreamCompleter
loader
())
{
assert
(
key
!=
null
);
assert
(
loader
!=
null
);
ImageStreamCompleter
result
=
_cache
[
key
];
if
(
result
!=
null
)
{
// Remove the provider from the list so that we can put it back in below
// and thus move it to the end of the list.
_cache
.
remove
(
key
);
}
else
{
if
(
_cache
.
length
==
maximumSize
&&
maximumSize
>
0
)
_cache
.
remove
(
_cache
.
keys
.
first
);
result
=
loader
();
ImageStreamCompleter
result
=
_pendingImages
[
key
];
// Nothing needs to be done because the image hasn't loaded yet.
if
(
result
!=
null
)
return
result
;
// Remove the provider from the list so that we can move it to the
// recently used position below.
final
_CachedImage
image
=
_cache
.
remove
(
key
);
if
(
image
!=
null
)
{
_cache
[
key
]
=
image
;
return
image
.
completer
;
}
if
(
maximumSize
>
0
)
{
assert
(
_cache
.
length
<
maximumSize
);
_cache
[
key
]
=
result
;
result
=
loader
();
void
listener
(
ImageInfo
info
,
bool
syncCall
)
{
// 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
_CachedImage
image
=
new
_CachedImage
(
result
,
imageSize
);
_currentSizeBytes
+=
imageSize
;
_pendingImages
.
remove
(
key
);
_cache
[
key
]
=
image
;
result
.
removeListener
(
listener
);
_checkCacheSize
();
}
if
(
maximumSize
>
0
&&
maximumSizeBytes
>
0
)
{
_pendingImages
[
key
]
=
result
;
result
.
addListener
(
listener
);
}
assert
(
_cache
.
length
<=
maximumSize
);
return
result
;
}
// Remove images from the cache until both the length and bytes are below
// maximum, or the cache is empty.
void
_checkCacheSize
()
{
while
(
_currentSizeBytes
>
_maximumSizeBytes
||
_cache
.
length
>
_maximumSize
)
{
final
Object
key
=
_cache
.
keys
.
first
;
final
_CachedImage
image
=
_cache
[
key
];
_currentSizeBytes
-=
image
.
sizeBytes
;
_cache
.
remove
(
key
);
}
assert
(
_currentSizeBytes
>=
0
);
assert
(
_cache
.
length
<=
maximumSize
);
assert
(
_currentSizeBytes
<=
maximumSizeBytes
);
}
}
class
_CachedImage
{
_CachedImage
(
this
.
completer
,
this
.
sizeBytes
);
final
ImageStreamCompleter
completer
;
final
int
sizeBytes
;
}
packages/flutter/lib/src/painting/image_provider.dart
View file @
e3d9ecae
...
...
@@ -12,6 +12,7 @@ import 'package:flutter/foundation.dart';
import
'package:flutter/services.dart'
;
import
'binding.dart'
;
import
'image_cache.dart'
;
import
'image_stream.dart'
;
/// Configuration information passed to the [ImageProvider.resolve] method to
...
...
@@ -284,6 +285,49 @@ abstract class ImageProvider<T> {
return
stream
;
}
/// Evicts an entry from the image cache.
///
/// Returns a [Future] which indicates whether the value was successfully
/// removed.
///
/// The [ImageProvider] used does not need to be the same instance that was
/// passed to an [Image] widget, but it does need to create a key which is
/// equal to one.
///
/// The [cache] is optional and defaults to the global image cache.
///
/// The [configuration] is optional and defaults to
/// [ImageConfiguration.empty].
///
/// ## Sample code
///
/// The following sample code shows how an image loaded using the [Image]
/// widget can be evicted using a [NetworkImage] with a matching url.
///
/// ```dart
/// class MyWidget extends StatelessWidget {
/// final String url = '...';
///
/// @override
/// Widget build(BuildContext context) {
/// return new Image.network(url);
/// }
///
/// void evictImage() {
/// final NetworkImage provider = new NetworkImage(url);
/// provider.evict().then<void>((bool success) {
/// if (success)
/// debugPrint('removed image!');
/// });
/// }
/// }
/// ```
Future
<
bool
>
evict
({
ImageCache
cache
,
ImageConfiguration
configuration
=
ImageConfiguration
.
empty
})
async
{
cache
??=
imageCache
;
final
T
key
=
await
obtainKey
(
configuration
);
return
cache
.
evict
(
key
);
}
/// Converts an ImageProvider's settings plus an ImageConfiguration to a key
/// that describes the precise image to load.
///
...
...
packages/flutter/test/painting/image_cache_resize_test.dart
View file @
e3d9ecae
...
...
@@ -10,35 +10,70 @@ import 'mocks_for_image_cache.dart';
void
main
(
)
{
new
TestRenderingFlutterBinding
();
// initializes the imageCache
group
(
ImageCache
,
()
{
tearDown
(()
{
imageCache
.
clear
();
imageCache
.
maximumSize
=
1000
;
imageCache
.
maximumSizeBytes
=
10485760
;
});
test
(
'Image cache resizing'
,
()
async
{
test
(
'Image cache resizing based on count'
,
()
async
{
imageCache
.
maximumSize
=
2
;
imageCache
.
maximumSize
=
2
;
final
TestImageInfo
a
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
1
).
resolve
(
ImageConfiguration
.
empty
));
final
TestImageInfo
b
=
await
extractOneFrame
(
const
TestImageProvider
(
2
,
2
).
resolve
(
ImageConfiguration
.
empty
));
final
TestImageInfo
c
=
await
extractOneFrame
(
const
TestImageProvider
(
3
,
3
).
resolve
(
ImageConfiguration
.
empty
));
final
TestImageInfo
d
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
4
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
a
.
value
,
equals
(
1
));
expect
(
b
.
value
,
equals
(
2
));
expect
(
c
.
value
,
equals
(
3
));
expect
(
d
.
value
,
equals
(
4
));
final
TestImageInfo
a
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
1
).
resolve
(
ImageConfiguration
.
empty
));
final
TestImageInfo
b
=
await
extractOneFrame
(
const
TestImageProvider
(
2
,
2
).
resolve
(
ImageConfiguration
.
empty
));
final
TestImageInfo
c
=
await
extractOneFrame
(
const
TestImageProvider
(
3
,
3
).
resolve
(
ImageConfiguration
.
empty
));
final
TestImageInfo
d
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
4
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
a
.
value
,
equals
(
1
));
expect
(
b
.
value
,
equals
(
2
));
expect
(
c
.
value
,
equals
(
3
));
expect
(
d
.
value
,
equals
(
4
));
imageCache
.
maximumSize
=
0
;
imageCache
.
maximumSize
=
0
;
final
TestImageInfo
e
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
5
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
e
.
value
,
equals
(
5
));
final
TestImageInfo
e
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
5
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
e
.
value
,
equals
(
5
));
final
TestImageInfo
f
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
6
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
f
.
value
,
equals
(
6
));
final
TestImageInfo
f
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
6
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
f
.
value
,
equals
(
6
));
imageCache
.
maximumSize
=
3
;
imageCache
.
maximumSize
=
3
;
final
TestImageInfo
g
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
7
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
g
.
value
,
equals
(
7
));
final
TestImageInfo
g
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
7
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
g
.
value
,
equals
(
7
));
final
TestImageInfo
h
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
8
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
h
.
value
,
equals
(
7
));
});
final
TestImageInfo
h
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
8
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
h
.
value
,
equals
(
7
));
test
(
'Image cache resizing based on size'
,
()
async
{
const
TestImage
testImage
=
const
TestImage
(
width:
8
,
height:
8
);
// 256 B.
imageCache
.
maximumSizeBytes
=
256
*
2
;
final
TestImageInfo
a
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
1
,
image:
testImage
).
resolve
(
ImageConfiguration
.
empty
));
final
TestImageInfo
b
=
await
extractOneFrame
(
const
TestImageProvider
(
2
,
2
,
image:
testImage
).
resolve
(
ImageConfiguration
.
empty
));
final
TestImageInfo
c
=
await
extractOneFrame
(
const
TestImageProvider
(
3
,
3
,
image:
testImage
).
resolve
(
ImageConfiguration
.
empty
));
final
TestImageInfo
d
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
4
,
image:
testImage
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
a
.
value
,
equals
(
1
));
expect
(
b
.
value
,
equals
(
2
));
expect
(
c
.
value
,
equals
(
3
));
expect
(
d
.
value
,
equals
(
4
));
imageCache
.
maximumSizeBytes
=
0
;
final
TestImageInfo
e
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
5
,
image:
testImage
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
e
.
value
,
equals
(
5
));
final
TestImageInfo
f
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
6
,
image:
testImage
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
f
.
value
,
equals
(
6
));
imageCache
.
maximumSizeBytes
=
256
*
3
;
final
TestImageInfo
g
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
7
,
image:
testImage
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
g
.
value
,
equals
(
7
));
final
TestImageInfo
h
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
8
,
image:
testImage
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
h
.
value
,
equals
(
7
));
});
});
}
packages/flutter/test/painting/image_cache_test.dart
View file @
e3d9ecae
...
...
@@ -10,77 +10,113 @@ import 'mocks_for_image_cache.dart';
void
main
(
)
{
new
TestRenderingFlutterBinding
();
// initializes the imageCache
group
(
ImageCache
,
()
{
tearDown
(()
{
imageCache
.
clear
();
imageCache
.
maximumSize
=
1000
;
imageCache
.
maximumSizeBytes
=
10485760
;
});
test
(
'Image cache'
,
()
async
{
test
(
'maintains cache size'
,
()
async
{
imageCache
.
maximumSize
=
3
;
imageCache
.
maximumSize
=
3
;
final
TestImageInfo
a
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
1
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
a
.
value
,
equals
(
1
));
final
TestImageInfo
b
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
2
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
b
.
value
,
equals
(
1
));
final
TestImageInfo
c
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
3
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
c
.
value
,
equals
(
1
));
final
TestImageInfo
d
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
4
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
d
.
value
,
equals
(
1
));
final
TestImageInfo
e
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
5
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
e
.
value
,
equals
(
1
));
final
TestImageInfo
f
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
6
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
f
.
value
,
equals
(
1
));
final
TestImageInfo
a
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
1
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
a
.
value
,
equals
(
1
));
final
TestImageInfo
b
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
2
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
b
.
value
,
equals
(
1
));
final
TestImageInfo
c
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
3
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
c
.
value
,
equals
(
1
));
final
TestImageInfo
d
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
4
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
d
.
value
,
equals
(
1
));
final
TestImageInfo
e
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
5
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
e
.
value
,
equals
(
1
));
final
TestImageInfo
f
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
6
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
f
.
value
,
equals
(
1
));
expect
(
f
,
equals
(
a
));
expect
(
f
,
equals
(
a
));
// cache still only has one entry in it: 1(1)
// cache still only has one entry in it: 1(1)
final
TestImageInfo
g
=
await
extractOneFrame
(
const
TestImageProvider
(
2
,
7
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
g
.
value
,
equals
(
7
));
final
TestImageInfo
g
=
await
extractOneFrame
(
const
TestImageProvider
(
2
,
7
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
g
.
value
,
equals
(
7
));
// cache has two entries in it: 1(1), 2(7)
// cache has two entries in it: 1(1), 2(7)
final
TestImageInfo
h
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
8
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
h
.
value
,
equals
(
1
));
final
TestImageInfo
h
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
8
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
h
.
value
,
equals
(
1
));
// cache still has two entries in it: 2(7), 1(1)
// cache still has two entries in it: 2(7), 1(1)
final
TestImageInfo
i
=
await
extractOneFrame
(
const
TestImageProvider
(
3
,
9
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
i
.
value
,
equals
(
9
));
final
TestImageInfo
i
=
await
extractOneFrame
(
const
TestImageProvider
(
3
,
9
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
i
.
value
,
equals
(
9
));
// cache has three entries in it: 2(7), 1(1), 3(9)
// cache has three entries in it: 2(7), 1(1), 3(9)
final
TestImageInfo
j
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
10
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
j
.
value
,
equals
(
1
));
final
TestImageInfo
j
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
10
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
j
.
value
,
equals
(
1
));
// cache still has three entries in it: 2(7), 3(9), 1(1)
// cache still has three entries in it: 2(7), 3(9), 1(1)
final
TestImageInfo
k
=
await
extractOneFrame
(
const
TestImageProvider
(
4
,
11
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
k
.
value
,
equals
(
11
));
final
TestImageInfo
k
=
await
extractOneFrame
(
const
TestImageProvider
(
4
,
11
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
k
.
value
,
equals
(
11
));
// cache has three entries: 3(9), 1(1), 4(11)
// cache has three entries: 3(9), 1(1), 4(11)
final
TestImageInfo
l
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
12
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
l
.
value
,
equals
(
1
));
final
TestImageInfo
l
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
12
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
l
.
value
,
equals
(
1
));
// cache has three entries: 3(9), 4(11), 1(1)
// cache has three entries: 3(9), 4(11), 1(1)
final
TestImageInfo
m
=
await
extractOneFrame
(
const
TestImageProvider
(
2
,
13
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
m
.
value
,
equals
(
13
));
final
TestImageInfo
m
=
await
extractOneFrame
(
const
TestImageProvider
(
2
,
13
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
m
.
value
,
equals
(
13
));
// cache has three entries: 4(11), 1(1), 2(13)
// cache has three entries: 4(11), 1(1), 2(13)
final
TestImageInfo
n
=
await
extractOneFrame
(
const
TestImageProvider
(
3
,
14
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
n
.
value
,
equals
(
14
));
final
TestImageInfo
n
=
await
extractOneFrame
(
const
TestImageProvider
(
3
,
14
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
n
.
value
,
equals
(
14
));
// cache has three entries: 1(1), 2(13), 3(14)
// cache has three entries: 1(1), 2(13), 3(14)
final
TestImageInfo
o
=
await
extractOneFrame
(
const
TestImageProvider
(
4
,
15
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
o
.
value
,
equals
(
15
));
final
TestImageInfo
o
=
await
extractOneFrame
(
const
TestImageProvider
(
4
,
15
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
o
.
value
,
equals
(
15
));
// cache has three entries: 2(13), 3(14), 4(15)
// cache has three entries: 2(13), 3(14), 4(15)
final
TestImageInfo
p
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
16
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
p
.
value
,
equals
(
16
));
final
TestImageInfo
p
=
await
extractOneFrame
(
const
TestImageProvider
(
1
,
16
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
p
.
value
,
equals
(
16
));
// cache has three entries: 3(14), 4(15), 1(16)
// cache has three entries: 3(14), 4(15), 1(16)
});
test
(
'clear removes all images and resets cache size'
,
()
async
{
const
TestImage
testImage
=
const
TestImage
(
width:
8
,
height:
8
);
expect
(
imageCache
.
currentSize
,
0
);
expect
(
imageCache
.
currentSizeBytes
,
0
);
await
extractOneFrame
(
const
TestImageProvider
(
1
,
1
,
image:
testImage
).
resolve
(
ImageConfiguration
.
empty
));
await
extractOneFrame
(
const
TestImageProvider
(
2
,
2
,
image:
testImage
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
imageCache
.
currentSize
,
2
);
expect
(
imageCache
.
currentSizeBytes
,
256
*
2
);
imageCache
.
clear
();
expect
(
imageCache
.
currentSize
,
0
);
expect
(
imageCache
.
currentSizeBytes
,
0
);
});
test
(
'evicts individual images'
,
()
async
{
const
TestImage
testImage
=
const
TestImage
(
width:
8
,
height:
8
);
await
extractOneFrame
(
const
TestImageProvider
(
1
,
1
,
image:
testImage
).
resolve
(
ImageConfiguration
.
empty
));
await
extractOneFrame
(
const
TestImageProvider
(
2
,
2
,
image:
testImage
).
resolve
(
ImageConfiguration
.
empty
));
expect
(
imageCache
.
currentSize
,
2
);
expect
(
imageCache
.
currentSizeBytes
,
256
*
2
);
expect
(
imageCache
.
evict
(
1
),
true
);
expect
(
imageCache
.
currentSize
,
1
);
expect
(
imageCache
.
currentSizeBytes
,
256
);
});
});
}
packages/flutter/test/painting/image_provider_test.dart
View file @
e3d9ecae
...
...
@@ -2,13 +2,56 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:typed_data'
;
import
'package:flutter/painting.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'../rendering/rendering_tester.dart'
;
import
'image_data.dart'
;
void
main
(
)
{
test
(
'NetworkImage non-null url test'
,
()
{
expect
(()
{
new
NetworkImage
(
nonconst
(
null
));
},
throwsAssertionError
);
new
TestRenderingFlutterBinding
();
// initializes the imageCache
group
(
ImageProvider
,
()
{
tearDown
(()
{
imageCache
.
clear
();
});
test
(
'NetworkImage non-null url test'
,
()
{
expect
(()
{
new
NetworkImage
(
nonconst
(
null
));
},
throwsAssertionError
);
});
test
(
'ImageProvider can evict images'
,
()
async
{
final
Uint8List
bytes
=
new
Uint8List
.
fromList
(
kTransparentImage
);
final
MemoryImage
imageProvider
=
new
MemoryImage
(
bytes
);
final
ImageStream
stream
=
imageProvider
.
resolve
(
ImageConfiguration
.
empty
);
final
Completer
<
void
>
completer
=
new
Completer
<
void
>();
stream
.
addListener
((
ImageInfo
info
,
bool
syncCall
)
=>
completer
.
complete
());
await
completer
.
future
;
expect
(
imageCache
.
currentSize
,
1
);
expect
(
await
new
MemoryImage
(
bytes
).
evict
(),
true
);
expect
(
imageCache
.
currentSize
,
0
);
});
test
(
'ImageProvider.evict respects the provided ImageCache'
,
()
async
{
final
ImageCache
otherCache
=
new
ImageCache
();
final
Uint8List
bytes
=
new
Uint8List
.
fromList
(
kTransparentImage
);
final
MemoryImage
imageProvider
=
new
MemoryImage
(
bytes
);
otherCache
.
putIfAbsent
(
imageProvider
,
()
=>
imageProvider
.
load
(
imageProvider
));
final
ImageStream
stream
=
imageProvider
.
resolve
(
ImageConfiguration
.
empty
);
final
Completer
<
void
>
completer
=
new
Completer
<
void
>();
stream
.
addListener
((
ImageInfo
info
,
bool
syncCall
)
=>
completer
.
complete
());
await
completer
.
future
;
expect
(
otherCache
.
currentSize
,
1
);
expect
(
imageCache
.
currentSize
,
1
);
expect
(
await
imageProvider
.
evict
(
cache:
otherCache
),
true
);
expect
(
otherCache
.
currentSize
,
0
);
expect
(
imageCache
.
currentSize
,
1
);
});
});
}
packages/flutter/test/painting/mocks_for_image_cache.dart
View file @
e3d9ecae
...
...
@@ -3,7 +3,9 @@
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:typed_data'
;
import
'dart:ui'
as
ui
show
Image
;
import
'dart:ui'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/painting.dart'
;
...
...
@@ -54,3 +56,19 @@ Future<ImageInfo> extractOneFrame(ImageStream stream) {
stream
.
addListener
(
listener
);
return
completer
.
future
;
}
class
TestImage
implements
ui
.
Image
{
const
TestImage
({
this
.
height
=
0
,
this
.
width
=
0
});
@override
final
int
height
;
@override
final
int
width
;
@override
void
dispose
()
{}
@override
Future
<
ByteData
>
toByteData
({
ImageByteFormat
format
=
ImageByteFormat
.
rawRgba
})
{
throw
new
UnimplementedError
();
}
}
packages/flutter/test/widgets/image_test.dart
View file @
e3d9ecae
...
...
@@ -295,7 +295,7 @@ void main() {
final
TestImageProvider
imageProvider
=
new
TestImageProvider
();
await
tester
.
pumpWidget
(
new
Image
(
image:
imageProvider
));
final
State
<
Image
>
image
=
tester
.
state
/*State<Image>*/
(
find
.
byType
(
Image
));
expect
(
image
.
toString
(),
equalsIgnoringHashCodes
(
'_ImageState#00000(stream: ImageStream#00000(OneFrameImageStreamCompleter#00000, unresolved,
1 listener
), pixels: null)'
));
expect
(
image
.
toString
(),
equalsIgnoringHashCodes
(
'_ImageState#00000(stream: ImageStream#00000(OneFrameImageStreamCompleter#00000, unresolved,
2 listeners
), pixels: null)'
));
imageProvider
.
complete
();
await
tester
.
pump
();
expect
(
image
.
toString
(),
equalsIgnoringHashCodes
(
'_ImageState#00000(stream: ImageStream#00000(OneFrameImageStreamCompleter#00000, [100×100] @ 1.0x, 1 listener), pixels: [100×100] @ 1.0x)'
));
...
...
@@ -353,14 +353,14 @@ void main() {
child:
image
,
),
);
expect
(
imageStreamCompleter
.
listeners
.
length
,
1
);
expect
(
imageStreamCompleter
.
listeners
.
length
,
2
);
await
tester
.
pumpWidget
(
new
TickerMode
(
enabled:
false
,
child:
image
,
),
);
expect
(
imageStreamCompleter
.
listeners
.
length
,
0
);
expect
(
imageStreamCompleter
.
listeners
.
length
,
1
);
});
testWidgets
(
'Verify Image shows correct RenderImage when changing to an already completed provider'
,
(
WidgetTester
tester
)
async
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment