Skip to content

Commit daedbc8

Browse files
authored
LinearGradient and RadialGradient RTL (#12204)
1 parent 4f6e350 commit daedbc8

File tree

3 files changed

+214
-99
lines changed

3 files changed

+214
-99
lines changed

packages/flutter/lib/src/painting/gradient.dart

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ abstract class Gradient {
2525
const Gradient();
2626

2727
/// Creates a [Shader] for this gradient to fill the given rect.
28-
Shader createShader(Rect rect);
28+
///
29+
/// If the gradient's configuration is text-direction-dependent, for example
30+
/// it uses [FractionalOffsetDirection] objects instead of [FractionalOffset]
31+
/// objects, then the `textDirection` argument must not be null.
32+
Shader createShader(Rect rect, { TextDirection textDirection });
2933
}
3034

3135
/// A 2D linear gradient.
@@ -91,21 +95,37 @@ class LinearGradient extends Gradient {
9195
assert(colors != null),
9296
assert(tileMode != null);
9397

94-
/// The offset from coordinate (0.0,0.0) at which stop 0.0 of the
95-
/// gradient is placed, in a coordinate space that maps the top left
96-
/// of the paint box at (0.0,0.0) and the bottom right at (1.0,1.0).
98+
/// The offset at which stop 0.0 of the gradient is placed.
99+
///
100+
/// If this is a [FractionalOffset], then it is expressed as a vector from
101+
/// coordinate (0.0,0.0), in a coordinate space that maps the top left of the
102+
/// paint box at (0.0,0.0) and the bottom right at (1.0,1.0).
97103
///
98104
/// For example, a begin offset of (0.0,0.5) is half way down the
99105
/// left side of the box.
100-
final FractionalOffset begin;
106+
///
107+
/// It can also be a [FractionalOffsetDirectional], in which case it is
108+
/// expressed as a vector from the top start corner, where the start is the
109+
/// left in left-to-right contexts and the right in right-to-left contexts. If
110+
/// a text-direction-dependent value is provided here, then the [createShader]
111+
/// method will need to be given a [TextDirection].
112+
final FractionalOffsetGeometry begin;
101113

102-
/// The offset from coordinate (0.0,0.0) at which stop 1.0 of the
103-
/// gradient is placed, in a coordinate space that maps the top left
104-
/// of the paint box at (0.0,0.0) and the bottom right at (1.0,1.0).
114+
/// The offset at which stop 1.0 of the gradient is placed.
105115
///
106-
/// For example, an end offset of (1.0,0.5) is half way down the
116+
/// If this is a [FractionalOffset], then it is expressed as a vector from
117+
/// coordinate (0.0,0.0), in a coordinate space that maps the top left of the
118+
/// paint box at (0.0,0.0) and the bottom right at (1.0,1.0).
119+
///
120+
/// For example, a begin offset of (1.0,0.5) is half way down the
107121
/// right side of the box.
108-
final FractionalOffset end;
122+
///
123+
/// It can also be a [FractionalOffsetDirectional], in which case it is
124+
/// expressed as a vector from the top start corner, where the start is the
125+
/// left in left-to-right contexts and the right in right-to-left contexts. If
126+
/// a text-direction-dependent value is provided here, then the [createShader]
127+
/// method will need to be given a [TextDirection].
128+
final FractionalOffsetGeometry end;
109129

110130
/// The colors the gradient should obtain at each of the stops.
111131
///
@@ -144,16 +164,20 @@ class LinearGradient extends Gradient {
144164
final TileMode tileMode;
145165

146166
@override
147-
Shader createShader(Rect rect) {
167+
Shader createShader(Rect rect, { TextDirection textDirection }) {
148168
return new ui.Gradient.linear(
149-
begin.withinRect(rect),
150-
end.withinRect(rect),
169+
begin.resolve(textDirection).withinRect(rect),
170+
end.resolve(textDirection).withinRect(rect),
151171
colors, stops, tileMode,
152172
);
153173
}
154174

155-
/// Returns a new [LinearGradient] with its properties scaled by the given
156-
/// factor.
175+
/// Returns a new [LinearGradient] with its properties (in particular the
176+
/// colors) scaled by the given factor.
177+
///
178+
/// If the factor is 1.0 or greater, then the gradient is returned unmodified.
179+
/// If the factor is 0.0 or less, then the gradient is fully transparent.
180+
/// Values in between scale the opacity of the colors.
157181
LinearGradient scale(double factor) {
158182
return new LinearGradient(
159183
begin: begin,
@@ -168,7 +192,7 @@ class LinearGradient extends Gradient {
168192
///
169193
/// If either gradient is null, this function linearly interpolates from a
170194
/// a gradient that matches the other gradient in [begin], [end], [stops] and
171-
/// [tileMode] and with the same [colors] but transparent.
195+
/// [tileMode] and with the same [colors] but transparent (using [scale]).
172196
///
173197
/// If neither gradient is null, they must have the same number of [colors].
174198
static LinearGradient lerp(LinearGradient a, LinearGradient b, double t) {
@@ -178,11 +202,7 @@ class LinearGradient extends Gradient {
178202
return b.scale(t);
179203
if (b == null)
180204
return a.scale(1.0 - t);
181-
// Interpolation is only possible when the lengths of colors and stops are
182-
// the same or stops is null for one.
183-
// TODO(xster): lerp unsimilar LinearGradients in the future by scaling
184-
// lists of LinearGradients.
185-
assert(a.colors.length == b.colors.length);
205+
assert(a.colors.length == b.colors.length, 'Cannot interpolate between two gradients with a different number of colors.');
186206
assert(a.stops == null || b.stops == null || a.stops.length == b.stops.length);
187207
final List<Color> interpolatedColors = <Color>[];
188208
for (int i = 0; i < a.colors.length; i += 1)
@@ -195,8 +215,8 @@ class LinearGradient extends Gradient {
195215
interpolatedStops = a.stops ?? b.stops;
196216
}
197217
return new LinearGradient(
198-
begin: FractionalOffset.lerp(a.begin, b.begin, t),
199-
end: FractionalOffset.lerp(a.end, b.end, t),
218+
begin: FractionalOffsetGeometry.lerp(a.begin, b.begin, t),
219+
end: FractionalOffsetGeometry.lerp(a.end, b.end, t),
200220
colors: interpolatedColors,
201221
stops: interpolatedStops,
202222
tileMode: t < 0.5 ? a.tileMode : b.tileMode,
@@ -240,7 +260,7 @@ class LinearGradient extends Gradient {
240260

241261
@override
242262
String toString() {
243-
return 'LinearGradient($begin, $end, $colors, $stops, $tileMode)';
263+
return '$runtimeType($begin, $end, $colors, $stops, $tileMode)';
244264
}
245265
}
246266

@@ -319,7 +339,17 @@ class RadialGradient extends Gradient {
319339
///
320340
/// For example, an offset of (0.5,0.5) will place the radial
321341
/// gradient in the center of the box.
322-
final FractionalOffset center;
342+
///
343+
/// If this is a [FractionalOffset], then it is expressed as a vector from
344+
/// coordinate (0.0,0.0), in a coordinate space that maps the top left of the
345+
/// paint box at (0.0,0.0) and the bottom right at (1.0,1.0).
346+
///
347+
/// It can also be a [FractionalOffsetDirectional], in which case it is
348+
/// expressed as a vector from the top start corner, where the start is the
349+
/// left in left-to-right contexts and the right in right-to-left contexts. If
350+
/// a text-direction-dependent value is provided here, then the [createShader]
351+
/// method will need to be given a [TextDirection].
352+
final FractionalOffsetGeometry center;
323353

324354
/// The radius of the gradient, as a fraction of the shortest side
325355
/// of the paint box.
@@ -368,11 +398,11 @@ class RadialGradient extends Gradient {
368398
final TileMode tileMode;
369399

370400
@override
371-
Shader createShader(Rect rect) {
401+
Shader createShader(Rect rect, { TextDirection textDirection }) {
372402
return new ui.Gradient.radial(
373-
center.withinRect(rect),
403+
center.resolve(textDirection).withinRect(rect),
374404
radius * rect.shortestSide,
375-
colors, stops, tileMode
405+
colors, stops, tileMode,
376406
);
377407
}
378408

@@ -413,6 +443,6 @@ class RadialGradient extends Gradient {
413443

414444
@override
415445
String toString() {
416-
return 'RadialGradient($center, $radius, $colors, $stops, $tileMode)';
446+
return '$runtimeType($center, $radius, $colors, $stops, $tileMode)';
417447
}
418448
}

packages/flutter/test/painting/box_painter_test.dart

Lines changed: 0 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -112,74 +112,4 @@ void main() {
112112
test('BoxShadow toString test', () {
113113
expect(const BoxShadow(blurRadius: 4.0).toString(), equals('BoxShadow(Color(0xff000000), Offset(0.0, 0.0), 4.0, 0.0)'));
114114
});
115-
116-
test('LinearGradient scale test', () {
117-
final LinearGradient testGradient = const LinearGradient(
118-
begin: FractionalOffset.bottomRight,
119-
end: const FractionalOffset(0.7, 1.0),
120-
colors: const <Color>[
121-
const Color(0x00FFFFFF),
122-
const Color(0x11777777),
123-
const Color(0x44444444),
124-
],
125-
);
126-
final LinearGradient actual = LinearGradient.lerp(null, testGradient, 0.25);
127-
128-
expect(actual, const LinearGradient(
129-
begin: FractionalOffset.bottomRight,
130-
end: const FractionalOffset(0.7, 1.0),
131-
colors: const <Color>[
132-
const Color(0x00FFFFFF),
133-
const Color(0x04777777),
134-
const Color(0x11444444),
135-
],
136-
));
137-
});
138-
139-
test('LinearGradient lerp test', () {
140-
final LinearGradient testGradient1 = const LinearGradient(
141-
begin: FractionalOffset.topLeft,
142-
end: FractionalOffset.bottomLeft,
143-
colors: const <Color>[
144-
const Color(0x33333333),
145-
const Color(0x66666666),
146-
],
147-
);
148-
149-
final LinearGradient testGradient2 = const LinearGradient(
150-
begin: FractionalOffset.topRight,
151-
end: FractionalOffset.topLeft,
152-
colors: const <Color>[
153-
const Color(0x44444444),
154-
const Color(0x88888888),
155-
],
156-
);
157-
final LinearGradient actual =
158-
LinearGradient.lerp(testGradient1, testGradient2, 0.5);
159-
160-
expect(actual, const LinearGradient(
161-
begin: const FractionalOffset(0.5, 0.0),
162-
end: const FractionalOffset(0.0, 0.5),
163-
colors: const <Color>[
164-
const Color(0x3B3B3B3B),
165-
const Color(0x77777777),
166-
],
167-
));
168-
});
169-
170-
test('LinearGradient toString', () {
171-
expect(
172-
const LinearGradient(
173-
begin: FractionalOffset.topLeft,
174-
end: FractionalOffset.bottomLeft,
175-
colors: const <Color>[
176-
const Color(0x33333333),
177-
const Color(0x66666666),
178-
],
179-
).toString(),
180-
equals(
181-
'LinearGradient(FractionalOffset.topLeft, FractionalOffset.bottomLeft, [Color(0x33333333), Color(0x66666666)], null, TileMode.clamp)',
182-
),
183-
);
184-
});
185115
}

0 commit comments

Comments
 (0)