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
73b2895f
Unverified
Commit
73b2895f
authored
Mar 12, 2020
by
Dan Field
Committed by
GitHub
Mar 12, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add errorBuilder to Image widget (#52481)
parent
1bf9d6f4
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
157 additions
and
0 deletions
+157
-0
image.dart
packages/flutter/lib/src/widgets/image.dart
+67
-0
image_test.dart
packages/flutter/test/widgets/image_test.dart
+90
-0
No files found.
packages/flutter/lib/src/widgets/image.dart
View file @
73b2895f
...
@@ -221,6 +221,14 @@ typedef ImageLoadingBuilder = Widget Function(
...
@@ -221,6 +221,14 @@ typedef ImageLoadingBuilder = Widget Function(
ImageChunkEvent
loadingProgress
,
ImageChunkEvent
loadingProgress
,
);
);
/// Signature used by [Image.errorBuilder] to create a replacement widget to
/// render instead of the image.
typedef
ImageErrorWidgetBuilder
=
Widget
Function
(
BuildContext
context
,
Object
error
,
StackTrace
stackTrace
,
);
/// A widget that displays an image.
/// A widget that displays an image.
///
///
/// Several constructors are provided for the various ways that an image can be
/// Several constructors are provided for the various ways that an image can be
...
@@ -311,6 +319,7 @@ class Image extends StatefulWidget {
...
@@ -311,6 +319,7 @@ class Image extends StatefulWidget {
@required
this
.
image
,
@required
this
.
image
,
this
.
frameBuilder
,
this
.
frameBuilder
,
this
.
loadingBuilder
,
this
.
loadingBuilder
,
this
.
errorBuilder
,
this
.
semanticLabel
,
this
.
semanticLabel
,
this
.
excludeFromSemantics
=
false
,
this
.
excludeFromSemantics
=
false
,
this
.
width
,
this
.
width
,
...
@@ -370,6 +379,7 @@ class Image extends StatefulWidget {
...
@@ -370,6 +379,7 @@ class Image extends StatefulWidget {
double
scale
=
1.0
,
double
scale
=
1.0
,
this
.
frameBuilder
,
this
.
frameBuilder
,
this
.
loadingBuilder
,
this
.
loadingBuilder
,
this
.
errorBuilder
,
this
.
semanticLabel
,
this
.
semanticLabel
,
this
.
excludeFromSemantics
=
false
,
this
.
excludeFromSemantics
=
false
,
this
.
width
,
this
.
width
,
...
@@ -423,6 +433,7 @@ class Image extends StatefulWidget {
...
@@ -423,6 +433,7 @@ class Image extends StatefulWidget {
Key
key
,
Key
key
,
double
scale
=
1.0
,
double
scale
=
1.0
,
this
.
frameBuilder
,
this
.
frameBuilder
,
this
.
errorBuilder
,
this
.
semanticLabel
,
this
.
semanticLabel
,
this
.
excludeFromSemantics
=
false
,
this
.
excludeFromSemantics
=
false
,
this
.
width
,
this
.
width
,
...
@@ -584,6 +595,7 @@ class Image extends StatefulWidget {
...
@@ -584,6 +595,7 @@ class Image extends StatefulWidget {
Key
key
,
Key
key
,
AssetBundle
bundle
,
AssetBundle
bundle
,
this
.
frameBuilder
,
this
.
frameBuilder
,
this
.
errorBuilder
,
this
.
semanticLabel
,
this
.
semanticLabel
,
this
.
excludeFromSemantics
=
false
,
this
.
excludeFromSemantics
=
false
,
double
scale
,
double
scale
,
...
@@ -643,6 +655,7 @@ class Image extends StatefulWidget {
...
@@ -643,6 +655,7 @@ class Image extends StatefulWidget {
Key
key
,
Key
key
,
double
scale
=
1.0
,
double
scale
=
1.0
,
this
.
frameBuilder
,
this
.
frameBuilder
,
this
.
errorBuilder
,
this
.
semanticLabel
,
this
.
semanticLabel
,
this
.
excludeFromSemantics
=
false
,
this
.
excludeFromSemantics
=
false
,
this
.
width
,
this
.
width
,
...
@@ -822,6 +835,43 @@ class Image extends StatefulWidget {
...
@@ -822,6 +835,43 @@ class Image extends StatefulWidget {
/// {@animation 400 400 https://flutter.github.io/assets-for-api-docs/assets/widgets/loading_progress_image.mp4}
/// {@animation 400 400 https://flutter.github.io/assets-for-api-docs/assets/widgets/loading_progress_image.mp4}
final
ImageLoadingBuilder
loadingBuilder
;
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 --template=stateless_widget_material}
///
/// The following sample uses [errorBuilder] to show a '😢' in place of the
/// image that fails to load, and prints the error to the console.
///
/// ```dart
/// Widget build(BuildContext context) {
/// return DecoratedBox(
/// decoration: BoxDecoration(
/// color: Colors.white,
/// border: Border.all(),
/// borderRadius: BorderRadius.circular(20),
/// ),
/// child: Image.network(
/// 'https://example.does.not.exist/image.jpg',
/// errorBuilder: (BuildContext context, Object exception, StackTrace stackTrace) {
/// // Appropriate logging or analytics, e.g.
/// // myAnalytics.recordError(
/// // 'An error occurred loading "https://example.does.not.exist/image.jpg"',
/// // exception,
/// // stackTrace,
/// // );
/// return Text('😢');
/// },
/// ),
/// );
/// }
/// ```
/// {@end-tool}
final
ImageErrorWidgetBuilder
errorBuilder
;
/// If non-null, require the image to have this width.
/// If non-null, require the image to have this width.
///
///
/// If null, the image will pick a size that best preserves its intrinsic
/// If null, the image will pick a size that best preserves its intrinsic
...
@@ -977,6 +1027,8 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
...
@@ -977,6 +1027,8 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
int
_frameNumber
;
int
_frameNumber
;
bool
_wasSynchronouslyLoaded
;
bool
_wasSynchronouslyLoaded
;
DisposableBuildContext
<
State
<
Image
>>
_scrollAwareContext
;
DisposableBuildContext
<
State
<
Image
>>
_scrollAwareContext
;
Object
_lastException
;
StackTrace
_lastStack
;
@override
@override
void
initState
()
{
void
initState
()
{
...
@@ -1054,9 +1106,19 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
...
@@ -1054,9 +1106,19 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
ImageStreamListener
_getListener
([
ImageLoadingBuilder
loadingBuilder
])
{
ImageStreamListener
_getListener
([
ImageLoadingBuilder
loadingBuilder
])
{
loadingBuilder
??=
widget
.
loadingBuilder
;
loadingBuilder
??=
widget
.
loadingBuilder
;
_lastException
=
null
;
_lastStack
=
null
;
return
ImageStreamListener
(
return
ImageStreamListener
(
_handleImageFrame
,
_handleImageFrame
,
onChunk:
loadingBuilder
==
null
?
null
:
_handleImageChunk
,
onChunk:
loadingBuilder
==
null
?
null
:
_handleImageChunk
,
onError:
widget
.
errorBuilder
!=
null
?
(
dynamic
error
,
StackTrace
stackTrace
)
{
setState
(()
{
_lastException
=
error
;
_lastStack
=
stackTrace
;
});
}
:
null
,
);
);
}
}
...
@@ -1116,6 +1178,11 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
...
@@ -1116,6 +1178,11 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
if
(
_lastException
!=
null
)
{
assert
(
widget
.
errorBuilder
!=
null
);
return
widget
.
errorBuilder
(
context
,
_lastException
,
_lastStack
);
}
Widget
result
=
RawImage
(
Widget
result
=
RawImage
(
image:
_imageInfo
?.
image
,
image:
_imageInfo
?.
image
,
width:
widget
.
width
,
width:
widget
.
width
,
...
...
packages/flutter/test/widgets/image_test.dart
View file @
73b2895f
...
@@ -1568,6 +1568,58 @@ void main() {
...
@@ -1568,6 +1568,58 @@ void main() {
expect
(
imageCache
.
statusForKey
(
provider
).
live
,
false
);
expect
(
imageCache
.
statusForKey
(
provider
).
live
,
false
);
});
});
});
});
testWidgets
(
'errorBuilder - fails on key'
,
(
WidgetTester
tester
)
async
{
final
UniqueKey
errorKey
=
UniqueKey
();
Object
caughtException
;
await
tester
.
pumpWidget
(
Image
(
image:
const
FailingImageProvider
(
failOnObtainKey:
true
,
throws:
'threw'
),
errorBuilder:
(
BuildContext
context
,
Object
error
,
StackTrace
stackTrace
)
{
caughtException
=
error
;
return
SizedBox
.
expand
(
key:
errorKey
);
},
),
);
await
tester
.
pump
();
expect
(
find
.
byKey
(
errorKey
),
findsOneWidget
);
expect
(
caughtException
.
toString
(),
'threw'
);
expect
(
tester
.
takeException
(),
isNull
);
});
testWidgets
(
'errorBuilder - fails on load'
,
(
WidgetTester
tester
)
async
{
final
UniqueKey
errorKey
=
UniqueKey
();
Object
caughtException
;
await
tester
.
pumpWidget
(
Image
(
image:
const
FailingImageProvider
(
failOnLoad:
true
,
throws:
'threw'
),
errorBuilder:
(
BuildContext
context
,
Object
error
,
StackTrace
stackTrace
)
{
caughtException
=
error
;
return
SizedBox
.
expand
(
key:
errorKey
);
},
),
);
await
tester
.
pump
();
expect
(
find
.
byKey
(
errorKey
),
findsOneWidget
);
expect
(
caughtException
.
toString
(),
'threw'
);
expect
(
tester
.
takeException
(),
isNull
);
});
testWidgets
(
'no errorBuilder - failure reported to FlutterError'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
Image
(
image:
FailingImageProvider
(
failOnLoad:
true
,
throws:
'threw'
),
),
);
await
tester
.
pump
();
expect
(
tester
.
takeException
(),
'threw'
);
});
}
}
class
ConfigurationAwareKey
{
class
ConfigurationAwareKey
{
...
@@ -1726,3 +1778,41 @@ class DebouncingImageProvider extends ImageProvider<Object> {
...
@@ -1726,3 +1778,41 @@ class DebouncingImageProvider extends ImageProvider<Object> {
@override
@override
ImageStreamCompleter
load
(
Object
key
,
DecoderCallback
decode
)
=>
imageProvider
.
load
(
key
,
decode
);
ImageStreamCompleter
load
(
Object
key
,
DecoderCallback
decode
)
=>
imageProvider
.
load
(
key
,
decode
);
}
}
class
FailingImageProvider
extends
ImageProvider
<
int
>
{
const
FailingImageProvider
({
this
.
failOnObtainKey
=
false
,
this
.
failOnLoad
=
false
,
@required
this
.
throws
,
})
:
assert
(
failOnLoad
!=
null
),
assert
(
failOnObtainKey
!=
null
),
assert
(
failOnLoad
==
true
||
failOnObtainKey
==
true
),
assert
(
throws
!=
null
);
final
bool
failOnObtainKey
;
final
bool
failOnLoad
;
final
Object
throws
;
@override
Future
<
int
>
obtainKey
(
ImageConfiguration
configuration
)
{
if
(
failOnObtainKey
)
{
throw
throws
;
}
return
SynchronousFuture
<
int
>(
hashCode
);
}
@override
ImageStreamCompleter
load
(
int
key
,
DecoderCallback
decode
)
{
if
(
failOnLoad
)
{
throw
throws
;
}
return
OneFrameImageStreamCompleter
(
Future
<
ImageInfo
>.
value
(
ImageInfo
(
image:
TestImage
(),
scale:
0
,
),
),
);
}
}
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