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
e04bf328
Commit
e04bf328
authored
Sep 24, 2017
by
Ian Hickson
Committed by
GitHub
Sep 24, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Image RTL (#12230)
parent
ff45d506
Changes
22
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
1190 additions
and
150 deletions
+1190
-150
box_decoration.dart
packages/flutter/lib/src/painting/box_decoration.dart
+13
-1
images.dart
packages/flutter/lib/src/painting/images.dart
+81
-25
image.dart
packages/flutter/lib/src/rendering/image.dart
+95
-18
object.dart
packages/flutter/lib/src/rendering/object.dart
+1
-1
shifted_box.dart
packages/flutter/lib/src/rendering/shifted_box.dart
+39
-26
sliver_padding.dart
packages/flutter/lib/src/rendering/sliver_padding.dart
+25
-11
stack.dart
packages/flutter/lib/src/rendering/stack.dart
+25
-16
image_provider.dart
packages/flutter/lib/src/services/image_provider.dart
+17
-4
image_resolution.dart
packages/flutter/lib/src/services/image_resolution.dart
+3
-2
animated_list.dart
packages/flutter/lib/src/widgets/animated_list.dart
+1
-1
basic.dart
packages/flutter/lib/src/widgets/basic.dart
+50
-10
container.dart
packages/flutter/lib/src/widgets/container.dart
+1
-1
fade_in_image.dart
packages/flutter/lib/src/widgets/fade_in_image.dart
+53
-14
image.dart
packages/flutter/lib/src/widgets/image.dart
+74
-18
localizations.dart
packages/flutter/lib/src/widgets/localizations.dart
+8
-1
media_query.dart
packages/flutter/lib/src/widgets/media_query.dart
+2
-0
image_test.dart
packages/flutter/test/rendering/image_test.dart
+1
-0
mock_canvas.dart
packages/flutter/test/rendering/mock_canvas.dart
+59
-1
align_test.dart
packages/flutter/test/widgets/align_test.dart
+20
-0
image_rtl_test.dart
packages/flutter/test/widgets/image_rtl_test.dart
+583
-0
rtl_test.dart
packages/flutter/test/widgets/rtl_test.dart
+20
-0
stack_test.dart
packages/flutter/test/widgets/stack_test.dart
+19
-0
No files found.
packages/flutter/lib/src/painting/box_decoration.dart
View file @
e04bf328
...
...
@@ -321,9 +321,20 @@ class _BoxDecorationPainter extends BoxPainter {
ImageInfo
_image
;
void
_paintBackgroundImage
(
Canvas
canvas
,
Rect
rect
,
ImageConfiguration
configuration
)
{
// TODO(ianh): factor this out into a DecorationImage.paint method.
final
DecorationImage
backgroundImage
=
_decoration
.
image
;
if
(
backgroundImage
==
null
)
return
;
bool
flipHorizontally
=
false
;
if
(
backgroundImage
.
matchTextDirection
)
{
// We check this first so that the assert will fire immediately, not just when the
// image is ready.
assert
(
configuration
.
textDirection
!=
null
,
'matchTextDirection can only be used when a TextDirection is available.'
);
if
(
configuration
.
textDirection
==
TextDirection
.
rtl
)
flipHorizontally
=
true
;
}
final
ImageStream
newImageStream
=
backgroundImage
.
image
.
resolve
(
configuration
);
if
(
newImageStream
.
key
!=
_imageStream
?.
key
)
{
_imageStream
?.
removeListener
(
_imageListener
);
...
...
@@ -350,9 +361,10 @@ class _BoxDecorationPainter extends BoxPainter {
image:
image
,
colorFilter:
backgroundImage
.
colorFilter
,
fit:
backgroundImage
.
fit
,
alignment:
backgroundImage
.
alignment
,
alignment:
backgroundImage
.
alignment
.
resolve
(
configuration
.
textDirection
)
,
centerSlice:
backgroundImage
.
centerSlice
,
repeat:
backgroundImage
.
repeat
,
flipHorizontally:
flipHorizontally
,
);
if
(
clipPath
!=
null
)
...
...
packages/flutter/lib/src/painting/images.dart
View file @
e04bf328
...
...
@@ -35,15 +35,20 @@ enum ImageRepeat {
class
DecorationImage
{
/// Creates an image to show in a [BoxDecoration].
///
/// The [image] argument must not be null.
/// The [image], [alignment], [repeat], and [matchTextDirection] arguments
/// must not be null.
const
DecorationImage
({
@required
this
.
image
,
this
.
colorFilter
,
this
.
fit
,
this
.
alignment
,
this
.
alignment
:
FractionalOffset
.
center
,
this
.
centerSlice
,
this
.
repeat
:
ImageRepeat
.
noRepeat
,
})
:
assert
(
image
!=
null
);
this
.
matchTextDirection
:
false
,
})
:
assert
(
image
!=
null
),
assert
(
alignment
!=
null
),
assert
(
repeat
!=
null
),
assert
(
matchTextDirection
!=
null
);
/// The image to be painted into the decoration.
///
...
...
@@ -64,12 +69,23 @@ class DecorationImage {
/// How to align the image within its bounds.
///
/// An alignment of (0.0, 0.0) aligns the image to the top-left corner of its
/// layout bounds. An alignment of (1.0, 0.5) aligns the image to the middle
/// of the right edge of its layout bounds.
/// The alignment aligns the given position in the image to the given position
/// in the layout bounds. For example, a [FractionalOffset] alignment of (0.0,
/// 0.0) aligns the image to the top-left corner of its layout bounds, while a
/// [FractionalOffset] alignment of (1.0, 1.0) aligns the bottom right of the
/// image with the bottom right corner of its layout bounds. Similarly, an
/// alignment of (0.5, 1.0) aligns the bottom middle of the image with the
/// middle of the bottom edge of its layout bounds.
///
/// To display a subpart of an image, consider using a [CustomPainter] and
/// [Canvas.drawImageRect].
///
/// If the [alignment] is [TextDirection]-dependent (i.e. if it is a
/// [FractionalOffsetDirectional]), then a [TextDirection] must be available
/// when the image is painted.
///
/// Defaults to [FractionalOffset.center].
final
FractionalOffset
alignment
;
final
FractionalOffset
Geometry
alignment
;
/// The center slice for a nine-patch image.
///
...
...
@@ -92,6 +108,15 @@ class DecorationImage {
/// by the image.
final
ImageRepeat
repeat
;
/// Whether to paint the image in the direction of the [TextDirection].
///
/// If this is true, then in [TextDirection.ltr] contexts, the image will be
/// drawn with its origin in the top left (the "normal" painting direction for
/// images); and in [TextDirection.rtl] contexts, the image will be drawn with
/// a scaling factor of -1 in the horizontal direction so that the origin is
/// in the top right.
final
bool
matchTextDirection
;
@override
bool
operator
==(
dynamic
other
)
{
if
(
identical
(
this
,
other
))
...
...
@@ -104,11 +129,12 @@ class DecorationImage {
&&
fit
==
typedOther
.
fit
&&
alignment
==
typedOther
.
alignment
&&
centerSlice
==
typedOther
.
centerSlice
&&
repeat
==
typedOther
.
repeat
;
&&
repeat
==
typedOther
.
repeat
&&
matchTextDirection
==
typedOther
.
matchTextDirection
;
}
@override
int
get
hashCode
=>
hashValues
(
image
,
colorFilter
,
fit
,
alignment
,
centerSlice
,
repeat
);
int
get
hashCode
=>
hashValues
(
image
,
colorFilter
,
fit
,
alignment
,
centerSlice
,
repeat
,
matchTextDirection
);
@override
String
toString
()
{
...
...
@@ -120,33 +146,44 @@ class DecorationImage {
!(
fit
==
BoxFit
.
fill
&&
centerSlice
!=
null
)
&&
!(
fit
==
BoxFit
.
scaleDown
&&
centerSlice
==
null
))
properties
.
add
(
'
$fit
'
);
if
(
alignment
!=
null
)
properties
.
add
(
'
$alignment
'
);
properties
.
add
(
'
$alignment
'
);
if
(
centerSlice
!=
null
)
properties
.
add
(
'centerSlice:
$centerSlice
'
);
if
(
repeat
!=
ImageRepeat
.
noRepeat
)
properties
.
add
(
'
$repeat
'
);
if
(
matchTextDirection
)
properties
.
add
(
'match text direction'
);
return
'
$runtimeType
(
${properties.join(", ")}
)'
;
}
}
/// Paints an image into the given rectangle on the canvas.
///
/// The arguments have the following meanings:
///
/// * `canvas`: The canvas onto which the image will be painted.
///
/// * `rect`: The region of the canvas into which the image will be painted.
/// The image might not fill the entire rectangle (e.g., depending on the
/// `fit`). If `rect` is empty, nothing is painted.
///
/// * `image`: The image to paint onto the canvas.
///
/// * `colorFilter`: If non-null, the color filter to apply when painting the
/// image.
///
/// * `fit`: How the image should be inscribed into `rect`. If null, the
/// default behavior depends on `centerSlice`. If `centerSlice` is also null,
/// the default behavior is [BoxFit.scaleDown]. If `centerSlice` is
/// non-null, the default behavior is [BoxFit.fill]. See [BoxFit] for
/// details.
/// * `repeat`: If the image does not fill `rect`, whether and how the image
/// should be repeated to fill `rect`. By default, the image is not repeated.
/// See [ImageRepeat] for details.
///
/// * `alignment`: How the destination rectangle defined by applying `fit` is
/// aligned within `rect`. For example, if `fit` is [BoxFit.contain] and
/// `alignment` is [FractionalOffset.bottomRight], the image will be as large
/// as possible within `rect` and placed with its bottom right corner at the
/// bottom right corner of `rect`. Defaults to [FractionalOffset.center].
///
/// * `centerSlice`: The image is drawn in nine portions described by splitting
/// the image by drawing two horizontal lines and two vertical lines, where
/// `centerSlice` describes the rectangle formed by the four points where
...
...
@@ -157,11 +194,19 @@ class DecorationImage {
/// remaining five regions are drawn by stretching them to fit such that they
/// exactly cover the destination rectangle while maintaining their relative
/// positions.
/// * `alignment`: How the destination rectangle defined by applying `fit` is
/// aligned within `rect`. For example, if `fit` is [BoxFit.contain] and
/// `alignment` is [FractionalOffset.bottomRight], the image will be as large
/// as possible within `rect` and placed with its bottom right corner at the
/// bottom right corner of `rect`.
///
/// * `repeat`: If the image does not fill `rect`, whether and how the image
/// should be repeated to fill `rect`. By default, the image is not repeated.
/// See [ImageRepeat] for details.
///
/// * `flipHorizontally`: Whether to flip the image horizontally. This is
/// occasionally used with images in right-to-left environments, for images
/// that were designed for left-to-right locales (or vice versa). Be careful,
/// when using this, to not flip images with integral shadows, text, or other
/// effects that will look incorrect when flipped.
///
/// The `canvas`, `rect`, `image`, `alignment`, `repeat`, and `flipHorizontally`
/// arguments must not be null.
///
/// See also:
///
...
...
@@ -174,12 +219,16 @@ void paintImage({
@required
ui
.
Image
image
,
ColorFilter
colorFilter
,
BoxFit
fit
,
FractionalOffset
alignment
,
FractionalOffset
alignment
:
FractionalOffset
.
center
,
Rect
centerSlice
,
ImageRepeat
repeat:
ImageRepeat
.
noRepeat
,
bool
flipHorizontally:
false
,
})
{
assert
(
canvas
!=
null
);
assert
(
image
!=
null
);
assert
(
alignment
!=
null
);
assert
(
repeat
!=
null
);
assert
(
flipHorizontally
!=
null
);
if
(
rect
.
isEmpty
)
return
;
Size
outputSize
=
rect
.
size
;
...
...
@@ -219,16 +268,23 @@ void paintImage({
// to nearest-neighbor.
paint
.
filterQuality
=
FilterQuality
.
low
;
}
final
double
dx
=
(
outputSize
.
width
-
destinationSize
.
width
)
*
(
alignment
?.
dx
??
0.5
);
final
double
dy
=
(
outputSize
.
height
-
destinationSize
.
height
)
*
(
alignment
?.
dy
??
0.5
)
;
final
double
dx
=
(
outputSize
.
width
-
destinationSize
.
width
)
*
(
flipHorizontally
?
1.0
-
alignment
.
dx
:
alignment
.
dx
);
final
double
dy
=
(
outputSize
.
height
-
destinationSize
.
height
)
*
alignment
.
dy
;
final
Offset
destinationPosition
=
rect
.
topLeft
.
translate
(
dx
,
dy
);
final
Rect
destinationRect
=
destinationPosition
&
destinationSize
;
if
(
repeat
!=
ImageRepeat
.
noRepeat
)
{
final
bool
needSave
=
repeat
!=
ImageRepeat
.
noRepeat
||
flipHorizontally
;
if
(
needSave
)
canvas
.
save
();
if
(
repeat
!=
ImageRepeat
.
noRepeat
)
canvas
.
clipRect
(
rect
);
if
(
flipHorizontally
)
{
final
double
dx
=
-(
rect
.
left
+
rect
.
width
/
2.0
);
canvas
.
translate
(-
dx
,
0.0
);
canvas
.
scale
(-
1.0
,
1.0
);
canvas
.
translate
(
dx
,
0.0
);
}
if
(
centerSlice
==
null
)
{
final
Rect
sourceRect
=
(
alignment
??
FractionalOffset
.
center
)
.
inscribe
(
final
Rect
sourceRect
=
alignment
.
inscribe
(
fittedSizes
.
source
,
Offset
.
zero
&
inputSize
);
for
(
Rect
tileRect
in
_generateImageTileRects
(
rect
,
destinationRect
,
repeat
))
...
...
@@ -237,7 +293,7 @@ void paintImage({
for
(
Rect
tileRect
in
_generateImageTileRects
(
rect
,
destinationRect
,
repeat
))
canvas
.
drawImageNine
(
image
,
centerSlice
,
tileRect
,
paint
);
}
if
(
repeat
!=
ImageRepeat
.
noRepeat
)
if
(
needSave
)
canvas
.
restore
();
}
...
...
packages/flutter/lib/src/rendering/image.dart
View file @
e04bf328
...
...
@@ -20,6 +20,10 @@ export 'package:flutter/painting.dart' show
/// various fields on this class in more detail.
class
RenderImage
extends
RenderBox
{
/// Creates a render box that displays an image.
///
/// The [scale], [alignment], [repeat], and [matchTextDirection] arguments
/// must not be null. The [textDirection] argument must not be null if
/// [alignment] will need resolving or if [matchTextDirection] is true.
RenderImage
({
ui
.
Image
image
,
double
width
,
...
...
@@ -28,22 +32,46 @@ class RenderImage extends RenderBox {
Color
color
,
BlendMode
colorBlendMode
,
BoxFit
fit
,
FractionalOffset
alignment
,
FractionalOffset
Geometry
alignment:
FractionalOffset
.
center
,
ImageRepeat
repeat:
ImageRepeat
.
noRepeat
,
Rect
centerSlice
})
:
_image
=
image
,
_width
=
width
,
_height
=
height
,
_scale
=
scale
,
_color
=
color
,
_colorBlendMode
=
colorBlendMode
,
_fit
=
fit
,
_alignment
=
alignment
,
_repeat
=
repeat
,
_centerSlice
=
centerSlice
{
Rect
centerSlice
,
bool
matchTextDirection:
false
,
TextDirection
textDirection
,
})
:
assert
(
scale
!=
null
),
assert
(
repeat
!=
null
),
assert
(
alignment
!=
null
),
assert
(
matchTextDirection
!=
null
),
_image
=
image
,
_width
=
width
,
_height
=
height
,
_scale
=
scale
,
_color
=
color
,
_colorBlendMode
=
colorBlendMode
,
_fit
=
fit
,
_alignment
=
alignment
,
_repeat
=
repeat
,
_centerSlice
=
centerSlice
,
_matchTextDirection
=
matchTextDirection
,
_textDirection
=
textDirection
{
_updateColorFilter
();
}
FractionalOffset
_resolvedAlignment
;
bool
_flipHorizontally
;
void
_resolve
()
{
if
(
_resolvedAlignment
!=
null
)
return
;
_resolvedAlignment
=
alignment
.
resolve
(
textDirection
);
_flipHorizontally
=
matchTextDirection
&&
textDirection
==
TextDirection
.
rtl
;
}
void
_markNeedResolution
()
{
_resolvedAlignment
=
null
;
_flipHorizontally
=
null
;
markNeedsPaint
();
}
/// The image to display.
ui
.
Image
get
image
=>
_image
;
ui
.
Image
_image
;
...
...
@@ -147,19 +175,24 @@ class RenderImage extends RenderBox {
}
/// How to align the image within its bounds.
FractionalOffset
get
alignment
=>
_alignment
;
FractionalOffset
_alignment
;
set
alignment
(
FractionalOffset
value
)
{
///
/// If this is set to a text-direction-dependent value, [textDirection] must
/// not be null.
FractionalOffsetGeometry
get
alignment
=>
_alignment
;
FractionalOffsetGeometry
_alignment
;
set
alignment
(
FractionalOffsetGeometry
value
)
{
assert
(
value
!=
null
);
if
(
value
==
_alignment
)
return
;
_alignment
=
value
;
markNeedsPaint
();
_markNeedResolution
();
}
/// How to repeat this image if it doesn't fill its layout bounds.
ImageRepeat
get
repeat
=>
_repeat
;
ImageRepeat
_repeat
;
set
repeat
(
ImageRepeat
value
)
{
assert
(
value
!=
null
);
if
(
value
==
_repeat
)
return
;
_repeat
=
value
;
...
...
@@ -182,6 +215,44 @@ class RenderImage extends RenderBox {
markNeedsPaint
();
}
/// Whether to paint the image in the direction of the [TextDirection].
///
/// If this is true, then in [TextDirection.ltr] contexts, the image will be
/// drawn with its origin in the top left (the "normal" painting direction for
/// images); and in [TextDirection.rtl] contexts, the image will be drawn with
/// a scaling factor of -1 in the horizontal direction so that the origin is
/// in the top right.
///
/// This is occasionally used with images in right-to-left environments, for
/// images that were designed for left-to-right locales. Be careful, when
/// using this, to not flip images with integral shadows, text, or other
/// effects that will look incorrect when flipped.
///
/// If this is set to true, [textDirection] must not be null.
bool
get
matchTextDirection
=>
_matchTextDirection
;
bool
_matchTextDirection
;
set
matchTextDirection
(
bool
value
)
{
assert
(
value
!=
null
);
if
(
value
==
_matchTextDirection
)
return
;
_matchTextDirection
=
value
;
_markNeedResolution
();
}
/// The text direction with which to resolve [alignment].
///
/// This may be changed to null, but only after the [alignment] and
/// [matchTextDirection] properties have been changed to values that do not
/// depend on the direction.
TextDirection
get
textDirection
=>
_textDirection
;
TextDirection
_textDirection
;
set
textDirection
(
TextDirection
value
)
{
if
(
_textDirection
==
value
)
return
;
_textDirection
=
value
;
_markNeedResolution
();
}
/// Find a size for the render image within the given constraints.
///
/// - The dimensions of the RenderImage must fit within the constraints.
...
...
@@ -246,15 +317,19 @@ class RenderImage extends RenderBox {
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
_image
==
null
)
return
;
_resolve
();
assert
(
_resolvedAlignment
!=
null
);
assert
(
_flipHorizontally
!=
null
);
paintImage
(
canvas:
context
.
canvas
,
rect:
offset
&
size
,
image:
_image
,
colorFilter:
_colorFilter
,
fit:
_fit
,
alignment:
_
a
lignment
,
alignment:
_
resolvedA
lignment
,
centerSlice:
_centerSlice
,
repeat:
_repeat
repeat:
_repeat
,
flipHorizontally:
_flipHorizontally
,
);
}
...
...
@@ -271,5 +346,7 @@ class RenderImage extends RenderBox {
description
.
add
(
new
DiagnosticsProperty
<
FractionalOffset
>(
'alignment'
,
alignment
,
defaultValue:
null
));
description
.
add
(
new
EnumProperty
<
ImageRepeat
>(
'repeat'
,
repeat
,
defaultValue:
ImageRepeat
.
noRepeat
));
description
.
add
(
new
DiagnosticsProperty
<
Rect
>(
'centerSlice'
,
centerSlice
,
defaultValue:
null
));
description
.
add
(
new
FlagProperty
(
'matchTextDirection'
,
value:
matchTextDirection
,
ifTrue:
'match text direction'
));
description
.
add
(
new
EnumProperty
<
TextDirection
>(
'textDirection'
,
textDirection
,
defaultValue:
null
));
}
}
packages/flutter/lib/src/rendering/object.dart
View file @
e04bf328
...
...
@@ -17,7 +17,7 @@ import 'layer.dart';
import
'node.dart'
;
import
'semantics.dart'
;
export
'package:flutter/foundation.dart'
show
FlutterError
,
InformationCollector
,
DiagnosticsNode
,
DiagnosticsProperty
,
StringProperty
,
DoubleProperty
,
EnumProperty
,
IntProperty
,
DiagnosticPropertiesBuilder
;
export
'package:flutter/foundation.dart'
show
FlutterError
,
InformationCollector
,
DiagnosticsNode
,
DiagnosticsProperty
,
StringProperty
,
DoubleProperty
,
EnumProperty
,
FlagProperty
,
IntProperty
,
DiagnosticPropertiesBuilder
;
export
'package:flutter/gestures.dart'
show
HitTestEntry
,
HitTestResult
;
export
'package:flutter/painting.dart'
;
...
...
packages/flutter/lib/src/rendering/shifted_box.dart
View file @
e04bf328
...
...
@@ -98,20 +98,20 @@ class RenderPadding extends RenderShiftedBox {
assert
(
padding
.
isNonNegative
),
_textDirection
=
textDirection
,
_padding
=
padding
,
super
(
child
)
{
_applyUpdate
();
}
super
(
child
);
// The resolved absolute insets.
EdgeInsets
_resolvedPadding
;
void
_applyUpdate
()
{
final
EdgeInsets
resolvedPadding
=
padding
.
resolve
(
textDirection
);
assert
(
resolvedPadding
.
isNonNegative
);
if
(
_resolvedPadding
!=
resolvedPadding
)
{
_resolvedPadding
=
resolvedPadding
;
markNeedsLayout
();
}
void
_resolve
()
{
if
(
_resolvedPadding
!=
null
)
return
;
_resolvedPadding
=
padding
.
resolve
(
textDirection
);
assert
(
_resolvedPadding
.
isNonNegative
);
}
void
_markNeedResolution
()
{
_resolvedPadding
=
null
;
markNeedsLayout
();
}
/// The amount to pad the child in each dimension.
...
...
@@ -126,21 +126,25 @@ class RenderPadding extends RenderShiftedBox {
if
(
_padding
==
value
)
return
;
_padding
=
value
;
_
applyUpdate
();
_
markNeedResolution
();
}
/// The text direction with which to resolve [padding].
///
/// This may be changed to null, but only after the [padding] has been changed
/// to a value that does not depend on the direction.
TextDirection
get
textDirection
=>
_textDirection
;
TextDirection
_textDirection
;
set
textDirection
(
TextDirection
value
)
{
if
(
_textDirection
==
value
)
return
;
_textDirection
=
value
;
_
applyUpdate
();
_
markNeedResolution
();
}
@override
double
computeMinIntrinsicWidth
(
double
height
)
{
_resolve
();
final
double
totalHorizontalPadding
=
_resolvedPadding
.
left
+
_resolvedPadding
.
right
;
final
double
totalVerticalPadding
=
_resolvedPadding
.
top
+
_resolvedPadding
.
bottom
;
if
(
child
!=
null
)
// next line relies on double.INFINITY absorption
...
...
@@ -150,6 +154,7 @@ class RenderPadding extends RenderShiftedBox {
@override
double
computeMaxIntrinsicWidth
(
double
height
)
{
_resolve
();
final
double
totalHorizontalPadding
=
_resolvedPadding
.
left
+
_resolvedPadding
.
right
;
final
double
totalVerticalPadding
=
_resolvedPadding
.
top
+
_resolvedPadding
.
bottom
;
if
(
child
!=
null
)
// next line relies on double.INFINITY absorption
...
...
@@ -159,6 +164,7 @@ class RenderPadding extends RenderShiftedBox {
@override
double
computeMinIntrinsicHeight
(
double
width
)
{
_resolve
();
final
double
totalHorizontalPadding
=
_resolvedPadding
.
left
+
_resolvedPadding
.
right
;
final
double
totalVerticalPadding
=
_resolvedPadding
.
top
+
_resolvedPadding
.
bottom
;
if
(
child
!=
null
)
// next line relies on double.INFINITY absorption
...
...
@@ -168,6 +174,7 @@ class RenderPadding extends RenderShiftedBox {
@override
double
computeMaxIntrinsicHeight
(
double
width
)
{
_resolve
();
final
double
totalHorizontalPadding
=
_resolvedPadding
.
left
+
_resolvedPadding
.
right
;
final
double
totalVerticalPadding
=
_resolvedPadding
.
top
+
_resolvedPadding
.
bottom
;
if
(
child
!=
null
)
// next line relies on double.INFINITY absorption
...
...
@@ -177,6 +184,7 @@ class RenderPadding extends RenderShiftedBox {
@override
void
performLayout
()
{
_resolve
();
assert
(
_resolvedPadding
!=
null
);
if
(
child
==
null
)
{
size
=
constraints
.
constrain
(
new
Size
(
...
...
@@ -226,19 +234,19 @@ abstract class RenderAligningShiftedBox extends RenderShiftedBox {
})
:
assert
(
alignment
!=
null
),
_alignment
=
alignment
,
_textDirection
=
textDirection
,
super
(
child
)
{
_applyUpdate
();
}
super
(
child
);
// The resolved absolute alignment.
FractionalOffset
_resolvedAlignment
;
void
_applyUpdate
()
{
final
FractionalOffset
resolvedAlignment
=
alignment
.
resolve
(
textDirection
);
if
(
_resolvedAlignment
!=
resolvedAlignment
)
{
_resolvedAlignment
=
resolvedAlignment
;
markNeedsLayout
();
}
void
_resolve
()
{
if
(
_resolvedAlignment
!=
null
)
return
;
_resolvedAlignment
=
alignment
.
resolve
(
textDirection
);
}
void
_markNeedResolution
()
{
_resolvedAlignment
=
null
;
markNeedsLayout
();
}
/// How to align the child.
...
...
@@ -251,7 +259,7 @@ abstract class RenderAligningShiftedBox extends RenderShiftedBox {
/// For example, a value of 0.5 means that the center of the child is aligned
/// with the center of the parent.
///
/// If this is set to a
n
[FractionalOffsetDirectional] object, then
/// If this is set to a [FractionalOffsetDirectional] object, then
/// [textDirection] must not be null.
FractionalOffsetGeometry
get
alignment
=>
_alignment
;
FractionalOffsetGeometry
_alignment
;
...
...
@@ -263,17 +271,20 @@ abstract class RenderAligningShiftedBox extends RenderShiftedBox {
if
(
_alignment
==
value
)
return
;
_alignment
=
value
;
_
applyUpdate
();
_
markNeedResolution
();
}
/// The text direction with which to resolve [alignment].
///
/// This may be changed to null, but only after [alignment] has been changed
/// to a value that does not depend on the direction.
TextDirection
get
textDirection
=>
_textDirection
;
TextDirection
_textDirection
;
set
textDirection
(
TextDirection
value
)
{
if
(
_textDirection
==
value
)
return
;
_textDirection
=
value
;
_
applyUpdate
();
_
markNeedResolution
();
}
/// Apply the current [alignment] to the [child].
...
...
@@ -285,10 +296,12 @@ abstract class RenderAligningShiftedBox extends RenderShiftedBox {
/// This method must be called after the child has been laid out and
/// this object's own size has been set.
void
alignChild
()
{
_resolve
();
assert
(
child
!=
null
);
assert
(!
child
.
debugNeedsLayout
);
assert
(
child
.
hasSize
);
assert
(
hasSize
);
assert
(
_resolvedAlignment
!=
null
);
final
BoxParentData
childParentData
=
child
.
parentData
;
childParentData
.
offset
=
_resolvedAlignment
.
alongOffset
(
size
-
child
.
size
);
}
...
...
packages/flutter/lib/src/rendering/sliver_padding.dart
View file @
e04bf328
...
...
@@ -37,22 +37,26 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
_padding
=
padding
,
_textDirection
=
textDirection
{
this
.
child
=
child
;
_applyUpdate
();
}
// The resolved absolute insets.
EdgeInsets
_resolvedPadding
;
void
_applyUpdate
()
{
final
EdgeInsets
resolvedPadding
=
padding
.
resolve
(
textDirection
);
assert
(
resolvedPadding
.
isNonNegative
);
if
(
_resolvedPadding
!=
resolvedPadding
)
{
_resolvedPadding
=
resolvedPadding
;
markNeedsLayout
();
}
void
_resolve
()
{
if
(
_resolvedPadding
!=
null
)
return
;
_resolvedPadding
=
padding
.
resolve
(
textDirection
);
assert
(
_resolvedPadding
.
isNonNegative
);
}
void
_markNeedResolution
()
{
_resolvedPadding
=
null
;
markNeedsLayout
();
}
/// The amount to pad the child in each dimension.
///
/// If this is set to an [EdgeInsetsDirectional] object, then [textDirection]
/// must not be null.
EdgeInsetsGeometry
get
padding
=>
_padding
;
EdgeInsetsGeometry
_padding
;
set
padding
(
EdgeInsetsGeometry
value
)
{
...
...
@@ -61,17 +65,20 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
if
(
_padding
==
value
)
return
;
_padding
=
value
;
_
applyUpdate
();
_
markNeedResolution
();
}
/// The text direction with which to resolve [padding].
///
/// This may be changed to null, but only after the [padding] has been changed
/// to a value that does not depend on the direction.
TextDirection
get
textDirection
=>
_textDirection
;
TextDirection
_textDirection
;
set
textDirection
(
TextDirection
value
)
{
if
(
_textDirection
==
value
)
return
;
_textDirection
=
value
;
_
applyUpdate
();
_
markNeedResolution
();
}
/// The padding in the scroll direction on the side nearest the 0.0 scroll direction.
...
...
@@ -82,6 +89,7 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
assert
(
constraints
!=
null
);
assert
(
constraints
.
axisDirection
!=
null
);
assert
(
constraints
.
growthDirection
!=
null
);
assert
(
_resolvedPadding
!=
null
);
switch
(
applyGrowthDirectionToAxisDirection
(
constraints
.
axisDirection
,
constraints
.
growthDirection
))
{
case
AxisDirection
.
up
:
return
_resolvedPadding
.
bottom
;
...
...
@@ -103,6 +111,7 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
assert
(
constraints
!=
null
);
assert
(
constraints
.
axisDirection
!=
null
);
assert
(
constraints
.
growthDirection
!=
null
);
assert
(
_resolvedPadding
!=
null
);
switch
(
applyGrowthDirectionToAxisDirection
(
constraints
.
axisDirection
,
constraints
.
growthDirection
))
{
case
AxisDirection
.
up
:
return
_resolvedPadding
.
top
;
...
...
@@ -125,6 +134,7 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
double
get
mainAxisPadding
{
assert
(
constraints
!=
null
);
assert
(
constraints
.
axis
!=
null
);
assert
(
_resolvedPadding
!=
null
);
return
_resolvedPadding
.
along
(
constraints
.
axis
);
}
...
...
@@ -137,6 +147,7 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
double
get
crossAxisPadding
{
assert
(
constraints
!=
null
);
assert
(
constraints
.
axis
!=
null
);
assert
(
_resolvedPadding
!=
null
);
switch
(
constraints
.
axis
)
{
case
Axis
.
horizontal
:
return
_resolvedPadding
.
vertical
;
...
...
@@ -154,6 +165,8 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
@override
void
performLayout
()
{
_resolve
();
assert
(
_resolvedPadding
!=
null
);
final
double
beforePadding
=
this
.
beforePadding
;
final
double
afterPadding
=
this
.
afterPadding
;
final
double
mainAxisPadding
=
this
.
mainAxisPadding
;
...
...
@@ -248,6 +261,7 @@ class RenderSliverPadding extends RenderSliver with RenderObjectWithChildMixin<R
assert
(
constraints
!=
null
);
assert
(
constraints
.
axisDirection
!=
null
);
assert
(
constraints
.
growthDirection
!=
null
);
assert
(
_resolvedPadding
!=
null
);
switch
(
applyGrowthDirectionToAxisDirection
(
constraints
.
axisDirection
,
constraints
.
growthDirection
))
{
case
AxisDirection
.
up
:
case
AxisDirection
.
down
:
...
...
packages/flutter/lib/src/rendering/stack.dart
View file @
e04bf328
...
...
@@ -309,7 +309,6 @@ class RenderStack extends RenderBox
_fit
=
fit
,
_overflow
=
overflow
{
addAll
(
children
);
_applyUpdate
();
}
bool
_hasVisualOverflow
=
false
;
...
...
@@ -320,15 +319,17 @@ class RenderStack extends RenderBox
child
.
parentData
=
new
StackParentData
();
}
// The resolved absolute insets.
FractionalOffset
_resolvedAlignment
;
void
_applyUpdate
()
{
final
FractionalOffset
resolvedAlignment
=
alignment
.
resolve
(
textDirection
);
if
(
_resolvedAlignment
!=
resolvedAlignment
)
{
_resolvedAlignment
=
resolvedAlignment
;
markNeedsLayout
();
}
void
_resolve
()
{
if
(
_resolvedAlignment
!=
null
)
return
;
_resolvedAlignment
=
alignment
.
resolve
(
textDirection
);
}
void
_markNeedResolution
()
{
_resolvedAlignment
=
null
;
markNeedsLayout
();
}
/// How to align the non-positioned children in the stack.
...
...
@@ -337,24 +338,30 @@ class RenderStack extends RenderBox
/// the points determined by [alignment] are co-located. For example, if the
/// [alignment] is [FractionalOffset.topLeft], then the top left corner of
/// each non-positioned child will be located at the same global coordinate.
///
/// If this is set to a [FractionalOffsetDirectional] object, then
/// [textDirection] must not be null.
FractionalOffsetGeometry
get
alignment
=>
_alignment
;
FractionalOffsetGeometry
_alignment
;
set
alignment
(
FractionalOffsetGeometry
value
)
{
assert
(
value
!=
null
);
if
(
_alignment
!=
value
)
{
_alignment
=
value
;
_applyUpdate
()
;
}
if
(
_alignment
==
value
)
return
;
_alignment
=
value
;
_markNeedResolution
();
}
/// The text direction with which to resolve [alignment].
///
/// This may be changed to null, but only after the [alignment] has been changed
/// to a value that does not depend on the direction.
TextDirection
get
textDirection
=>
_textDirection
;
TextDirection
_textDirection
;
set
textDirection
(
TextDirection
value
)
{
if
(
_textDirection
!=
value
)
{
_textDirection
=
value
;
_applyUpdate
()
;
}
if
(
_textDirection
==
value
)
return
;
_textDirection
=
value
;
_markNeedResolution
();
}
/// How to size the non-positioned children in the stack.
...
...
@@ -426,6 +433,8 @@ class RenderStack extends RenderBox
@override
void
performLayout
()
{
_resolve
();
assert
(
_resolvedAlignment
!=
null
);
_hasVisualOverflow
=
false
;
bool
hasNonPositionedChildren
=
false
;
...
...
packages/flutter/lib/src/services/image_provider.dart
View file @
e04bf328
...
...
@@ -6,7 +6,7 @@ import 'dart:async';
import
'dart:io'
show
File
;
import
'dart:typed_data'
;
import
'dart:ui'
as
ui
show
Image
;
import
'dart:ui'
show
Size
,
Locale
,
hashValues
;
import
'dart:ui'
show
Size
,
Locale
,
TextDirection
,
hashValues
;
import
'package:flutter/foundation.dart'
;
import
'package:http/http.dart'
as
http
;
...
...
@@ -36,8 +36,9 @@ class ImageConfiguration {
this
.
bundle
,
this
.
devicePixelRatio
,
this
.
locale
,
this
.
textDirection
,
this
.
size
,
this
.
platform
this
.
platform
,
});
/// Creates an object holding the configuration information for an [ImageProvider].
...
...
@@ -48,15 +49,17 @@ class ImageConfiguration {
AssetBundle
bundle
,
double
devicePixelRatio
,
Locale
locale
,
TextDirection
textDirection
,
Size
size
,
String
platform
String
platform
,
})
{
return
new
ImageConfiguration
(
bundle:
bundle
??
this
.
bundle
,
devicePixelRatio:
devicePixelRatio
??
this
.
devicePixelRatio
,
locale:
locale
??
this
.
locale
,
textDirection:
textDirection
??
this
.
textDirection
,
size:
size
??
this
.
size
,
platform:
platform
??
this
.
platform
platform:
platform
??
this
.
platform
,
);
}
...
...
@@ -70,6 +73,9 @@ class ImageConfiguration {
/// The language and region for which to select the image.
final
Locale
locale
;
/// The reading direction of the language for which to select the image.
final
TextDirection
textDirection
;
/// The size at which the image will be rendered.
final
Size
size
;
...
...
@@ -92,6 +98,7 @@ class ImageConfiguration {
return
typedOther
.
bundle
==
bundle
&&
typedOther
.
devicePixelRatio
==
devicePixelRatio
&&
typedOther
.
locale
==
locale
&&
typedOther
.
textDirection
==
textDirection
&&
typedOther
.
size
==
size
&&
typedOther
.
platform
==
platform
;
}
...
...
@@ -122,6 +129,12 @@ class ImageConfiguration {
result
.
write
(
'locale:
$locale
'
);
hasArguments
=
true
;
}
if
(
textDirection
!=
null
)
{
if
(
hasArguments
)
result
.
write
(
', '
);
result
.
write
(
'textDirection:
$textDirection
'
);
hasArguments
=
true
;
}
if
(
size
!=
null
)
{
if
(
hasArguments
)
result
.
write
(
', '
);
...
...
packages/flutter/lib/src/services/image_resolution.dart
View file @
e04bf328
...
...
@@ -224,8 +224,9 @@ class AssetImage extends AssetBundleImageProvider {
final
SplayTreeMap
<
double
,
String
>
mapping
=
new
SplayTreeMap
<
double
,
String
>();
for
(
String
candidate
in
candidates
)
mapping
[
_parseScale
(
candidate
)]
=
candidate
;
// TODO(ianh): implement support for config.locale, config.size, config.platform
// (then document this over in the Image.asset docs)
// TODO(ianh): implement support for config.locale, config.textDirection,
// config.size, config.platform (then document this over in the Image.asset
// docs)
return
_findNearest
(
mapping
,
config
.
devicePixelRatio
);
}
...
...
packages/flutter/lib/src/widgets/animated_list.dart
View file @
e04bf328
...
...
@@ -161,8 +161,8 @@ class AnimatedList extends StatefulWidget {
/// AnimatedListState animatedList = AnimatedList.of(context);
/// ```
static
AnimatedListState
of
(
BuildContext
context
,
{
bool
nullOk:
false
})
{
assert
(
nullOk
!=
null
);
assert
(
context
!=
null
);
assert
(
nullOk
!=
null
);
final
AnimatedListState
result
=
context
.
ancestorStateOfType
(
const
TypeMatcher
<
AnimatedListState
>());
if
(
nullOk
||
result
!=
null
)
return
result
;
...
...
packages/flutter/lib/src/widgets/basic.dart
View file @
e04bf328
...
...
@@ -3826,7 +3826,8 @@ class RichText extends LeafRenderObjectWidget {
class
RawImage
extends
LeafRenderObjectWidget
{
/// Creates a widget that displays an image.
///
/// The [scale] and [repeat] arguments must not be null.
/// The [scale], [alignment], [repeat], and [matchTextDirection] arguments must
/// not be null.
const
RawImage
({
Key
key
,
this
.
image
,
...
...
@@ -3836,11 +3837,14 @@ class RawImage extends LeafRenderObjectWidget {
this
.
color
,
this
.
colorBlendMode
,
this
.
fit
,
this
.
alignment
,
this
.
alignment
:
FractionalOffset
.
center
,
this
.
repeat
:
ImageRepeat
.
noRepeat
,
this
.
centerSlice
this
.
centerSlice
,
this
.
matchTextDirection
:
false
,
})
:
assert
(
scale
!=
null
),
assert
(
alignment
!=
null
),
assert
(
repeat
!=
null
),
assert
(
matchTextDirection
!=
null
),
super
(
key:
key
);
/// The image to display.
...
...
@@ -3884,10 +3888,23 @@ class RawImage extends LeafRenderObjectWidget {
/// How to align the image within its bounds.
///
/// An alignment of (0.0, 0.0) aligns the image to the top-left corner of its
/// layout bounds. An alignment of (1.0, 0.5) aligns the image to the middle
/// of the right edge of its layout bounds.
final
FractionalOffset
alignment
;
/// The alignment aligns the given position in the image to the given position
/// in the layout bounds. For example, a [FractionalOffset] alignment of (0.0,
/// 0.0) aligns the image to the top-left corner of its layout bounds, while a
/// [FractionalOffset] alignment of (1.0, 1.0) aligns the bottom right of the
/// image with the bottom right corner of its layout bounds. Similarly, an
/// alignment of (0.5, 1.0) aligns the bottom middle of the image with the
/// middle of the bottom edge of its layout bounds.
///
/// To display a subpart of an image, consider using a [CustomPainter] and
/// [Canvas.drawImageRect].
///
/// If the [alignment] is [TextDirection]-dependent (i.e. if it is a
/// [FractionalOffsetDirectional]), then an ambient [Directionality] widget
/// must be in scope.
///
/// Defaults to [FractionalOffset.center].
final
FractionalOffsetGeometry
alignment
;
/// How to paint any portions of the layout bounds not covered by the image.
final
ImageRepeat
repeat
;
...
...
@@ -3901,8 +3918,26 @@ class RawImage extends LeafRenderObjectWidget {
/// the center slice will be stretched only vertically.
final
Rect
centerSlice
;
/// Whether to paint the image in the direction of the [TextDirection].
///
/// If this is true, then in [TextDirection.ltr] contexts, the image will be
/// drawn with its origin in the top left (the "normal" painting direction for
/// images); and in [TextDirection.rtl] contexts, the image will be drawn with
/// a scaling factor of -1 in the horizontal direction so that the origin is
/// in the top right.
///
/// This is occasionally used with images in right-to-left environments, for
/// images that were designed for left-to-right locales. Be careful, when
/// using this, to not flip images with integral shadows, text, or other
/// effects that will look incorrect when flipped.
///
/// If this is true, there must be an ambient [Directionality] widget in
/// scope.
final
bool
matchTextDirection
;
@override
RenderImage
createRenderObject
(
BuildContext
context
)
{
assert
((!
matchTextDirection
&&
alignment
is
FractionalOffset
)
||
debugCheckHasDirectionality
(
context
));
return
new
RenderImage
(
image:
image
,
width:
width
,
...
...
@@ -3913,7 +3948,9 @@ class RawImage extends LeafRenderObjectWidget {
fit:
fit
,
alignment:
alignment
,
repeat:
repeat
,
centerSlice:
centerSlice
centerSlice:
centerSlice
,
matchTextDirection:
matchTextDirection
,
textDirection:
matchTextDirection
||
alignment
is
!
FractionalOffset
?
Directionality
.
of
(
context
)
:
null
,
);
}
...
...
@@ -3929,7 +3966,9 @@ class RawImage extends LeafRenderObjectWidget {
..
alignment
=
alignment
..
fit
=
fit
..
repeat
=
repeat
..
centerSlice
=
centerSlice
;
..
centerSlice
=
centerSlice
..
matchTextDirection
=
matchTextDirection
..
textDirection
=
matchTextDirection
||
alignment
is
!
FractionalOffset
?
Directionality
.
of
(
context
)
:
null
;
}
@override
...
...
@@ -3942,9 +3981,10 @@ class RawImage extends LeafRenderObjectWidget {
description
.
add
(
new
DiagnosticsProperty
<
Color
>(
'color'
,
color
,
defaultValue:
null
));
description
.
add
(
new
EnumProperty
<
BlendMode
>(
'colorBlendMode'
,
colorBlendMode
,
defaultValue:
null
));
description
.
add
(
new
EnumProperty
<
BoxFit
>(
'fit'
,
fit
,
defaultValue:
null
));
description
.
add
(
new
DiagnosticsProperty
<
FractionalOffset
>(
'alignment'
,
alignment
,
defaultValue:
null
));
description
.
add
(
new
DiagnosticsProperty
<
FractionalOffset
Geometry
>(
'alignment'
,
alignment
,
defaultValue:
null
));
description
.
add
(
new
EnumProperty
<
ImageRepeat
>(
'repeat'
,
repeat
,
defaultValue:
ImageRepeat
.
noRepeat
));
description
.
add
(
new
DiagnosticsProperty
<
Rect
>(
'centerSlice'
,
centerSlice
,
defaultValue:
null
));
description
.
add
(
new
FlagProperty
(
'matchTextDirection'
,
value:
matchTextDirection
,
ifTrue:
'match text direction'
));
}
}
...
...
packages/flutter/lib/src/widgets/container.dart
View file @
e04bf328
...
...
@@ -71,7 +71,7 @@ class DecoratedBox extends SingleChildRenderObjectWidget {
return
new
RenderDecoratedBox
(
decoration:
decoration
,
position:
position
,
configuration:
createLocalImageConfiguration
(
context
)
configuration:
createLocalImageConfiguration
(
context
)
,
);
}
...
...
packages/flutter/lib/src/widgets/fade_in_image.dart
View file @
e04bf328
...
...
@@ -60,7 +60,8 @@ class FadeInImage extends StatefulWidget {
/// then cross-fades to display the [image].
///
/// The [placeholder], [image], [fadeOutDuration], [fadeOutCurve],
/// [fadeInDuration], [fadeInCurve] and [repeat] arguments must not be null.
/// [fadeInDuration], [fadeInCurve], [alignment], [repeat], and
/// [matchTextDirection] arguments must not be null.
const
FadeInImage
({
Key
key
,
@required
this
.
placeholder
,
...
...
@@ -72,15 +73,18 @@ class FadeInImage extends StatefulWidget {
this
.
width
,
this
.
height
,
this
.
fit
,
this
.
alignment
,
this
.
alignment
:
FractionalOffset
.
center
,
this
.
repeat
:
ImageRepeat
.
noRepeat
,
this
.
matchTextDirection
:
false
,
})
:
assert
(
placeholder
!=
null
),
assert
(
image
!=
null
),
assert
(
fadeOutDuration
!=
null
),
assert
(
fadeOutCurve
!=
null
),
assert
(
fadeInDuration
!=
null
),
assert
(
fadeInCurve
!=
null
),
assert
(
alignment
!=
null
),
assert
(
repeat
!=
null
),
assert
(
matchTextDirection
!=
null
),
super
(
key:
key
);
/// Creates a widget that uses a placeholder image stored in memory while
...
...
@@ -94,8 +98,9 @@ class FadeInImage extends StatefulWidget {
/// [ImageProvider]s (see also [ImageInfo.scale]).
///
/// The [placeholder], [image], [placeholderScale], [imageScale],
/// [fadeOutDuration], [fadeOutCurve], [fadeInDuration], [fadeInCurve] and
/// [repeat] arguments must not be null.
/// [fadeOutDuration], [fadeOutCurve], [fadeInDuration], [fadeInCurve],
/// [alignment], [repeat], and [matchTextDirection] arguments must not be
/// null.
///
/// See also:
///
...
...
@@ -116,8 +121,9 @@ class FadeInImage extends StatefulWidget {
this
.
width
,
this
.
height
,
this
.
fit
,
this
.
alignment
,
this
.
alignment
:
FractionalOffset
.
center
,
this
.
repeat
:
ImageRepeat
.
noRepeat
,
this
.
matchTextDirection
:
false
,
})
:
assert
(
placeholder
!=
null
),
assert
(
image
!=
null
),
assert
(
placeholderScale
!=
null
),
...
...
@@ -126,7 +132,9 @@ class FadeInImage extends StatefulWidget {
assert
(
fadeOutCurve
!=
null
),
assert
(
fadeInDuration
!=
null
),
assert
(
fadeInCurve
!=
null
),
assert
(
alignment
!=
null
),
assert
(
repeat
!=
null
),
assert
(
matchTextDirection
!=
null
),
placeholder
=
new
MemoryImage
(
placeholder
,
scale:
placeholderScale
),
image
=
new
NetworkImage
(
image
,
scale:
imageScale
),
super
(
key:
key
);
...
...
@@ -146,8 +154,8 @@ class FadeInImage extends StatefulWidget {
/// exact asset specified will be used.
///
/// The [placeholder], [image], [imageScale], [fadeOutDuration],
/// [fadeOutCurve], [fadeInDuration], [fadeInCurve]
and [repeat] arguments
/// must not be null.
/// [fadeOutCurve], [fadeInDuration], [fadeInCurve]
, [alignment], [repeat],
///
and [matchTextDirection] arguments
must not be null.
///
/// See also:
///
...
...
@@ -169,8 +177,9 @@ class FadeInImage extends StatefulWidget {
this
.
width
,
this
.
height
,
this
.
fit
,
this
.
alignment
,
this
.
alignment
:
FractionalOffset
.
center
,
this
.
repeat
:
ImageRepeat
.
noRepeat
,
this
.
matchTextDirection
:
false
,
})
:
assert
(
placeholder
!=
null
),
assert
(
image
!=
null
),
placeholder
=
placeholderScale
!=
null
...
...
@@ -181,7 +190,9 @@ class FadeInImage extends StatefulWidget {
assert
(
fadeOutCurve
!=
null
),
assert
(
fadeInDuration
!=
null
),
assert
(
fadeInCurve
!=
null
),
assert
(
alignment
!=
null
),
assert
(
repeat
!=
null
),
assert
(
matchTextDirection
!=
null
),
image
=
new
NetworkImage
(
image
,
scale:
imageScale
),
super
(
key:
key
);
...
...
@@ -227,14 +238,41 @@ class FadeInImage extends StatefulWidget {
/// How to align the image within its bounds.
///
/// An alignment of (0.0, 0.0) aligns the image to the top-left corner of its
/// layout bounds. An alignment of (1.0, 0.5) aligns the image to the middle
/// of the right edge of its layout bounds.
final
FractionalOffset
alignment
;
/// The alignment aligns the given position in the image to the given position
/// in the layout bounds. For example, a [FractionalOffset] alignment of (0.0,
/// 0.0) aligns the image to the top-left corner of its layout bounds, while a
/// [FractionalOffset] alignment of (1.0, 1.0) aligns the bottom right of the
/// image with the bottom right corner of its layout bounds. Similarly, an
/// alignment of (0.5, 1.0) aligns the bottom middle of the image with the
/// middle of the bottom edge of its layout bounds.
///
/// If the [alignment] is [TextDirection]-dependent (i.e. if it is a
/// [FractionalOffsetDirectional]), then an ambient [Directionality] widget
/// must be in scope.
///
/// Defaults to [FractionalOffset.center].
final
FractionalOffsetGeometry
alignment
;
/// How to paint any portions of the layout bounds not covered by the image.
final
ImageRepeat
repeat
;
/// Whether to paint the image in the direction of the [TextDirection].
///
/// If this is true, then in [TextDirection.ltr] contexts, the image will be
/// drawn with its origin in the top left (the "normal" painting direction for
/// images); and in [TextDirection.rtl] contexts, the image will be drawn with
/// a scaling factor of -1 in the horizontal direction so that the origin is
/// in the top right.
///
/// This is occasionally used with images in right-to-left environments, for
/// images that were designed for left-to-right locales. Be careful, when
/// using this, to not flip images with integral shadows, text, or other
/// effects that will look incorrect when flipped.
///
/// If this is true, there must be an ambient [Directionality] widget in
/// scope.
final
bool
matchTextDirection
;
@override
State
<
StatefulWidget
>
createState
()
=>
new
_FadeInImageState
();
}
...
...
@@ -282,8 +320,8 @@ class _ImageProviderResolver {
void
resolve
(
ImageProvider
provider
)
{
final
ImageStream
oldImageStream
=
_imageStream
;
_imageStream
=
provider
.
resolve
(
createLocalImageConfiguration
(
state
.
context
,
size:
widget
.
width
!=
null
&&
widget
.
height
!=
null
?
new
Size
(
widget
.
width
,
widget
.
height
)
:
null
state
.
context
,
size:
widget
.
width
!=
null
&&
widget
.
height
!=
null
?
new
Size
(
widget
.
width
,
widget
.
height
)
:
null
));
assert
(
_imageStream
!=
null
);
...
...
@@ -456,6 +494,7 @@ class _FadeInImageState extends State<FadeInImage> with TickerProviderStateMixin
fit:
widget
.
fit
,
alignment:
widget
.
alignment
,
repeat:
widget
.
repeat
,
matchTextDirection:
widget
.
matchTextDirection
,
);
}
...
...
packages/flutter/lib/src/widgets/image.dart
View file @
e04bf328
...
...
@@ -11,6 +11,7 @@ import 'package:flutter/services.dart';
import
'basic.dart'
;
import
'framework.dart'
;
import
'localizations.dart'
;
import
'media_query.dart'
;
export
'package:flutter/services.dart'
show
...
...
@@ -39,7 +40,8 @@ ImageConfiguration createLocalImageConfiguration(BuildContext context, { Size si
return
new
ImageConfiguration
(
bundle:
DefaultAssetBundle
.
of
(
context
),
devicePixelRatio:
MediaQuery
.
of
(
context
,
nullOk:
true
)?.
devicePixelRatio
??
1.0
,
// TODO(ianh): provide the locale
locale:
Localizations
.
localeOf
(
context
,
nullOk:
true
),
textDirection:
Directionality
.
of
(
context
),
size:
size
,
platform:
defaultTargetPlatform
,
);
...
...
@@ -101,7 +103,8 @@ class Image extends StatefulWidget {
/// To show an image from the network or from an asset bundle, consider using
/// [new Image.network] and [new Image.asset] respectively.
///
/// The [image] and [repeat] arguments must not be null.
/// The [image], [alignment], [repeat], and [matchTextDirection] arguments
/// must not be null.
const
Image
({
Key
key
,
@required
this
.
image
,
...
...
@@ -110,12 +113,16 @@ class Image extends StatefulWidget {
this
.
color
,
this
.
colorBlendMode
,
this
.
fit
,
this
.
alignment
,
this
.
alignment
:
FractionalOffset
.
center
,
this
.
repeat
:
ImageRepeat
.
noRepeat
,
this
.
centerSlice
,
this
.
matchTextDirection
:
false
,
this
.
gaplessPlayback
:
false
,
this
.
package
,
})
:
assert
(
image
!=
null
),
assert
(
alignment
!=
null
),
assert
(
repeat
!=
null
),
assert
(
matchTextDirection
!=
null
),
super
(
key:
key
);
/// Creates a widget that displays an [ImageStream] obtained from the network.
...
...
@@ -129,12 +136,16 @@ class Image extends StatefulWidget {
this
.
color
,
this
.
colorBlendMode
,
this
.
fit
,
this
.
alignment
,
this
.
alignment
:
FractionalOffset
.
center
,
this
.
repeat
:
ImageRepeat
.
noRepeat
,
this
.
centerSlice
,
this
.
matchTextDirection
:
false
,
this
.
gaplessPlayback
:
false
,
this
.
package
,
})
:
image
=
new
NetworkImage
(
src
,
scale:
scale
),
assert
(
alignment
!=
null
),
assert
(
repeat
!=
null
),
assert
(
matchTextDirection
!=
null
),
super
(
key:
key
);
/// Creates a widget that displays an [ImageStream] obtained from a [File].
...
...
@@ -151,12 +162,16 @@ class Image extends StatefulWidget {
this
.
color
,
this
.
colorBlendMode
,
this
.
fit
,
this
.
alignment
,
this
.
alignment
:
FractionalOffset
.
center
,
this
.
repeat
:
ImageRepeat
.
noRepeat
,
this
.
centerSlice
,
this
.
matchTextDirection
:
false
,
this
.
gaplessPlayback
:
false
,
this
.
package
,
})
:
image
=
new
FileImage
(
file
,
scale:
scale
),
assert
(
alignment
!=
null
),
assert
(
repeat
!=
null
),
assert
(
matchTextDirection
!=
null
),
super
(
key:
key
);
/// Creates a widget that displays an [ImageStream] obtained from an asset
...
...
@@ -181,8 +196,9 @@ class Image extends StatefulWidget {
// /// size-aware asset resolution will be attempted also, with the given
// /// dimensions interpreted as logical pixels.
// ///
// /// * If the images have platform or locale variants, the current platform
// /// and locale is taken into account during asset resolution as well.
// /// * If the images have platform, locale, or directionality variants, the
// /// current platform, locale, and directionality are taken into account
// /// during asset resolution as well.
///
/// The [name] and [repeat] arguments must not be null.
///
...
...
@@ -277,15 +293,19 @@ class Image extends StatefulWidget {
this
.
color
,
this
.
colorBlendMode
,
this
.
fit
,
this
.
alignment
,
this
.
alignment
:
FractionalOffset
.
center
,
this
.
repeat
:
ImageRepeat
.
noRepeat
,
this
.
centerSlice
,
this
.
matchTextDirection
:
false
,
this
.
gaplessPlayback
:
false
,
this
.
package
,
})
:
image
=
scale
!=
null
?
new
ExactAssetImage
(
name
,
bundle:
bundle
,
scale:
scale
,
package:
package
)
:
new
AssetImage
(
name
,
bundle:
bundle
,
package:
package
),
super
(
key:
key
);
?
new
ExactAssetImage
(
name
,
bundle:
bundle
,
scale:
scale
,
package:
package
)
:
new
AssetImage
(
name
,
bundle:
bundle
,
package:
package
),
assert
(
alignment
!=
null
),
assert
(
repeat
!=
null
),
assert
(
matchTextDirection
!=
null
),
super
(
key:
key
);
/// Creates a widget that displays an [ImageStream] obtained from a [Uint8List].
///
...
...
@@ -298,12 +318,16 @@ class Image extends StatefulWidget {
this
.
color
,
this
.
colorBlendMode
,
this
.
fit
,
this
.
alignment
,
this
.
alignment
:
FractionalOffset
.
center
,
this
.
repeat
:
ImageRepeat
.
noRepeat
,
this
.
centerSlice
,
this
.
matchTextDirection
:
false
,
this
.
gaplessPlayback
:
false
,
this
.
package
,
})
:
image
=
new
MemoryImage
(
bytes
,
scale:
scale
),
assert
(
alignment
!=
null
),
assert
(
repeat
!=
null
),
assert
(
matchTextDirection
!=
null
),
super
(
key:
key
);
/// The image to display.
...
...
@@ -342,10 +366,23 @@ class Image extends StatefulWidget {
/// How to align the image within its bounds.
///
/// An alignment of (0.0, 0.0) aligns the image to the top-left corner of its
/// layout bounds. An alignment of (1.0, 0.5) aligns the image to the middle
/// of the right edge of its layout bounds.
final
FractionalOffset
alignment
;
/// The alignment aligns the given position in the image to the given position
/// in the layout bounds. For example, a [FractionalOffset] alignment of (0.0,
/// 0.0) aligns the image to the top-left corner of its layout bounds, while a
/// [FractionalOffset] alignment of (1.0, 1.0) aligns the bottom right of the
/// image with the bottom right corner of its layout bounds. Similarly, an
/// alignment of (0.5, 1.0) aligns the bottom middle of the image with the
/// middle of the bottom edge of its layout bounds.
///
/// To display a subpart of an image, consider using a [CustomPainter] and
/// [Canvas.drawImageRect].
///
/// If the [alignment] is [TextDirection]-dependent (i.e. if it is a
/// [FractionalOffsetDirectional]), then an ambient [Directionality] widget
/// must be in scope.
///
/// Defaults to [FractionalOffset.center].
final
FractionalOffsetGeometry
alignment
;
/// How to paint any portions of the layout bounds not covered by the image.
final
ImageRepeat
repeat
;
...
...
@@ -359,6 +396,23 @@ class Image extends StatefulWidget {
/// the center slice will be stretched only vertically.
final
Rect
centerSlice
;
/// Whether to paint the image in the direction of the [TextDirection].
///
/// If this is true, then in [TextDirection.ltr] contexts, the image will be
/// drawn with its origin in the top left (the "normal" painting direction for
/// images); and in [TextDirection.rtl] contexts, the image will be drawn with
/// a scaling factor of -1 in the horizontal direction so that the origin is
/// in the top right.
///
/// This is occasionally used with images in right-to-left environments, for
/// images that were designed for left-to-right locales. Be careful, when
/// using this, to not flip images with integral shadows, text, or other
/// effects that will look incorrect when flipped.
///
/// If this is true, there must be an ambient [Directionality] widget in
/// scope.
final
bool
matchTextDirection
;
/// Whether to continue showing the old image (true), or briefly show nothing
/// (false), when the image provider changes.
final
bool
gaplessPlayback
;
...
...
@@ -379,9 +433,10 @@ class Image extends StatefulWidget {
description
.
add
(
new
DiagnosticsProperty
<
Color
>(
'color'
,
color
,
defaultValue:
null
));
description
.
add
(
new
EnumProperty
<
BlendMode
>(
'colorBlendMode'
,
colorBlendMode
,
defaultValue:
null
));
description
.
add
(
new
EnumProperty
<
BoxFit
>(
'fit'
,
fit
,
defaultValue:
null
));
description
.
add
(
new
DiagnosticsProperty
<
FractionalOffset
>(
'alignment'
,
alignment
,
defaultValue:
null
));
description
.
add
(
new
DiagnosticsProperty
<
FractionalOffset
Geometry
>(
'alignment'
,
alignment
,
defaultValue:
null
));
description
.
add
(
new
EnumProperty
<
ImageRepeat
>(
'repeat'
,
repeat
,
defaultValue:
ImageRepeat
.
noRepeat
));
description
.
add
(
new
DiagnosticsProperty
<
Rect
>(
'centerSlice'
,
centerSlice
,
defaultValue:
null
));
description
.
add
(
new
FlagProperty
(
'matchTextDirection'
,
value:
matchTextDirection
,
ifTrue:
'match text direction'
));
}
}
...
...
@@ -448,7 +503,8 @@ class _ImageState extends State<Image> {
fit:
widget
.
fit
,
alignment:
widget
.
alignment
,
repeat:
widget
.
repeat
,
centerSlice:
widget
.
centerSlice
centerSlice:
widget
.
centerSlice
,
matchTextDirection:
widget
.
matchTextDirection
,
);
}
...
...
packages/flutter/lib/src/widgets/localizations.dart
View file @
e04bf328
...
...
@@ -385,9 +385,16 @@ class Localizations extends StatefulWidget {
/// The locale of the Localizations widget for the widget tree that
/// corresponds to [BuildContext] `context`.
static
Locale
localeOf
(
BuildContext
context
)
{
///
/// If no [Localizations] widget is in scope then the [Localizations.localeOf]
/// method will throw an exception, unless the `nullOk` argument is set to
/// true, in which case it returns null.
static
Locale
localeOf
(
BuildContext
context
,
{
bool
nullOk:
false
})
{
assert
(
context
!=
null
);
assert
(
nullOk
!=
null
);
final
_LocalizationsScope
scope
=
context
.
inheritFromWidgetOfExactType
(
_LocalizationsScope
);
if
(
nullOk
&&
scope
==
null
)
return
null
;
assert
(
scope
!=
null
,
'a Localizations ancestor was not found'
);
return
scope
.
localizationsState
.
locale
;
}
...
...
packages/flutter/lib/src/widgets/media_query.dart
View file @
e04bf328
...
...
@@ -173,6 +173,8 @@ class MediaQuery extends InheritedWidget {
/// If you use this from a widget (e.g. in its build function), consider
/// calling [debugCheckHasMediaQuery].
static
MediaQueryData
of
(
BuildContext
context
,
{
bool
nullOk:
false
})
{
assert
(
context
!=
null
);
assert
(
nullOk
!=
null
);
final
MediaQuery
query
=
context
.
inheritFromWidgetOfExactType
(
MediaQuery
);
if
(
query
!=
null
)
return
query
.
data
;
...
...
packages/flutter/test/rendering/image_test.dart
View file @
e04bf328
...
...
@@ -74,6 +74,7 @@ void main() {
' constraints: BoxConstraints(25.0<=w<=100.0, 25.0<=h<=100.0)
\n
'
' size: Size(25.0, 25.0)
\n
'
' image: [10×10]
\n
'
' alignment: FractionalOffset.center
\n
'
),
);
...
...
packages/flutter/test/rendering/mock_canvas.dart
View file @
e04bf328
...
...
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:ui'
as
ui
show
Paragraph
;
import
'dart:ui'
as
ui
show
Paragraph
,
Image
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/rendering.dart'
;
...
...
@@ -278,6 +278,24 @@ abstract class PaintPattern {
/// If no call to [Canvas.drawParagraph] was made, then this results in failure.
void
paragraph
({
ui
.
Paragraph
paragraph
,
Offset
offset
});
/// Indicates that an image is expected next.
///
/// The next call to [Canvas.drawImageRect] is examined, and its arguments
/// compared to those passed to _this_ method.
///
/// If no call to [Canvas.drawImageRect] was made, then this results in
/// failure.
///
/// Any calls made between the last matched call (if any) and the
/// [Canvas.drawImageRect] call are ignored.
///
/// The [Paint]-related arguments (`color`, `strokeWidth`, `hasMaskFilter`,
/// `style`) are compared against the state of the [Paint] object after the
/// painting has completed, not at the time of the call. If the same [Paint]
/// object is reused multiple times, then this may not match the actual
/// arguments as they were seen by the method.
void
drawImageRect
({
ui
.
Image
image
,
Rect
source
,
Rect
destination
,
Color
color
,
double
strokeWidth
,
bool
hasMaskFilter
,
PaintingStyle
style
});
/// Provides a custom matcher.
///
/// Each method call after the last matched call (if any) will be passed to
...
...
@@ -472,6 +490,11 @@ class _TestRecordingCanvasPatternMatcher extends _TestRecordingCanvasMatcher imp
_predicates
.
add
(
new
_FunctionPaintPredicate
(
#drawParagraph
,
<
dynamic
>[
paragraph
,
offset
]));
}
@override
void
drawImageRect
({
ui
.
Image
image
,
Rect
source
,
Rect
destination
,
Color
color
,
double
strokeWidth
,
bool
hasMaskFilter
,
PaintingStyle
style
})
{
_predicates
.
add
(
new
_DrawImageRectPaintPredicate
(
image:
image
,
source
:
source
,
destination:
destination
,
color:
color
,
strokeWidth:
strokeWidth
,
hasMaskFilter:
hasMaskFilter
,
style:
style
));
}
@override
void
something
(
PaintPatternPredicate
predicate
)
{
_predicates
.
add
(
new
_SomethingPaintPredicate
(
predicate
));
...
...
@@ -800,6 +823,41 @@ class _ArcPaintPredicate extends _DrawCommandPaintPredicate {
);
}
class
_DrawImageRectPaintPredicate
extends
_DrawCommandPaintPredicate
{
_DrawImageRectPaintPredicate
({
this
.
image
,
this
.
source
,
this
.
destination
,
Color
color
,
double
strokeWidth
,
bool
hasMaskFilter
,
PaintingStyle
style
})
:
super
(
#drawImageRect
,
'an image'
,
4
,
3
,
color:
color
,
strokeWidth:
strokeWidth
,
hasMaskFilter:
hasMaskFilter
,
style:
style
);
final
ui
.
Image
image
;
final
Rect
source
;
final
Rect
destination
;
@override
void
verifyArguments
(
List
<
dynamic
>
arguments
)
{
super
.
verifyArguments
(
arguments
);
final
ui
.
Image
imageArgument
=
arguments
[
0
];
if
(
image
!=
null
&&
imageArgument
!=
image
)
throw
'It called
$methodName
with an image,
$imageArgument
, which was not exactly the expected image (
$image
).'
;
final
Rect
sourceArgument
=
arguments
[
1
];
if
(
source
!=
null
&&
sourceArgument
!=
source
)
throw
'It called
$methodName
with a source rectangle,
$sourceArgument
, which was not exactly the expected rectangle (
$source
).'
;
final
Rect
destinationArgument
=
arguments
[
2
];
if
(
destination
!=
null
&&
destinationArgument
!=
destination
)
throw
'It called
$methodName
with a destination rectangle,
$destinationArgument
, which was not exactly the expected rectangle (
$destination
).'
;
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
if
(
image
!=
null
)
description
.
add
(
'image
$image
'
);
if
(
source
!=
null
)
description
.
add
(
'source
$source
'
);
if
(
destination
!=
null
)
description
.
add
(
'destination
$destination
'
);
}
}
class
_SomethingPaintPredicate
extends
_PaintPredicate
{
_SomethingPaintPredicate
(
this
.
predicate
);
...
...
packages/flutter/test/widgets/align_test.dart
View file @
e04bf328
...
...
@@ -21,6 +21,26 @@ void main() {
alignment:
const
FractionalOffset
(
0.5
,
0.5
),
),
);
await
tester
.
pumpWidget
(
const
Align
(
key:
const
GlobalObjectKey
<
Null
>(
null
),
alignment:
FractionalOffset
.
topLeft
,
),
);
await
tester
.
pumpWidget
(
const
Directionality
(
textDirection:
TextDirection
.
rtl
,
child:
const
Align
(
key:
const
GlobalObjectKey
<
Null
>(
null
),
alignment:
FractionalOffsetDirectional
.
topStart
,
),
));
await
tester
.
pumpWidget
(
const
Align
(
key:
const
GlobalObjectKey
<
Null
>(
null
),
alignment:
FractionalOffset
.
topLeft
,
),
);
});
testWidgets
(
'Align control test (LTR)'
,
(
WidgetTester
tester
)
async
{
...
...
packages/flutter/test/widgets/image_rtl_test.dart
0 → 100644
View file @
e04bf328
This diff is collapsed.
Click to expand it.
packages/flutter/test/widgets/rtl_test.dart
View file @
e04bf328
...
...
@@ -21,6 +21,26 @@ void main() {
child:
child
,
));
expect
(
tester
.
getTopLeft
(
find
.
byType
(
Placeholder
)),
const
Offset
(
0.0
,
0.0
));
await
tester
.
pumpWidget
(
const
Padding
(
key:
const
GlobalObjectKey
<
Null
>(
null
),
padding:
const
EdgeInsets
.
only
(
left:
1.0
),
),
);
await
tester
.
pumpWidget
(
const
Directionality
(
textDirection:
TextDirection
.
rtl
,
child:
const
Padding
(
key:
const
GlobalObjectKey
<
Null
>(
null
),
padding:
const
EdgeInsetsDirectional
.
only
(
start:
1.0
),
),
));
await
tester
.
pumpWidget
(
const
Padding
(
key:
const
GlobalObjectKey
<
Null
>(
null
),
padding:
const
EdgeInsets
.
only
(
left:
1.0
),
),
);
});
testWidgets
(
'Container padding/margin RTL'
,
(
WidgetTester
tester
)
async
{
...
...
packages/flutter/test/widgets/stack_test.dart
View file @
e04bf328
...
...
@@ -610,4 +610,23 @@ void main() {
expect
(
tester
.
getTopLeft
(
find
.
byKey
(
key
)),
const
Offset
(
50.0
,
0.0
));
});
testWidgets
(
'Can change the text direction of a Stack'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
Stack
(
alignment:
FractionalOffset
.
center
,
),
);
await
tester
.
pumpWidget
(
new
Stack
(
alignment:
FractionalOffsetDirectional
.
topStart
,
textDirection:
TextDirection
.
rtl
,
),
);
await
tester
.
pumpWidget
(
new
Stack
(
alignment:
FractionalOffset
.
center
,
),
);
});
}
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