From bb7d007b513d75ce4b56d4f103e6d3a21127cc81 Mon Sep 17 00:00:00 2001 From: Taha Tesser Date: Thu, 5 Dec 2024 19:55:40 +0200 Subject: [PATCH] Introduce Material 3 `year2023` flag to `ProgressIndicatorThemeData` (#159720) Related to [Introduce Material 3 `year2023` flag to the updated widget themes](https://github.com/flutter/flutter/issues/159484) ## 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]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [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 --- .../lib/src/material/progress_indicator.dart | 53 +++--- .../material/progress_indicator_theme.dart | 30 ++- .../material/progress_indicator_test.dart | 4 + .../progress_indicator_theme_test.dart | 178 +++++++++++++++++- 4 files changed, 240 insertions(+), 25 deletions(-) diff --git a/packages/flutter/lib/src/material/progress_indicator.dart b/packages/flutter/lib/src/material/progress_indicator.dart index 891773e721..a1b4085eba 100644 --- a/packages/flutter/lib/src/material/progress_indicator.dart +++ b/packages/flutter/lib/src/material/progress_indicator.dart @@ -353,7 +353,7 @@ class LinearProgressIndicator extends ProgressIndicator { 'Use ProgressIndicatorTheme to customize the ProgressIndicator appearance. ' 'This feature was deprecated after v3.26.0-0.1.pre.' ) - this.year2023 = true, + this.year2023, }) : assert(minHeight == null || minHeight > 0); /// {@template flutter.material.LinearProgressIndicator.trackColor} @@ -416,18 +416,21 @@ class LinearProgressIndicator extends ProgressIndicator { /// If that is null, then defaults to 4. final double? trackGap; - /// When true, the [LinearProgressIndicator] will use the 2023 Material 3 - /// Design appearance. + /// When true, the [LinearProgressIndicator] will use the 2023 Material Design 3 + /// appearance. /// - /// Defaults to true. If false, the [LinearProgressIndicator] will use the - /// latest Material 3 Design appearance, which was introduced in December 2023. + /// If null, then the [ProgressIndicatorThemeData.year2023] will be used. + /// If that is null, then defaults to true. + /// + /// If this is set to false, the [LinearProgressIndicator] will use the + /// latest Material Design 3 appearance, which was introduced in December 2023. /// /// If [ThemeData.useMaterial3] is false, then this property is ignored. @Deprecated( 'Use ProgressIndicatorTheme to customize the ProgressIndicator appearance. ' 'This feature was deprecated after v3.27.0-0.1.pre.' ) - final bool year2023; + final bool? year2023; @override State createState() => _LinearProgressIndicatorState(); @@ -465,14 +468,14 @@ class _LinearProgressIndicatorState extends State with } Widget _buildIndicator(BuildContext context, double animationValue, TextDirection textDirection) { + final ProgressIndicatorThemeData indicatorTheme = ProgressIndicatorTheme.of(context); + final bool year2023 = widget.year2023 ?? indicatorTheme.year2023 ?? true; final ProgressIndicatorThemeData defaults = switch (Theme.of(context).useMaterial3) { - true => widget.year2023 + true => year2023 ? _LinearProgressIndicatorDefaultsM3Year2023(context) : _LinearProgressIndicatorDefaultsM3(context), false => _LinearProgressIndicatorDefaultsM2(context), }; - - final ProgressIndicatorThemeData indicatorTheme = ProgressIndicatorTheme.of(context); final Color trackColor = widget.backgroundColor ?? indicatorTheme.linearTrackColor ?? defaults.linearTrackColor!; @@ -482,17 +485,17 @@ class _LinearProgressIndicatorState extends State with final BorderRadiusGeometry? borderRadius = widget.borderRadius ?? indicatorTheme.borderRadius ?? defaults.borderRadius; - final Color? stopIndicatorColor = !widget.year2023 + final Color? stopIndicatorColor = !year2023 ? widget.stopIndicatorColor ?? indicatorTheme.stopIndicatorColor ?? defaults.stopIndicatorColor : null; - final double? stopIndicatorRadius = !widget.year2023 + final double? stopIndicatorRadius = !year2023 ? widget.stopIndicatorRadius ?? indicatorTheme.stopIndicatorRadius ?? defaults.stopIndicatorRadius : null; - final double? trackGap = !widget.year2023 + final double? trackGap = !year2023 ? widget.trackGap ?? indicatorTheme.trackGap ?? defaults.trackGap @@ -748,7 +751,7 @@ class CircularProgressIndicator extends ProgressIndicator { 'Use ProgressIndicatorTheme to customize the ProgressIndicator appearance. ' 'This feature was deprecated after v3.27.0-0.1.pre.' ) - this.year2023 = true, + this.year2023, this.padding, }) : _indicatorType = _ActivityIndicatorType.material; @@ -778,7 +781,7 @@ class CircularProgressIndicator extends ProgressIndicator { 'Use ProgressIndicatorTheme to customize the ProgressIndicator appearance. ' 'This feature was deprecated after v3.27.0-0.2.pre.' ) - this.year2023 = true, + this.year2023, this.padding, }) : _indicatorType = _ActivityIndicatorType.adaptive; @@ -847,18 +850,21 @@ class CircularProgressIndicator extends ProgressIndicator { /// If that is null, then defaults to 4. final double? trackGap; - /// When true, the [CircularProgressIndicator] will use the 2023 Material 3 - /// Design appearance. + /// When true, the [CircularProgressIndicator] will use the 2023 Material Design 3 + /// appearance. /// - /// Defaults to true. If false, the [CircularProgressIndicator] will use the - /// latest Material 3 Design appearance, which was introduced in December 2023. + /// If null, then the [ProgressIndicatorThemeData.year2023] will be used. + /// If that is null, then defaults to true. + /// + /// If this is set to false, the [CircularProgressIndicator] will use the + /// latest Material Design 3 appearance, which was introduced in December 2023. /// /// If [ThemeData.useMaterial3] is false, then this property is ignored. @Deprecated( 'Use ProgressIndicatorTheme to customize the ProgressIndicator appearance. ' 'This feature was deprecated after v3.27.0-0.2.pre.' ) - final bool year2023; + final bool? year2023; /// The padding around the indicator track. /// @@ -954,13 +960,14 @@ class _CircularProgressIndicatorState extends State w } Widget _buildMaterialIndicator(BuildContext context, double headValue, double tailValue, double offsetValue, double rotationValue) { + final ProgressIndicatorThemeData indicatorTheme = ProgressIndicatorTheme.of(context); + final bool year2023 = widget.year2023 ?? indicatorTheme.year2023 ?? true; final ProgressIndicatorThemeData defaults = switch (Theme.of(context).useMaterial3) { - true => widget.year2023 + true => year2023 ? _CircularProgressIndicatorDefaultsM3Year2023(context, indeterminate: widget.value == null) : _CircularProgressIndicatorDefaultsM3(context, indeterminate: widget.value == null), false => _CircularProgressIndicatorDefaultsM2(context, indeterminate: widget.value == null), }; - final ProgressIndicatorThemeData indicatorTheme = ProgressIndicatorTheme.of(context); final Color? trackColor = widget.backgroundColor ?? indicatorTheme.circularTrackColor ?? defaults.circularTrackColor; @@ -975,7 +982,7 @@ class _CircularProgressIndicatorState extends State w final BoxConstraints constraints = widget.constraints ?? indicatorTheme.constraints ?? defaults.constraints!; - final double? trackGap = widget.year2023 + final double? trackGap = year2023 ? null : widget.trackGap ?? indicatorTheme.trackGap ?? @@ -999,7 +1006,7 @@ class _CircularProgressIndicatorState extends State w strokeAlign: strokeAlign, strokeCap: strokeCap, trackGap: trackGap, - year2023: widget.year2023, + year2023: year2023, ), ), ); diff --git a/packages/flutter/lib/src/material/progress_indicator_theme.dart b/packages/flutter/lib/src/material/progress_indicator_theme.dart index 2a6f7f4eb2..7a4c415f58 100644 --- a/packages/flutter/lib/src/material/progress_indicator_theme.dart +++ b/packages/flutter/lib/src/material/progress_indicator_theme.dart @@ -48,6 +48,11 @@ class ProgressIndicatorThemeData with Diagnosticable { this.constraints, this.trackGap, this.circularTrackPadding, + @Deprecated( + 'Use ProgressIndicatorTheme to customize the ProgressIndicator appearance. ' + 'This feature was deprecated after v3.27.0-0.2.pre.' + ) + this.year2023, }); /// The color of the [ProgressIndicator]'s indicator. @@ -114,6 +119,23 @@ class ProgressIndicatorThemeData with Diagnosticable { /// Overrides the padding of the [CircularProgressIndicator]. final EdgeInsetsGeometry? circularTrackPadding; + /// Overrides the [CircularProgressIndicator.year2023] and + /// [LinearProgressIndicator.year2023] properties. + /// + /// When true, the [CircularProgressIndicator] and [LinearProgressIndicator] + /// will use the 2023 Material Design 3 appearance. Defaults to true. + /// + /// If this is set to false, the [CircularProgressIndicator] and + /// [LinearProgressIndicator] will use the latest Material Design 3 appearance, + /// which was introduced in December 2023. + /// + /// If [ThemeData.useMaterial3] is false, then this property is ignored. + @Deprecated( + 'Use ProgressIndicatorTheme to customize the ProgressIndicator appearance. ' + 'This feature was deprecated after v3.27.0-0.2.pre.' + ) + final bool? year2023; + /// Creates a copy of this object but with the given fields replaced with the /// new values. ProgressIndicatorThemeData copyWith({ @@ -131,6 +153,7 @@ class ProgressIndicatorThemeData with Diagnosticable { BoxConstraints? constraints, double? trackGap, EdgeInsetsGeometry? circularTrackPadding, + bool? year2023, }) { return ProgressIndicatorThemeData( color: color ?? this.color, @@ -147,6 +170,7 @@ class ProgressIndicatorThemeData with Diagnosticable { constraints: constraints ?? this.constraints, trackGap : trackGap ?? this.trackGap, circularTrackPadding: circularTrackPadding ?? this.circularTrackPadding, + year2023: year2023 ?? this.year2023, ); } @@ -172,6 +196,7 @@ class ProgressIndicatorThemeData with Diagnosticable { constraints: BoxConstraints.lerp(a?.constraints, b?.constraints, t), trackGap : lerpDouble(a?.trackGap, b?.trackGap, t), circularTrackPadding: EdgeInsetsGeometry.lerp(a?.circularTrackPadding, b?.circularTrackPadding, t), + year2023: t < 0.5 ? a?.year2023 : b?.year2023, ); } @@ -191,6 +216,7 @@ class ProgressIndicatorThemeData with Diagnosticable { constraints, trackGap, circularTrackPadding, + year2023, ); @override @@ -215,7 +241,8 @@ class ProgressIndicatorThemeData with Diagnosticable { && other.strokeCap == strokeCap && other.constraints == constraints && other.trackGap == trackGap - && other.circularTrackPadding == circularTrackPadding; + && other.circularTrackPadding == circularTrackPadding + && other.year2023 == year2023; } @override @@ -235,6 +262,7 @@ class ProgressIndicatorThemeData with Diagnosticable { properties.add(DiagnosticsProperty('constraints', constraints, defaultValue: null)); properties.add(DoubleProperty('trackGap', trackGap, defaultValue: null)); properties.add(DiagnosticsProperty('circularTrackPadding', circularTrackPadding, defaultValue: null)); + properties.add(DiagnosticsProperty('year2023', year2023, defaultValue: null)); } } diff --git a/packages/flutter/test/material/progress_indicator_test.dart b/packages/flutter/test/material/progress_indicator_test.dart index d38988e801..2ba267f2f3 100644 --- a/packages/flutter/test/material/progress_indicator_test.dart +++ b/packages/flutter/test/material/progress_indicator_test.dart @@ -1540,6 +1540,8 @@ void main() { rrect: RRect.fromLTRBR(100.0 + customTrackGap, 0.0, 200.0, 4.0, const Radius.circular(2.0)), color: theme.colorScheme.secondaryContainer, ) + // Stop indicator. + ..circle() // Active track. ..rrect( rrect: RRect.fromLTRBR(0.0, 0.0, 100.0, 4.0, const Radius.circular(2.0)), @@ -1557,6 +1559,8 @@ void main() { rrect: RRect.fromLTRBR(0.0, 0.0, 200.0, 4.0, const Radius.circular(2.0)), color: theme.colorScheme.secondaryContainer, ) + // Stop indicator. + ..circle() // Active indicator. ..rrect( rrect: RRect.fromLTRBR(0.0, 0.0, 100.0, 4.0, const Radius.circular(2.0)), diff --git a/packages/flutter/test/material/progress_indicator_theme_test.dart b/packages/flutter/test/material/progress_indicator_theme_test.dart index 5f1b153cce..c799057373 100644 --- a/packages/flutter/test/material/progress_indicator_theme_test.dart +++ b/packages/flutter/test/material/progress_indicator_theme_test.dart @@ -41,6 +41,7 @@ void main() { constraints: BoxConstraints.tightFor(width: 80.0, height: 80.0), trackGap: 16.0, circularTrackPadding: EdgeInsets.all(12.0), + year2023: false ).debugFillProperties(builder); final List description = builder.properties @@ -62,7 +63,8 @@ void main() { 'strokeCap: StrokeCap.butt', 'constraints: BoxConstraints(w=80.0, h=80.0)', 'trackGap: 16.0', - 'circularTrackPadding: EdgeInsets.all(12.0)' + 'circularTrackPadding: EdgeInsets.all(12.0)', + 'year2023: false', ])); }); @@ -361,4 +363,178 @@ void main() { matchesGoldenFile('circular_progress_indicator_theme_year2023_false.png'), ); }); + + testWidgets('Opt into 2024 CircularProgressIndicator appearance with ProgressIndicatorThemeData.year2023', (WidgetTester tester) async { + final ThemeData theme = ThemeData( + progressIndicatorTheme: const ProgressIndicatorThemeData( + year2023: false, + ), + ); + const EdgeInsetsGeometry padding = EdgeInsets.all(4.0); + await tester.pumpWidget( + MaterialApp( + theme: theme, + home: const Scaffold( + body: Center( + child: CircularProgressIndicator(value: 0.5), + ), + ), + ), + ); + + final Size indicatorBoxSize = tester.getSize(find.descendant( + of: find.byType(CircularProgressIndicator), + matching: find.byType(ConstrainedBox), + )); + expect( + tester.getSize(find.byType(CircularProgressIndicator)), + equals(Size( + indicatorBoxSize.width + padding.horizontal, + indicatorBoxSize.height + padding.vertical, + )), + ); + expect( + find.byType(CircularProgressIndicator), + paints + // Track. + ..arc( + rect: const Rect.fromLTRB(2.0, 2.0, 38.0, 38.0), + color: theme.colorScheme.secondaryContainer, + strokeWidth: 4.0, + strokeCap: StrokeCap.round, + style: PaintingStyle.stroke, + ) + // Active indicator. + ..arc( + rect: const Rect.fromLTRB(2.0, 2.0, 38.0, 38.0), + color: theme.colorScheme.primary, + strokeWidth: 4.0, + strokeCap: StrokeCap.round, + style: PaintingStyle.stroke, + ), + ); + await expectLater( + find.byType(CircularProgressIndicator), + matchesGoldenFile('circular_progress_indicator_theme_opt_into_2024.png'), + ); + }); + + testWidgets('CircularProgressIndicator.year2023 overrides ProgressIndicatorThemeData.year2023', (WidgetTester tester) async { + final ThemeData theme = ThemeData( + progressIndicatorTheme: const ProgressIndicatorThemeData( + year2023: false, + ), + ); + await tester.pumpWidget( + MaterialApp( + theme: theme, + home: const Scaffold( + body: Center( + child: CircularProgressIndicator( + year2023: true, + value: 0.5, + ), + ), + ), + ), + ); + + final Size indicatorBoxSize = tester.getSize(find.descendant( + of: find.byType(CircularProgressIndicator), + matching: find.byType(ConstrainedBox), + )); + expect( + tester.getSize(find.byType(CircularProgressIndicator)), + equals(indicatorBoxSize), + ); + expect( + find.byType(CircularProgressIndicator), + paints + // Active indicator. + ..arc( + rect: const Rect.fromLTRB(-0.0, -0.0, 36.0, 36.0), + color: theme.colorScheme.primary, + strokeWidth: 4.0, + style: PaintingStyle.stroke, + ), + ); + await expectLater( + find.byType(CircularProgressIndicator), + matchesGoldenFile('circular_progress_indicator_theme_opt_into_2024_override.png'), + ); + }); + + testWidgets('Opt into 2024 LinearProgressIndicator appearance with ProgressIndicatorThemeData.year2023', (WidgetTester tester) async { + final ThemeData theme = ThemeData( + progressIndicatorTheme: const ProgressIndicatorThemeData( + year2023: false, + ), + ); + const double defaultTrackGap = 4.0; + await tester.pumpWidget( + MaterialApp( + theme: theme, + home: const Scaffold( + body: SizedBox( + width: 200.0, + child: LinearProgressIndicator(value: 0.5), + ), + ), + ), + ); + + expect( + find.byType(LinearProgressIndicator), + paints + // Track. + ..rrect( + rrect: RRect.fromLTRBR(100.0 + defaultTrackGap, 0.0, 200.0, 4.0, const Radius.circular(2.0)), + color: theme.colorScheme.secondaryContainer, + ) + // Stop indicator. + ..circle(x: 198.0, y: 2.0, radius: 2.0, color: theme.colorScheme.primary) + // Active track. + ..rrect( + rrect: RRect.fromLTRBR(0.0, 0.0, 100.0, 4.0, const Radius.circular(2.0)), + color: theme.colorScheme.primary, + ), + ); + }); + + testWidgets('LinearProgressIndicator.year2023 overrides ProgressIndicatorThemeData.year2023', (WidgetTester tester) async { + final ThemeData theme = ThemeData( + progressIndicatorTheme: const ProgressIndicatorThemeData( + year2023: false, + ), + ); + await tester.pumpWidget( + MaterialApp( + theme: theme, + home: const Scaffold( + body: SizedBox( + width: 200.0, + child: LinearProgressIndicator( + year2023: true, + value: 0.5, + ), + ), + ), + ), + ); + + expect( + find.byType(LinearProgressIndicator), + paints + // Track. + ..rect( + rect: const Rect.fromLTRB(0.0, 0.0, 200.0, 4.0), + color: theme.colorScheme.secondaryContainer, + ) + // Active track. + ..rect( + rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 4.0), + color: theme.colorScheme.primary, + ), + ); + }); }