Unverified Commit d01d8aee authored by Alex Li's avatar Alex Li Committed by GitHub

Add `strokeAlign` to `CircularProgressIndicator` and `RefreshProgressIndicator` (#125945)

parent a3257ca5
...@@ -421,6 +421,7 @@ class _CircularProgressIndicatorPainter extends CustomPainter { ...@@ -421,6 +421,7 @@ class _CircularProgressIndicatorPainter extends CustomPainter {
required this.offsetValue, required this.offsetValue,
required this.rotationValue, required this.rotationValue,
required this.strokeWidth, required this.strokeWidth,
required this.strokeAlign,
this.strokeCap, this.strokeCap,
}) : arcStart = value != null }) : arcStart = value != null
? _startAngle ? _startAngle
...@@ -437,6 +438,7 @@ class _CircularProgressIndicatorPainter extends CustomPainter { ...@@ -437,6 +438,7 @@ class _CircularProgressIndicatorPainter extends CustomPainter {
final double offsetValue; final double offsetValue;
final double rotationValue; final double rotationValue;
final double strokeWidth; final double strokeWidth;
final double strokeAlign;
final double arcStart; final double arcStart;
final double arcSweep; final double arcSweep;
final StrokeCap? strokeCap; final StrokeCap? strokeCap;
...@@ -454,12 +456,27 @@ class _CircularProgressIndicatorPainter extends CustomPainter { ...@@ -454,12 +456,27 @@ class _CircularProgressIndicatorPainter extends CustomPainter {
..strokeWidth = strokeWidth ..strokeWidth = strokeWidth
..style = PaintingStyle.stroke; ..style = PaintingStyle.stroke;
// Use the negative operator as intended to keep the exposed constant value
// as users are already familiar with.
final double strokeOffset = strokeWidth / 2 * -strokeAlign;
final Offset arcBaseOffset = Offset(strokeOffset, strokeOffset);
final Size arcActualSize = Size(
size.width - strokeOffset * 2,
size.height - strokeOffset * 2,
);
if (backgroundColor != null) { if (backgroundColor != null) {
final Paint backgroundPaint = Paint() final Paint backgroundPaint = Paint()
..color = backgroundColor! ..color = backgroundColor!
..strokeWidth = strokeWidth ..strokeWidth = strokeWidth
..style = PaintingStyle.stroke; ..style = PaintingStyle.stroke;
canvas.drawArc(Offset.zero & size, 0, _sweep, false, backgroundPaint); canvas.drawArc(
arcBaseOffset & arcActualSize,
0,
_sweep,
false,
backgroundPaint,
);
} }
if (value == null && strokeCap == null) { if (value == null && strokeCap == null) {
...@@ -470,7 +487,13 @@ class _CircularProgressIndicatorPainter extends CustomPainter { ...@@ -470,7 +487,13 @@ class _CircularProgressIndicatorPainter extends CustomPainter {
paint.strokeCap = strokeCap ?? StrokeCap.butt; paint.strokeCap = strokeCap ?? StrokeCap.butt;
} }
canvas.drawArc(Offset.zero & size, arcStart, arcSweep, false, paint); canvas.drawArc(
arcBaseOffset & arcActualSize,
arcStart,
arcSweep,
false,
paint,
);
} }
@override @override
...@@ -483,6 +506,7 @@ class _CircularProgressIndicatorPainter extends CustomPainter { ...@@ -483,6 +506,7 @@ class _CircularProgressIndicatorPainter extends CustomPainter {
|| oldPainter.offsetValue != offsetValue || oldPainter.offsetValue != offsetValue
|| oldPainter.rotationValue != rotationValue || oldPainter.rotationValue != rotationValue
|| oldPainter.strokeWidth != strokeWidth || oldPainter.strokeWidth != strokeWidth
|| oldPainter.strokeAlign != strokeAlign
|| oldPainter.strokeCap != strokeCap; || oldPainter.strokeCap != strokeCap;
} }
} }
...@@ -538,6 +562,7 @@ class CircularProgressIndicator extends ProgressIndicator { ...@@ -538,6 +562,7 @@ class CircularProgressIndicator extends ProgressIndicator {
super.color, super.color,
super.valueColor, super.valueColor,
this.strokeWidth = 4.0, this.strokeWidth = 4.0,
this.strokeAlign = strokeAlignCenter,
super.semanticsLabel, super.semanticsLabel,
super.semanticsValue, super.semanticsValue,
this.strokeCap, this.strokeCap,
...@@ -560,6 +585,7 @@ class CircularProgressIndicator extends ProgressIndicator { ...@@ -560,6 +585,7 @@ class CircularProgressIndicator extends ProgressIndicator {
super.semanticsLabel, super.semanticsLabel,
super.semanticsValue, super.semanticsValue,
this.strokeCap, this.strokeCap,
this.strokeAlign = strokeAlignCenter,
}) : _indicatorType = _ActivityIndicatorType.adaptive; }) : _indicatorType = _ActivityIndicatorType.adaptive;
final _ActivityIndicatorType _indicatorType; final _ActivityIndicatorType _indicatorType;
...@@ -577,6 +603,15 @@ class CircularProgressIndicator extends ProgressIndicator { ...@@ -577,6 +603,15 @@ class CircularProgressIndicator extends ProgressIndicator {
/// The width of the line used to draw the circle. /// The width of the line used to draw the circle.
final double strokeWidth; final double strokeWidth;
/// The relative position of the stroke on a [CircularProgressIndicator].
///
/// Values typically range from -1.0 ([strokeAlignInside], inside stroke)
/// to 1.0 ([strokeAlignOutside], outside stroke),
/// without any bound constraints (e.g., a value of -2.0 is not typical, but allowed).
/// A value of 0 ([strokeAlignCenter], default) will center the border
/// on the edge of the widget.
final double strokeAlign;
/// The progress indicator's line ending. /// The progress indicator's line ending.
/// ///
/// This determines the shape of the stroke ends of the progress indicator. /// This determines the shape of the stroke ends of the progress indicator.
...@@ -598,6 +633,25 @@ class CircularProgressIndicator extends ProgressIndicator { ...@@ -598,6 +633,25 @@ class CircularProgressIndicator extends ProgressIndicator {
/// degrees and end at 275 degrees. /// degrees and end at 275 degrees.
final StrokeCap? strokeCap; final StrokeCap? strokeCap;
/// The indicator stroke is drawn fully inside of the indicator path.
///
/// This is a constant for use with [strokeAlign].
static const double strokeAlignInside = -1.0;
/// The indicator stroke is drawn on the center of the indicator path,
/// with half of the [strokeWidth] on the inside, and the other half
/// on the outside of the path.
///
/// This is a constant for use with [strokeAlign].
///
/// This is the default value for [strokeAlign].
static const double strokeAlignCenter = 0.0;
/// The indicator stroke is drawn on the outside of the indicator path.
///
/// This is a constant for use with [strokeAlign].
static const double strokeAlignOutside = 1.0;
@override @override
State<CircularProgressIndicator> createState() => _CircularProgressIndicatorState(); State<CircularProgressIndicator> createState() => _CircularProgressIndicatorState();
} }
...@@ -677,6 +731,7 @@ class _CircularProgressIndicatorState extends State<CircularProgressIndicator> w ...@@ -677,6 +731,7 @@ class _CircularProgressIndicatorState extends State<CircularProgressIndicator> w
offsetValue: offsetValue, offsetValue: offsetValue,
rotationValue: rotationValue, rotationValue: rotationValue,
strokeWidth: widget.strokeWidth, strokeWidth: widget.strokeWidth,
strokeAlign: widget.strokeAlign,
strokeCap: widget.strokeCap, strokeCap: widget.strokeCap,
), ),
), ),
...@@ -735,6 +790,7 @@ class _RefreshProgressIndicatorPainter extends _CircularProgressIndicatorPainter ...@@ -735,6 +790,7 @@ class _RefreshProgressIndicatorPainter extends _CircularProgressIndicatorPainter
required super.offsetValue, required super.offsetValue,
required super.rotationValue, required super.rotationValue,
required super.strokeWidth, required super.strokeWidth,
required super.strokeAlign,
required this.arrowheadScale, required this.arrowheadScale,
required super.strokeCap, required super.strokeCap,
}); });
...@@ -805,6 +861,7 @@ class RefreshProgressIndicator extends CircularProgressIndicator { ...@@ -805,6 +861,7 @@ class RefreshProgressIndicator extends CircularProgressIndicator {
super.color, super.color,
super.valueColor, super.valueColor,
super.strokeWidth = defaultStrokeWidth, // Different default than CircularProgressIndicator. super.strokeWidth = defaultStrokeWidth, // Different default than CircularProgressIndicator.
super.strokeAlign,
super.semanticsLabel, super.semanticsLabel,
super.semanticsValue, super.semanticsValue,
super.strokeCap, super.strokeCap,
...@@ -936,6 +993,7 @@ class _RefreshProgressIndicatorState extends _CircularProgressIndicatorState { ...@@ -936,6 +993,7 @@ class _RefreshProgressIndicatorState extends _CircularProgressIndicatorState {
offsetValue: offsetValue, offsetValue: offsetValue,
rotationValue: rotationValue, rotationValue: rotationValue,
strokeWidth: widget.strokeWidth, strokeWidth: widget.strokeWidth,
strokeAlign: widget.strokeAlign,
arrowheadScale: arrowheadScale, arrowheadScale: arrowheadScale,
strokeCap: widget.strokeCap, strokeCap: widget.strokeCap,
), ),
......
...@@ -443,6 +443,47 @@ void main() { ...@@ -443,6 +443,47 @@ void main() {
expect(find.byType(CircularProgressIndicator), paints..arc(strokeWidth: 16.0)); expect(find.byType(CircularProgressIndicator), paints..arc(strokeWidth: 16.0));
}); });
testWidgets('CircularProgressIndicator strokeAlign', (WidgetTester tester) async {
await tester.pumpWidget(
Theme(
data: theme,
child: const CircularProgressIndicator(),
),
);
expect(find.byType(CircularProgressIndicator), paints..arc(rect: Offset.zero & const Size(800.0, 600.0)));
await tester.pumpWidget(
Theme(
data: theme,
child: const CircularProgressIndicator(
strokeAlign: CircularProgressIndicator.strokeAlignInside,
),
),
);
expect(find.byType(CircularProgressIndicator), paints..arc(rect: const Offset(2.0, 2.0) & const Size(796.0, 596.0)));
await tester.pumpWidget(
Theme(
data: theme,
child: const CircularProgressIndicator(
strokeAlign: CircularProgressIndicator.strokeAlignOutside,
),
),
);
expect(find.byType(CircularProgressIndicator), paints..arc(rect: const Offset(-2.0, -2.0) & const Size(804.0, 604.0)));
// Unbounded alignment.
await tester.pumpWidget(
Theme(
data: theme,
child: const CircularProgressIndicator(
strokeAlign: 2.0,
),
),
);
expect(find.byType(CircularProgressIndicator), paints..arc(rect: const Offset(-4.0, -4.0) & const Size(808.0, 608.0)));
});
testWidgets('CircularProgressIndicator with strokeCap', (WidgetTester tester) async { testWidgets('CircularProgressIndicator with strokeCap', (WidgetTester tester) async {
await tester.pumpWidget(const CircularProgressIndicator()); await tester.pumpWidget(const CircularProgressIndicator());
expect(find.byType(CircularProgressIndicator), expect(find.byType(CircularProgressIndicator),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment