From a6d3bb5cb4c7c2ff54288b1a0da9508e3837af74 Mon Sep 17 00:00:00 2001 From: Taha Tesser Date: Thu, 5 Dec 2024 19:54:54 +0200 Subject: [PATCH] Updated Material 3 Progress Indicators Samples (#158925) Fixes [Update both `ProgressIndicator` for Material 3 redesign](https://github.com/flutter/flutter/issues/141340) > [!IMPORTANT] > ~~This to be merged after https://github.com/flutter/flutter/pull/158104.~~ Merged. ### Description This updates progress indicator samples to include toggle to opt in to the updated Material 3 appearance . ### Preview Screenshot 2024-12-04 at 15 54 50 Screenshot 2024-12-04 at 15 58 35 ## 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 --- .../circular_progress_indicator.0.dart | 49 +++++++++------ .../circular_progress_indicator.1.dart | 9 ++- .../linear_progress_indicator.0.dart | 43 +++++++++----- .../linear_progress_indicator.1.dart | 3 +- .../circular_progress_indicator.0_test.dart | 51 +++++++++++++--- .../circular_progress_indicator.1_test.dart | 2 +- .../linear_progress_indicator.0_test.dart | 59 ++++++++++++------- .../lib/src/material/progress_indicator.dart | 6 +- 8 files changed, 153 insertions(+), 69 deletions(-) diff --git a/examples/api/lib/material/progress_indicator/circular_progress_indicator.0.dart b/examples/api/lib/material/progress_indicator/circular_progress_indicator.0.dart index a41036a8c4..af695cea4b 100644 --- a/examples/api/lib/material/progress_indicator/circular_progress_indicator.0.dart +++ b/examples/api/lib/material/progress_indicator/circular_progress_indicator.0.dart @@ -6,10 +6,10 @@ import 'package:flutter/material.dart'; /// Flutter code sample for [CircularProgressIndicator]. -void main() => runApp(const ProgressIndicatorApp()); +void main() => runApp(const ProgressIndicatorExampleApp()); -class ProgressIndicatorApp extends StatelessWidget { - const ProgressIndicatorApp({super.key}); +class ProgressIndicatorExampleApp extends StatelessWidget { + const ProgressIndicatorExampleApp({super.key}); @override Widget build(BuildContext context) { @@ -28,19 +28,18 @@ class ProgressIndicatorExample extends StatefulWidget { class _ProgressIndicatorExampleState extends State with TickerProviderStateMixin { late AnimationController controller; + bool year2023 = true; @override void initState() { + super.initState(); controller = AnimationController( - /// [AnimationController]s can be created with `vsync: this` because of - /// [TickerProviderStateMixin]. vsync: this, duration: const Duration(seconds: 5), )..addListener(() { - setState(() {}); - }); - controller.repeat(reverse: true); - super.initState(); + setState(() {}); + }) + ..repeat(reverse: true); } @override @@ -52,18 +51,32 @@ class _ProgressIndicatorExampleState extends State wit @override Widget build(BuildContext context) { return Scaffold( - body: Padding( - padding: const EdgeInsets.all(20.0), + body: Center( child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, + spacing: 16.0, + mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - 'Circular progress indicator with a fixed color', - style: Theme.of(context).textTheme.titleLarge, + const Text('Determinate CircularProgressIndicator'), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: CircularProgressIndicator( + year2023: year2023, + value: controller.value, + ), ), - CircularProgressIndicator( - value: controller.value, - semanticsLabel: 'Circular progress indicator', + const Text('Indeterminate CircularProgressIndicator'), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: CircularProgressIndicator(year2023: year2023), + ), + SwitchListTile( + value: year2023, + title: year2023 ? const Text('Switch to latest M3 style') : const Text('Switch to year2023 M3 style'), + onChanged: (bool value) { + setState(() { + year2023 = !year2023; + }); + }, ), ], ), diff --git a/examples/api/lib/material/progress_indicator/circular_progress_indicator.1.dart b/examples/api/lib/material/progress_indicator/circular_progress_indicator.1.dart index 6b228f89e2..2398b6747d 100644 --- a/examples/api/lib/material/progress_indicator/circular_progress_indicator.1.dart +++ b/examples/api/lib/material/progress_indicator/circular_progress_indicator.1.dart @@ -6,10 +6,10 @@ import 'package:flutter/material.dart'; /// Flutter code sample for [CircularProgressIndicator]. -void main() => runApp(const ProgressIndicatorApp()); +void main() => runApp(const ProgressIndicatorExampleApp()); -class ProgressIndicatorApp extends StatelessWidget { - const ProgressIndicatorApp({super.key}); +class ProgressIndicatorExampleApp extends StatelessWidget { + const ProgressIndicatorExampleApp({super.key}); @override Widget build(BuildContext context) { @@ -57,18 +57,17 @@ class _ProgressIndicatorExampleState extends State wit body: Padding( padding: const EdgeInsets.all(20.0), child: Column( + spacing: 16.0, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Circular progress indicator', style: Theme.of(context).textTheme.titleLarge, ), - const SizedBox(height: 30), CircularProgressIndicator( value: controller.value, semanticsLabel: 'Circular progress indicator', ), - const SizedBox(height: 10), Row( children: [ Expanded( diff --git a/examples/api/lib/material/progress_indicator/linear_progress_indicator.0.dart b/examples/api/lib/material/progress_indicator/linear_progress_indicator.0.dart index 2d7a626d83..2c04e1e8a9 100644 --- a/examples/api/lib/material/progress_indicator/linear_progress_indicator.0.dart +++ b/examples/api/lib/material/progress_indicator/linear_progress_indicator.0.dart @@ -27,9 +27,9 @@ class ProgressIndicatorExample extends StatefulWidget { _ProgressIndicatorExampleState(); } -class _ProgressIndicatorExampleState extends State - with TickerProviderStateMixin { +class _ProgressIndicatorExampleState extends State with TickerProviderStateMixin { late AnimationController controller; + bool year2023 = true; @override void initState() { @@ -54,18 +54,33 @@ class _ProgressIndicatorExampleState extends State @override Widget build(BuildContext context) { return Scaffold( - body: Padding( - padding: const EdgeInsets.all(20.0), - child: Column( - spacing: 16.0, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Determinate LinearProgressIndicator'), - LinearProgressIndicator(value: controller.value), - const Text('Indeterminate LinearProgressIndicator'), - const LinearProgressIndicator(), - ], - ), + body: Column( + spacing: 16.0, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Determinate LinearProgressIndicator'), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: LinearProgressIndicator( + year2023: year2023, + value: controller.value, + ), + ), + const Text('Indeterminate LinearProgressIndicator'), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: LinearProgressIndicator(year2023: year2023), + ), + SwitchListTile( + value: year2023, + title: year2023 ? const Text('Switch to latest M3 style') : const Text('Switch to year2023 M3 style'), + onChanged: (bool value) { + setState(() { + year2023 = !year2023; + }); + }, + ), + ], ), ); } diff --git a/examples/api/lib/material/progress_indicator/linear_progress_indicator.1.dart b/examples/api/lib/material/progress_indicator/linear_progress_indicator.1.dart index 6663c60b43..f8422c4c7b 100644 --- a/examples/api/lib/material/progress_indicator/linear_progress_indicator.1.dart +++ b/examples/api/lib/material/progress_indicator/linear_progress_indicator.1.dart @@ -58,18 +58,17 @@ class _ProgressIndicatorExampleState extends State body: Padding( padding: const EdgeInsets.all(20.0), child: Column( + spacing: 16.0, mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( 'Linear progress indicator', style: TextStyle(fontSize: 20), ), - const SizedBox(height: 30), LinearProgressIndicator( value: determinate ? controller.value : null, semanticsLabel: 'Linear progress indicator', ), - const SizedBox(height: 10), Row( children: [ Expanded( diff --git a/examples/api/test/material/progress_indicator/circular_progress_indicator.0_test.dart b/examples/api/test/material/progress_indicator/circular_progress_indicator.0_test.dart index 787acd9816..b51cabf519 100644 --- a/examples/api/test/material/progress_indicator/circular_progress_indicator.0_test.dart +++ b/examples/api/test/material/progress_indicator/circular_progress_indicator.0_test.dart @@ -2,23 +2,58 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/material.dart'; import 'package:flutter_api_samples/material/progress_indicator/circular_progress_indicator.0.dart' as example; import 'package:flutter_test/flutter_test.dart'; void main() { - testWidgets('Finds CircularProgressIndicator', (WidgetTester tester) async { + testWidgets('Determinate CircularProgressIndicator uses the provided value', (WidgetTester tester) async { await tester.pumpWidget( - const example.ProgressIndicatorApp(), + const example.ProgressIndicatorExampleApp(), + ); + await tester.pump(const Duration(milliseconds: 2500)); + + final Finder indicatorFinder = find.byType(CircularProgressIndicator).first; + final CircularProgressIndicator progressIndicator = tester.widget(indicatorFinder); + expect(progressIndicator.value, equals(0.5)); + }); + + testWidgets('Indeterminate CircularProgressIndicator does not have a value', (WidgetTester tester) async { + await tester.pumpWidget( + const example.ProgressIndicatorExampleApp(), + ); + await tester.pump(const Duration(milliseconds: 2500)); + + final Finder indicatorFinder = find.byType(CircularProgressIndicator).last; + final CircularProgressIndicator progressIndicator = tester.widget(indicatorFinder); + expect(progressIndicator.value, null); + }); + + testWidgets('Progress indicators year2023 flag can be toggled', (WidgetTester tester) async { + await tester.pumpWidget( + const example.ProgressIndicatorExampleApp(), ); - expect( - find.bySemanticsLabel('Circular progress indicator'), - findsOneWidget, + CircularProgressIndicator determinateIndicator = tester.widget( + find.byType(CircularProgressIndicator).first, ); + expect(determinateIndicator.year2023, true); + CircularProgressIndicator indeterminateIndicator = tester.widget( + find.byType(CircularProgressIndicator).last, + ); + expect(indeterminateIndicator.year2023, true); - // Test if CircularProgressIndicator is animating. - await tester.pump(const Duration(seconds: 2)); - expect(tester.hasRunningAnimations, isTrue); + await tester.tap(find.byType(SwitchListTile)); + await tester.pump(); + + determinateIndicator = tester.widget( + find.byType(CircularProgressIndicator).first, + ); + expect(determinateIndicator.year2023, false); + indeterminateIndicator = tester.widget( + find.byType(CircularProgressIndicator).last, + ); + expect(indeterminateIndicator.year2023, false); }); } diff --git a/examples/api/test/material/progress_indicator/circular_progress_indicator.1_test.dart b/examples/api/test/material/progress_indicator/circular_progress_indicator.1_test.dart index 8078c63c35..28ee53d40a 100644 --- a/examples/api/test/material/progress_indicator/circular_progress_indicator.1_test.dart +++ b/examples/api/test/material/progress_indicator/circular_progress_indicator.1_test.dart @@ -10,7 +10,7 @@ import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets('Finds CircularProgressIndicator', (WidgetTester tester) async { await tester.pumpWidget( - const example.ProgressIndicatorApp(), + const example.ProgressIndicatorExampleApp(), ); expect( diff --git a/examples/api/test/material/progress_indicator/linear_progress_indicator.0_test.dart b/examples/api/test/material/progress_indicator/linear_progress_indicator.0_test.dart index 5f8b019f83..881b5139e6 100644 --- a/examples/api/test/material/progress_indicator/linear_progress_indicator.0_test.dart +++ b/examples/api/test/material/progress_indicator/linear_progress_indicator.0_test.dart @@ -8,33 +8,52 @@ import 'package:flutter_api_samples/material/progress_indicator/linear_progress_ import 'package:flutter_test/flutter_test.dart'; void main() { - testWidgets('Determinate and Indeterminate LinearProgressIndicators', - (WidgetTester tester) async { + testWidgets('Determinate LinearProgressIndicator uses the provided value', (WidgetTester tester) async { + await tester.pumpWidget( + const example.ProgressIndicatorExampleApp(), + ); + await tester.pump(const Duration(milliseconds: 2500)); + + final Finder indicatorFinder = find.byType(LinearProgressIndicator).first; + final LinearProgressIndicator progressIndicator = tester.widget(indicatorFinder); + expect(progressIndicator.value, equals(0.5)); + }); + + testWidgets('Indeterminate LinearProgressIndicator does not have a value', (WidgetTester tester) async { + await tester.pumpWidget( + const example.ProgressIndicatorExampleApp(), + ); + await tester.pump(const Duration(milliseconds: 2500)); + + final Finder indicatorFinder = find.byType(LinearProgressIndicator).last; + final LinearProgressIndicator progressIndicator = tester.widget(indicatorFinder); + expect(progressIndicator.value, null); + }); + + testWidgets('Progress indicators year2023 flag can be toggled', (WidgetTester tester) async { await tester.pumpWidget( const example.ProgressIndicatorExampleApp(), ); - expect(find.text('Determinate LinearProgressIndicator'), findsOneWidget); - expect(find.text('Indeterminate LinearProgressIndicator'), findsOneWidget); - expect(find.byType(LinearProgressIndicator), findsNWidgets(2)); - - // Test determinate LinearProgressIndicator. - LinearProgressIndicator determinateIndicator = tester.firstWidget( + LinearProgressIndicator determinateIndicator = tester.widget( find.byType(LinearProgressIndicator).first, ); - expect(determinateIndicator.value, equals(0.0)); - - // Advance the animation by 2 seconds. - await tester.pump(const Duration(seconds: 2)); - determinateIndicator = tester.firstWidget( - find.byType(LinearProgressIndicator).first, - ); - expect(determinateIndicator.value, equals(0.4)); - - // Test indeterminate LinearProgressIndicator. - final LinearProgressIndicator indeterminateIndicator = tester.firstWidget( + expect(determinateIndicator.year2023, true); + LinearProgressIndicator indeterminateIndicator = tester.widget( find.byType(LinearProgressIndicator).last, ); - expect(indeterminateIndicator.value, null); + expect(indeterminateIndicator.year2023, true); + + await tester.tap(find.byType(SwitchListTile)); + await tester.pump(); + + determinateIndicator = tester.widget( + find.byType(LinearProgressIndicator).first, + ); + expect(determinateIndicator.year2023, false); + indeterminateIndicator = tester.widget( + find.byType(LinearProgressIndicator).last, + ); + expect(indeterminateIndicator.year2023, false); }); } diff --git a/packages/flutter/lib/src/material/progress_indicator.dart b/packages/flutter/lib/src/material/progress_indicator.dart index b92922244b..891773e721 100644 --- a/packages/flutter/lib/src/material/progress_indicator.dart +++ b/packages/flutter/lib/src/material/progress_indicator.dart @@ -312,6 +312,8 @@ class _LinearProgressIndicatorPainter extends CustomPainter { /// /// {@tool dartpad} /// This example showcases determinate and indeterminate [LinearProgressIndicator]s. +/// The [LinearProgressIndicator]s will use the ![updated Material 3 Design appearance](https://m3.material.io/components/progress-indicators/overview) +/// when setting the [LinearProgressIndicator.year2023] flag to false. /// /// ** See code in examples/api/lib/material/progress_indicator/linear_progress_indicator.0.dart ** /// {@end-tool} @@ -704,7 +706,9 @@ class _CircularProgressIndicatorPainter extends CustomPainter { /// specify a constant color use: `AlwaysStoppedAnimation(color)`. /// /// {@tool dartpad} -/// This example shows a [CircularProgressIndicator] with a changing value. +/// This example showcases determinate and indeterminate [CircularProgressIndicator]s. +/// The [CircularProgressIndicator]s will use the ![updated Material 3 Design appearance](https://m3.material.io/components/progress-indicators/overview) +/// when setting the [CircularProgressIndicator.year2023] flag to false. /// /// ** See code in examples/api/lib/material/progress_indicator/circular_progress_indicator.0.dart ** /// {@end-tool}