Skip to content

Commit 0a2906b

Browse files
SalehTZvictorsanni
andauthored
Improve SweepGradient angle and TileMode documentation (flutter#172406)
# Improve SweepGradient and TileMode Documentation ## Description This PR enhances the documentation for `SweepGradient` and `TileMode` to provide clearer guidance on how angles are measured and how tile modes affect sweep gradient rendering. ### Changes 1. **SweepGradient Documentation**: - Clarified angle measurement in radians from the positive x-axis - Documented angle normalization behavior for values outside [0, 2π] - Added detailed explanations of how each `TileMode` affects rendering outside the angular sector 2. **Gradient.sweep Constructor**: - Improved parameter documentation - Added a practical example showing how to create a 90-degree sweep gradient - Clarified the relationship between color stops and angles 3. **TileMode Documentation**: - Added sweep gradient-specific behavior to each `TileMode` variant - Clarified how each mode (clamp, repeated, mirror, decal) affects rendering outside the angular sector - Improved overall documentation structure for gradient edge behavior ## Related Issues Fixes flutter#166206 ## Testing - Verified documentation changes by reviewing the generated API docs - Ensured all examples compile and render as expected ## Breaking Changes None - this is purely a documentation improvement. ## Additional Notes The changes make it much clearer how `startAngle` and `endAngle` interact with different `TileMode` values, which was a source of confusion in the original issue. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: Victor Sanni <[email protected]>
1 parent 36e1db8 commit 0a2906b

File tree

2 files changed

+95
-28
lines changed

2 files changed

+95
-28
lines changed

engine/src/flutter/lib/ui/painting.dart

Lines changed: 69 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4646,23 +4646,32 @@ base class Shader extends NativeFieldWrapperClass1 {
46464646
}
46474647
}
46484648

4649-
/// Defines what happens at the edge of a gradient or the sampling of a source image
4650-
/// in an [ImageFilter].
4649+
/// Defines how to handle areas outside the defined bounds of a gradient or image filter.
46514650
///
4652-
/// A gradient is defined along a finite inner area. In the case of a linear
4653-
/// gradient, it's between the parallel lines that are orthogonal to the line
4654-
/// drawn between two points. In the case of radial gradients, it's the disc
4655-
/// that covers the circle centered on a particular point up to a given radius.
4651+
/// ## For Gradients
46564652
///
4657-
/// An image filter reads source samples from a source image and performs operations
4658-
/// on those samples to produce a result image. An image defines color samples only
4659-
/// for pixels within the bounds of the image but some filter operations, such as a blur
4660-
/// filter, read samples over a wide area to compute the output for a given pixel. Such
4661-
/// a filter would need to combine samples from inside the image with hypothetical
4662-
/// color values from outside the image.
4653+
/// Gradients are defined with some specific bounds creating an inner area and an outer area, and `TileMode` controls how colors
4654+
/// are determined for areas outside these bounds:
46634655
///
4664-
/// This enum is used to define how the gradient or image filter should treat the regions
4665-
/// outside that defined inner area.
4656+
/// - **Linear gradients**: The inner area is the area between two points
4657+
/// (typically referred to as `start` and `end` in the gradient API), or more precisely,
4658+
/// it's the area between the parallel lines that are orthogonal to the line drawn between the two points.
4659+
/// Colors outside this area are determined by the `TileMode`.
4660+
///
4661+
/// - **Radial gradients**: The inner area is the disc defined by a center and radius.
4662+
/// Colors outside this disc are determined by the `TileMode`.
4663+
///
4664+
/// - **Sweep gradients**: The inner area is the angular sector between `startAngle`
4665+
/// and `endAngle`. Colors outside this sector are determined by the `TileMode`.
4666+
///
4667+
/// ## For Image Filters
4668+
///
4669+
/// When applying filters (like blur) that sample colors from outside an image's bounds,
4670+
/// `TileMode` defines how those out-of-bounds samples are determined:
4671+
///
4672+
/// - It controls what color values are used when the filter needs to sample
4673+
/// from areas outside the original image.
4674+
/// - This is particularly important for effects like blurring near image edges.
46664675
///
46674676
/// See also:
46684677
///
@@ -4680,8 +4689,12 @@ base class Shader extends NativeFieldWrapperClass1 {
46804689
enum TileMode {
46814690
/// Samples beyond the edge are clamped to the nearest color in the defined inner area.
46824691
///
4683-
/// A gradient will paint all the regions outside the inner area with the
4684-
/// color at the end of the color stop list closest to that region.
4692+
/// For gradients, this means the region outside the inner area is painted with
4693+
/// the color at the end of the color stop list closest to that region.
4694+
///
4695+
/// For sweep gradients specifically, the entire area outside the angular sector
4696+
/// defined by [startAngle] and [endAngle] will be painted with the color at the
4697+
/// end of the color stop list closest to that region.
46854698
///
46864699
/// An image filter will substitute the nearest edge pixel for any samples taken from
46874700
/// outside its source image.
@@ -4697,6 +4710,9 @@ enum TileMode {
46974710
/// repeated from 1.0 to 2.0, 2.0 to 3.0, and so forth (and for linear gradients, similarly
46984711
/// from -1.0 to 0.0, -2.0 to -1.0, etc).
46994712
///
4713+
/// For sweep gradients, the gradient pattern is repeated in the same direction
4714+
/// (clockwise) for angles beyond [endAngle] and before [startAngle].
4715+
///
47004716
/// An image filter will treat its source image as if it were tiled across the enlarged
47014717
/// sample space from which it reads, each tile in the same orientation as the base image.
47024718
///
@@ -4712,6 +4728,9 @@ enum TileMode {
47124728
/// again from 4.0 to 3.0, and so forth (and for linear gradients, similarly in the
47134729
/// negative direction).
47144730
///
4731+
/// For sweep gradients, the gradient pattern is mirrored back and forth as the angle
4732+
/// increases beyond [endAngle] or decreases below [startAngle].
4733+
///
47154734
/// An image filter will treat its source image as tiled in an alternating forwards and
47164735
/// backwards or upwards and downwards direction across the sample space from which
47174736
/// it is reading.
@@ -4724,8 +4743,11 @@ enum TileMode {
47244743
/// Samples beyond the edge are treated as transparent black.
47254744
///
47264745
/// A gradient will render transparency over any region that is outside the circle of a
4727-
/// radial gradient or outside the parallel lines that define the inner area of a linear
4728-
/// gradient.
4746+
/// radial gradient, outside the parallel lines that define the inner area of a linear
4747+
/// gradient, or outside the angular sector of a sweep gradient.
4748+
///
4749+
/// For sweep gradients, only the sector between [startAngle] and [endAngle] will be
4750+
/// painted; all other areas will be transparent.
47294751
///
47304752
/// An image filter will substitute transparent black for any sample it must read from
47314753
/// outside its source image.
@@ -4933,17 +4955,39 @@ base class Gradient extends Shader {
49334955
/// positive angles going clockwise around the `center`.
49344956
///
49354957
/// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0
4936-
/// that specifies where `color[i]` begins in the gradient. If `colorStops` is
4937-
/// not provided, then only two stops, at 0.0 and 1.0, are implied (and
4938-
/// `color` must therefore only have two entries). Stop values less than 0.0
4939-
/// will be rounded up to 0.0 and stop values greater than 1.0 will be rounded
4940-
/// down to 1.0. Each stop value must be greater than or equal to the previous
4941-
/// stop value. Stop values that do not meet this criteria will be rounded up
4942-
/// to the previous stop value.
4958+
/// that specifies where `colors[i]` begins in the gradient. If `colorStops` is
4959+
/// not provided, then only two stops, at 0.0 and 1.0, are implied
4960+
/// (and `colors` must therefore only have two entries). Stop values less than
4961+
/// 0.0 will be rounded up to 0.0 and stop values greater than 1.0 will be
4962+
/// rounded down to 1.0. Each stop value must be greater than or equal to the
4963+
/// previous stop value. Stop values that do not meet this criteria will be
4964+
/// rounded up to the previous stop value.
4965+
///
4966+
/// The `startAngle` and `endAngle` parameters define the angular sector to be
4967+
/// painted. Angles are measured in radians clockwise from the positive x-axis.
4968+
/// Values outside the range `[0, 2π]` are normalized to this range using modulo
4969+
/// arithmetic. The gradient is only painted in the sector between `startAngle`
4970+
/// and `endAngle`. The `tileMode` determines how the gradient behaves outside
4971+
/// this sector.
4972+
///
4973+
/// The `tileMode` argument specifies how the gradient should handle areas
4974+
/// outside the angular sector defined by `startAngle` and `endAngle`:
49434975
///
49444976
/// The behavior before `startAngle` and after `endAngle` is described by the
49454977
/// `tileMode` argument. For details, see the [TileMode] enum.
49464978
///
4979+
/// * [TileMode.clamp]: The edge colors are extended to infinity.
4980+
/// * [TileMode.mirror]: The gradient is repeated, alternating direction each time.
4981+
/// * [TileMode.repeated]: The gradient is repeated in the same direction.
4982+
/// * [TileMode.decal]: Only the colors within the gradient's angular sector are
4983+
/// drawn, with transparent black elsewhere.
4984+
///
4985+
/// The [colorStops] argument must have the same number of values as [colors],
4986+
/// if specified. It specifies the position of each color stop between 0.0 and
4987+
/// 1.0. If it is null, a uniform distribution is assumed. The stop values must
4988+
/// be in ascending order. A stop value of 0.0 corresponds to [startAngle], and
4989+
/// a stop value of 1.0 corresponds to [endAngle].
4990+
///
49474991
/// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_sweep.png)
49484992
/// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_decal_sweep.png)
49494993
/// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_mirror_sweep.png)

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

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -984,18 +984,41 @@ class SweepGradient extends Gradient {
984984

985985
/// The angle in radians at which stop 0.0 of the gradient is placed.
986986
///
987+
/// The angle is measured in radians clockwise from the positive x-axis.
988+
///
989+
/// Values outside the range `[0, 2π]` are normalized to the equivalent angle
990+
/// within this range using modulo arithmetic.
991+
///
992+
/// The gradient will be painted in the sector between [startAngle] and [endAngle].
993+
/// The behavior outside this sector is determined by [tileMode].
994+
///
987995
/// Defaults to 0.0.
988996
final double startAngle;
989997

990998
/// The angle in radians at which stop 1.0 of the gradient is placed.
991999
///
992-
/// Defaults to math.pi * 2.
1000+
/// The angle is measured in radians clockwise from the positive x-axis.
1001+
///
1002+
/// Values outside the range `[0, 2π]` are normalized to the equivalent angle
1003+
/// within this range using modulo arithmetic.
1004+
///
1005+
/// The gradient will be painted in the sector between [startAngle] and [endAngle].
1006+
/// The behavior outside this sector is determined by [tileMode].
1007+
///
1008+
/// Defaults to math.pi * 2 (2π = a full circle).
9931009
final double endAngle;
9941010

995-
/// How this gradient should tile the plane beyond in the region before
1011+
/// How this gradient should tile the plane in the region before
9961012
/// [startAngle] and after [endAngle].
9971013
///
998-
/// For details, see [TileMode].
1014+
/// The gradient will be painted in the sector between [startAngle] and
1015+
/// [endAngle]. The [tileMode] determines what happens in the remaining area:
1016+
///
1017+
/// * [TileMode.clamp]: The edge colors are extended to fill the remaining area.
1018+
/// * [TileMode.repeated]: The gradient is repeated in the angular direction.
1019+
/// * [TileMode.mirror]: The gradient is mirrored in the angular direction.
1020+
/// * [TileMode.decal]: Only the gradient is drawn, leaving the remaining area
1021+
/// transparent.
9991022
///
10001023
/// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_clamp_sweep.png)
10011024
/// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/tile_mode_decal_sweep.png)

0 commit comments

Comments
 (0)