Feat: Add a11y for loading indicators (#165173)

Feat: Add a11y for loading indicators
fixes: #161631 

## 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.
diff --git a/engine/src/flutter/lib/ui/fixtures/ui_test.dart b/engine/src/flutter/lib/ui/fixtures/ui_test.dart
index 24da51e..ce6af8e 100644
--- a/engine/src/flutter/lib/ui/fixtures/ui_test.dart
+++ b/engine/src/flutter/lib/ui/fixtures/ui_test.dart
@@ -258,6 +258,8 @@
     controlsNodes: null,
     inputType: SemanticsInputType.none,
     locale: null,
+    minValue: '0',
+    maxValue: '0',
   );
   _semanticsUpdate(builder.build());
 }
@@ -319,6 +321,8 @@
     controlsNodes: null,
     inputType: SemanticsInputType.none,
     locale: null,
+    minValue: '0',
+    maxValue: '0',
   );
   _semanticsUpdate(builder.build());
 }
@@ -380,6 +384,8 @@
     controlsNodes: null,
     inputType: SemanticsInputType.none,
     locale: Locale('es', 'MX'),
+    minValue: '0',
+    maxValue: '0',
   );
   _semanticsUpdate(builder.build());
 }
@@ -436,6 +442,8 @@
     controlsNodes: null,
     inputType: SemanticsInputType.none,
     locale: Locale('es', 'MX'),
+    minValue: '0',
+    maxValue: '0',
   );
   _semanticsUpdate(builder.build());
 }
diff --git a/engine/src/flutter/lib/ui/semantics.dart b/engine/src/flutter/lib/ui/semantics.dart
index 8d496fa..d5db605 100644
--- a/engine/src/flutter/lib/ui/semantics.dart
+++ b/engine/src/flutter/lib/ui/semantics.dart
@@ -1975,6 +1975,8 @@
     SemanticsHitTestBehavior hitTestBehavior = SemanticsHitTestBehavior.defer,
     required SemanticsInputType inputType,
     required Locale? locale,
+    required String minValue,
+    required String maxValue,
   });
 
   /// Update the custom semantics action associated with the given `id`.
@@ -2056,6 +2058,8 @@
     SemanticsHitTestBehavior hitTestBehavior = SemanticsHitTestBehavior.defer,
     required SemanticsInputType inputType,
     required Locale? locale,
+    required String minValue,
+    required String maxValue,
   }) {
     assert(_matrix4IsValid(transform));
     assert(
@@ -2107,6 +2111,8 @@
       hitTestBehavior.index,
       inputType.index,
       locale?.toLanguageTag() ?? '',
+      minValue,
+      maxValue,
     );
   }
 
@@ -2157,6 +2163,8 @@
       Int32,
       Int32,
       Handle,
+      Handle,
+      Handle,
     )
   >(symbol: 'SemanticsUpdateBuilder::updateNode')
   external void _updateNode(
@@ -2204,6 +2212,8 @@
     int hitTestBehaviorIndex,
     int inputType,
     String locale,
+    String minValue,
+    String maxValue,
   );
 
   @override
diff --git a/engine/src/flutter/lib/ui/semantics/semantics_node.h b/engine/src/flutter/lib/ui/semantics/semantics_node.h
index 6bbf7e0..1bdec7c 100644
--- a/engine/src/flutter/lib/ui/semantics/semantics_node.h
+++ b/engine/src/flutter/lib/ui/semantics/semantics_node.h
@@ -145,6 +145,8 @@
   double scrollPosition = std::nan("");
   double scrollExtentMax = std::nan("");
   double scrollExtentMin = std::nan("");
+  std::string minValue;
+  std::string maxValue;
   std::string identifier;
   std::string label;
   StringAttributes labelAttributes;
diff --git a/engine/src/flutter/lib/ui/semantics/semantics_update_builder.cc b/engine/src/flutter/lib/ui/semantics/semantics_update_builder.cc
index a2cc6d6..b23096c 100644
--- a/engine/src/flutter/lib/ui/semantics/semantics_update_builder.cc
+++ b/engine/src/flutter/lib/ui/semantics/semantics_update_builder.cc
@@ -74,7 +74,9 @@
     int validationResult,
     int hitTestBehavior,
     int inputType,
-    std::string locale) {
+    std::string locale,
+    std::string minValue,
+    std::string maxValue) {
   FML_CHECK(scrollChildren == 0 ||
             (scrollChildren > 0 && childrenInHitTestOrder.data()))
       << "Semantics update contained scrollChildren but did not have "
@@ -96,6 +98,8 @@
   node.scrollPosition = scrollPosition;
   node.scrollExtentMax = scrollExtentMax;
   node.scrollExtentMin = scrollExtentMin;
+  node.minValue = std::move(minValue);
+  node.maxValue = std::move(maxValue);
   node.rect = SkRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top),
                                SafeNarrow(right), SafeNarrow(bottom));
   node.identifier = std::move(identifier);
diff --git a/engine/src/flutter/lib/ui/semantics/semantics_update_builder.h b/engine/src/flutter/lib/ui/semantics/semantics_update_builder.h
index 14dfcca..e53b9c0 100644
--- a/engine/src/flutter/lib/ui/semantics/semantics_update_builder.h
+++ b/engine/src/flutter/lib/ui/semantics/semantics_update_builder.h
@@ -73,7 +73,9 @@
       int validationResult,
       int hitTestBehavior,
       int inputType,
-      std::string locale);
+      std::string locale,
+      std::string minValue,
+      std::string maxValue);
 
   void updateCustomAction(int id,
                           std::string label,
diff --git a/engine/src/flutter/lib/web_ui/lib/semantics.dart b/engine/src/flutter/lib/web_ui/lib/semantics.dart
index 7435290..395cbc4 100644
--- a/engine/src/flutter/lib/web_ui/lib/semantics.dart
+++ b/engine/src/flutter/lib/web_ui/lib/semantics.dart
@@ -753,6 +753,8 @@
     SemanticsHitTestBehavior hitTestBehavior = SemanticsHitTestBehavior.defer,
     required SemanticsInputType inputType,
     required Locale? locale,
+    required String minValue,
+    required String maxValue,
   }) {
     if (transform.length != 16) {
       throw ArgumentError('transform argument must have 16 entries.');
@@ -800,6 +802,8 @@
         hitTestBehavior: hitTestBehavior,
         inputType: inputType,
         locale: locale,
+        minValue: minValue,
+        maxValue: maxValue,
       ),
     );
   }
diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine.dart b/engine/src/flutter/lib/web_ui/lib/src/engine.dart
index d56b9ec..1eac876 100644
--- a/engine/src/flutter/lib/web_ui/lib/src/engine.dart
+++ b/engine/src/flutter/lib/web_ui/lib/src/engine.dart
@@ -116,6 +116,7 @@
 export 'engine/semantics/live_region.dart';
 export 'engine/semantics/menus.dart';
 export 'engine/semantics/platform_view.dart';
+export 'engine/semantics/progress_bar.dart';
 export 'engine/semantics/requirable.dart';
 export 'engine/semantics/route.dart';
 export 'engine/semantics/scrollable.dart';
diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/semantics.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/semantics.dart
index 600fc3d..ea30b9e 100644
--- a/engine/src/flutter/lib/web_ui/lib/src/engine/semantics.dart
+++ b/engine/src/flutter/lib/web_ui/lib/src/engine/semantics.dart
@@ -20,6 +20,7 @@
 export 'semantics/live_region.dart';
 export 'semantics/menus.dart';
 export 'semantics/platform_view.dart';
+export 'semantics/progress_bar.dart';
 export 'semantics/requirable.dart';
 export 'semantics/scrollable.dart';
 export 'semantics/semantics.dart';
diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/semantics/progress_bar.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/semantics/progress_bar.dart
new file mode 100644
index 0000000..c5e40d1
--- /dev/null
+++ b/engine/src/flutter/lib/web_ui/lib/src/engine/semantics/progress_bar.dart
@@ -0,0 +1,66 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+import 'label_and_value.dart';
+import 'semantics.dart';
+
+/// Indicates a progress bar element.
+///
+/// Uses aria progressbar role to convey this semantic information to the element.
+///
+/// Screen-readers take advantage of "aria-label" to describe the visual.
+class SemanticsProgressBar extends SemanticRole {
+  SemanticsProgressBar(SemanticsObject semanticsObject)
+    : super.withBasics(
+        EngineSemanticsRole.progressBar,
+        semanticsObject,
+        preferredLabelRepresentation: LabelRepresentation.ariaLabel,
+      ) {
+    setAriaRole('progressbar');
+
+    // Set ARIA attributes for min, max and current value.
+    if (semanticsObject.minValue != null) {
+      setAttribute('aria-valuemin', semanticsObject.minValue!);
+    }
+    if (semanticsObject.maxValue != null) {
+      setAttribute('aria-valuemax', semanticsObject.maxValue!);
+    }
+
+    if (semanticsObject.value != null) {
+      setAttribute('aria-valuenow', semanticsObject.value!);
+    }
+  }
+
+  @override
+  void update() {
+    super.update();
+
+    if (semanticsObject.minValue != null) {
+      setAttribute('aria-valuemin', semanticsObject.minValue!);
+    }
+
+    if (semanticsObject.maxValue != null) {
+      setAttribute('aria-valuemax', semanticsObject.maxValue!);
+    }
+
+    if (semanticsObject.value != null) {
+      setAttribute('aria-valuenow', semanticsObject.value!);
+    }
+  }
+
+  @override
+  bool focusAsRouteDefault() => focusable?.focusAsRouteDefault() ?? false;
+}
+
+/// Indicates a loading spinner element.
+class SementicsLoadingSpinner extends SemanticRole {
+  SementicsLoadingSpinner(SemanticsObject semanticsObject)
+    : super.withBasics(
+        EngineSemanticsRole.loadingSpinner,
+        semanticsObject,
+        preferredLabelRepresentation: LabelRepresentation.ariaLabel,
+      );
+
+  @override
+  bool focusAsRouteDefault() => focusable?.focusAsRouteDefault() ?? false;
+}
diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/semantics/semantics.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/semantics/semantics.dart
index e68c414..01cca11 100644
--- a/engine/src/flutter/lib/web_ui/lib/src/engine/semantics/semantics.dart
+++ b/engine/src/flutter/lib/web_ui/lib/src/engine/semantics/semantics.dart
@@ -37,6 +37,7 @@
 import 'live_region.dart';
 import 'menus.dart';
 import 'platform_view.dart';
+import 'progress_bar.dart';
 import 'requirable.dart';
 import 'route.dart';
 import 'scrollable.dart';
@@ -275,6 +276,8 @@
     this.hitTestBehavior = ui.SemanticsHitTestBehavior.defer,
     required this.inputType,
     required this.locale,
+    required this.minValue,
+    required this.maxValue,
   });
 
   /// See [ui.SemanticsUpdateBuilder.updateNode].
@@ -399,6 +402,12 @@
 
   /// See [ui.SemanticsUpdateBuilder.updateNode].
   final ui.Locale? locale;
+
+  /// See [ui.SemanticsUpdateBuilder.updateNode].
+  final String minValue;
+
+  /// See [ui.SemanticsUpdateBuilder.updateNode].
+  final String maxValue;
 }
 
 /// Identifies [SemanticRole] implementations.
@@ -503,6 +512,12 @@
   /// An item in a [list].
   listItem,
 
+  /// A graphic object that shows progress with a numeric number.
+  progressBar,
+
+  /// A graphic object that spins to indicate the application is busy.
+  loadingSpinner,
+
   /// A role used when a more specific role cannot be assigend to
   /// a [SemanticsObject].
   ///
@@ -1550,6 +1565,31 @@
     _dirtyFields |= _hitTestBehaviorIndex;
   }
 
+  String? get minValue => _minValue;
+  String? _minValue;
+
+  static const int _minValueIndex = 1 << 29;
+
+  /// Whether the [minValue] field has been updated but has not been
+  /// applied to the DOM yet.
+  bool get isMinValueDirty => _isDirty(_minValueIndex);
+  void _markMinValueDirty() {
+    _dirtyFields |= _minValueIndex;
+  }
+
+  /// See [ui.SemanticsUpdateBuilder.updateNode].
+  String? get maxValue => _maxValue;
+  String? _maxValue;
+
+  static const int _maxValueIndex = 1 << 30;
+
+  /// Whether the [maxValue] field has been updated but has not been
+  /// applied to the DOM yet.
+  bool get isMaxValueDirty => _isDirty(_maxValueIndex);
+  void _markMaxValueDirty() {
+    _dirtyFields |= _maxValueIndex;
+  }
+
   /// A unique permanent identifier of the semantics node in the tree.
   final int id;
 
@@ -1887,6 +1927,16 @@
       _markHitTestBehaviorDirty();
     }
 
+    if (_minValue != update.minValue) {
+      _minValue = update.minValue;
+      _markMinValueDirty();
+    }
+
+    if (_maxValue != update.maxValue) {
+      _maxValue = update.maxValue;
+      _markMaxValueDirty();
+    }
+
     role = update.role;
 
     inputType = update.inputType;
@@ -2139,14 +2189,16 @@
         return EngineSemanticsRole.region;
       case ui.SemanticsRole.form:
         return EngineSemanticsRole.form;
+      case ui.SemanticsRole.loadingSpinner:
+        return EngineSemanticsRole.loadingSpinner;
+      case ui.SemanticsRole.progressBar:
+        return EngineSemanticsRole.progressBar;
       // TODO(chunhtai): implement these roles.
       // https://github.com/flutter/flutter/issues/159741.
       case ui.SemanticsRole.dragHandle:
       case ui.SemanticsRole.spinButton:
       case ui.SemanticsRole.comboBox:
       case ui.SemanticsRole.tooltip:
-      case ui.SemanticsRole.loadingSpinner:
-      case ui.SemanticsRole.progressBar:
       case ui.SemanticsRole.hotKey:
       case ui.SemanticsRole.none:
       // fallback to checking semantics properties.
@@ -2213,6 +2265,8 @@
       EngineSemanticsRole.menuItemRadio => SemanticMenuItemRadio(this),
       EngineSemanticsRole.alert => SemanticAlert(this),
       EngineSemanticsRole.status => SemanticStatus(this),
+      EngineSemanticsRole.progressBar => SemanticsProgressBar(this),
+      EngineSemanticsRole.loadingSpinner => SementicsLoadingSpinner(this),
       EngineSemanticsRole.generic => GenericRole(this),
       EngineSemanticsRole.complementary => SemanticComplementary(this),
       EngineSemanticsRole.contentInfo => SemanticContentInfo(this),
diff --git a/engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_test.dart b/engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_test.dart
index 704da94..990c3af 100644
--- a/engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_test.dart
+++ b/engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_test.dart
@@ -160,6 +160,12 @@
   group('forms', () {
     _testForms();
   });
+  group('progressBar', () {
+    _testProgressBar();
+  });
+  group('loadingSpinner', () {
+    _testLoadingSpinner();
+  });
 }
 
 void _testSemanticRole() {
@@ -6153,6 +6159,55 @@
   semantics().semanticsEnabled = false;
 }
 
+void _testProgressBar() {
+  test('nodes with progress bar role', () {
+    semantics()
+      ..debugOverrideTimestampFunction(() => _testTime)
+      ..semanticsEnabled = true;
+
+    SemanticsObject pumpSemantics() {
+      final SemanticsTester tester = SemanticsTester(owner());
+      tester.updateNode(
+        id: 0,
+        role: ui.SemanticsRole.progressBar,
+        rect: const ui.Rect.fromLTRB(0, 0, 100, 50),
+      );
+      tester.apply();
+      return tester.getSemanticsObject(0);
+    }
+
+    final SemanticsObject object = pumpSemantics();
+    expect(object.semanticRole?.kind, EngineSemanticsRole.progressBar);
+    expect(object.element.getAttribute('role'), 'progressbar');
+  });
+
+  semantics().semanticsEnabled = false;
+}
+
+void _testLoadingSpinner() {
+  test('nodes with loading spinner role', () {
+    semantics()
+      ..debugOverrideTimestampFunction(() => _testTime)
+      ..semanticsEnabled = true;
+
+    SemanticsObject pumpSemantics() {
+      final SemanticsTester tester = SemanticsTester(owner());
+      tester.updateNode(
+        id: 0,
+        role: ui.SemanticsRole.loadingSpinner,
+        rect: const ui.Rect.fromLTRB(0, 0, 100, 50),
+      );
+      tester.apply();
+      return tester.getSemanticsObject(0);
+    }
+
+    final SemanticsObject object = pumpSemantics();
+    expect(object.semanticRole?.kind, EngineSemanticsRole.loadingSpinner);
+  });
+
+  semantics().semanticsEnabled = false;
+}
+
 /// A facade in front of [ui.SemanticsUpdateBuilder.updateNode] that
 /// supplies default values for semantics attributes.
 void updateNode(
@@ -6197,6 +6252,8 @@
   ui.SemanticsHitTestBehavior hitTestBehavior = ui.SemanticsHitTestBehavior.defer,
   ui.SemanticsInputType inputType = ui.SemanticsInputType.none,
   ui.Locale? locale,
+  String minValue = '0',
+  String maxValue = '0',
 }) {
   transform ??= Float64List.fromList(Matrix4.identity().storage);
   hitTestTransform ??= Float64List.fromList(Matrix4.identity().storage);
@@ -6244,6 +6301,8 @@
     hitTestBehavior: hitTestBehavior,
     inputType: inputType,
     locale: locale,
+    minValue: minValue,
+    maxValue: maxValue,
   );
 }
 
diff --git a/engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_tester.dart b/engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_tester.dart
index ddc190e..f062265 100644
--- a/engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_tester.dart
+++ b/engine/src/flutter/lib/web_ui/test/engine/semantics/semantics_tester.dart
@@ -92,6 +92,8 @@
     ui.SemanticsHitTestBehavior hitTestBehavior = ui.SemanticsHitTestBehavior.defer,
     ui.SemanticsInputType inputType = ui.SemanticsInputType.none,
     ui.Locale? locale,
+    String? minValue,
+    String? maxValue,
   }) {
     // Actions
     if (hasTap ?? false) {
@@ -228,6 +230,8 @@
       hitTestBehavior: hitTestBehavior,
       inputType: inputType,
       locale: locale,
+      minValue: minValue ?? '0',
+      maxValue: maxValue ?? '0',
     );
     _nodeUpdates.add(update);
     return update;
diff --git a/engine/src/flutter/runtime/fixtures/runtime_test.dart b/engine/src/flutter/runtime/fixtures/runtime_test.dart
index f561835..ce20bf4 100644
--- a/engine/src/flutter/runtime/fixtures/runtime_test.dart
+++ b/engine/src/flutter/runtime/fixtures/runtime_test.dart
@@ -316,6 +316,8 @@
     controlsNodes: null,
     inputType: SemanticsInputType.none,
     locale: null,
+    minValue: '0',
+    maxValue: '0',
   );
   _semanticsUpdate(builder.build());
 }
diff --git a/engine/src/flutter/shell/platform/embedder/fixtures/main.dart b/engine/src/flutter/shell/platform/embedder/fixtures/main.dart
index 276df9d..6a12310 100644
--- a/engine/src/flutter/shell/platform/embedder/fixtures/main.dart
+++ b/engine/src/flutter/shell/platform/embedder/fixtures/main.dart
@@ -200,6 +200,8 @@
       controlsNodes: null,
       inputType: SemanticsInputType.none,
       locale: null,
+      minValue: '0',
+      maxValue: '0',
     )
     ..updateNode(
       id: 84,
@@ -238,6 +240,8 @@
       controlsNodes: null,
       inputType: SemanticsInputType.none,
       locale: null,
+      minValue: '0',
+      maxValue: '0',
     )
     ..updateNode(
       id: 96,
@@ -276,6 +280,8 @@
       controlsNodes: null,
       inputType: SemanticsInputType.none,
       locale: null,
+      minValue: '0',
+      maxValue: '0',
     )
     ..updateNode(
       id: 128,
@@ -314,6 +320,8 @@
       controlsNodes: null,
       inputType: SemanticsInputType.none,
       locale: null,
+      minValue: '0',
+      maxValue: '0',
     )
     ..updateCustomAction(id: 21, label: 'Archive', hint: 'archive message');
 
@@ -399,6 +407,8 @@
       controlsNodes: null,
       inputType: SemanticsInputType.none,
       locale: null,
+      minValue: '0',
+      maxValue: '0',
     );
 
   PlatformDispatcher.instance.setSemanticsTreeEnabled(true);
@@ -1690,6 +1700,8 @@
       controlsNodes: null,
       inputType: SemanticsInputType.none,
       locale: null,
+      minValue: '0',
+      maxValue: '0',
     );
   }
 
diff --git a/engine/src/flutter/shell/platform/windows/fixtures/main.dart b/engine/src/flutter/shell/platform/windows/fixtures/main.dart
index 39a80db..705aac4 100644
--- a/engine/src/flutter/shell/platform/windows/fixtures/main.dart
+++ b/engine/src/flutter/shell/platform/windows/fixtures/main.dart
@@ -478,6 +478,8 @@
       controlsNodes: null,
       inputType: ui.SemanticsInputType.none,
       locale: null,
+      minValue: '0',
+      maxValue: '0',
     );
     return builder.build();
   }
diff --git a/engine/src/flutter/testing/ios_scenario_app/lib/src/locale_initialization.dart b/engine/src/flutter/testing/ios_scenario_app/lib/src/locale_initialization.dart
index 607a0f6..4e27a36 100644
--- a/engine/src/flutter/testing/ios_scenario_app/lib/src/locale_initialization.dart
+++ b/engine/src/flutter/testing/ios_scenario_app/lib/src/locale_initialization.dart
@@ -77,6 +77,8 @@
         controlsNodes: null,
         inputType: SemanticsInputType.none,
         locale: null,
+        minValue: '0',
+        maxValue: '0',
       );
 
     final SemanticsUpdate semanticsUpdate = semanticsUpdateBuilder.build();
@@ -139,6 +141,8 @@
         controlsNodes: null,
         inputType: SemanticsInputType.none,
         locale: null,
+        minValue: '0',
+        maxValue: '0',
       );
 
     final SemanticsUpdate semanticsUpdate = semanticsUpdateBuilder.build();
diff --git a/packages/flutter/lib/src/material/progress_indicator.dart b/packages/flutter/lib/src/material/progress_indicator.dart
index b29452b..8d0616d 100644
--- a/packages/flutter/lib/src/material/progress_indicator.dart
+++ b/packages/flutter/lib/src/material/progress_indicator.dart
@@ -8,6 +8,7 @@
 library;
 
 import 'dart:math' as math;
+import 'dart:ui';
 
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/foundation.dart';
@@ -141,11 +142,20 @@
   }
 
   Widget _buildSemanticsWrapper({required BuildContext context, required Widget child}) {
+    bool isProgressBar = false;
     String? expandedSemanticsValue = semanticsValue;
     if (value != null) {
-      expandedSemanticsValue ??= '${(value! * 100).round()}%';
+      expandedSemanticsValue ??= '${(value! * 100).round()}';
+      isProgressBar = true;
     }
-    return Semantics(label: semanticsLabel, value: expandedSemanticsValue, child: child);
+    return Semantics(
+      label: semanticsLabel,
+      role: isProgressBar ? SemanticsRole.progressBar : SemanticsRole.loadingSpinner,
+      minValue: isProgressBar ? '0' : null,
+      maxValue: isProgressBar ? '100' : null,
+      value: expandedSemanticsValue,
+      child: child,
+    );
   }
 }
 
@@ -1185,28 +1195,32 @@
 
   @override
   Widget build(BuildContext context) {
-    switch (widget._indicatorType) {
-      case _ActivityIndicatorType.material:
-        if (widget.value != null) {
-          return _buildMaterialIndicator(context, 0.0, 0.0, 0, 0.0);
-        }
-        return _buildAnimation();
-      case _ActivityIndicatorType.adaptive:
-        final ThemeData theme = Theme.of(context);
-        switch (theme.platform) {
-          case TargetPlatform.iOS:
-          case TargetPlatform.macOS:
-            return _buildCupertinoIndicator(context);
-          case TargetPlatform.android:
-          case TargetPlatform.fuchsia:
-          case TargetPlatform.linux:
-          case TargetPlatform.windows:
+    return Builder(
+      builder: (BuildContext context) {
+        switch (widget._indicatorType) {
+          case _ActivityIndicatorType.material:
             if (widget.value != null) {
               return _buildMaterialIndicator(context, 0.0, 0.0, 0, 0.0);
             }
             return _buildAnimation();
+          case _ActivityIndicatorType.adaptive:
+            final ThemeData theme = Theme.of(context);
+            switch (theme.platform) {
+              case TargetPlatform.iOS:
+              case TargetPlatform.macOS:
+                return _buildCupertinoIndicator(context);
+              case TargetPlatform.android:
+              case TargetPlatform.fuchsia:
+              case TargetPlatform.linux:
+              case TargetPlatform.windows:
+                if (widget.value != null) {
+                  return _buildMaterialIndicator(context, 0.0, 0.0, 0, 0.0);
+                }
+                return _buildAnimation();
+            }
         }
-    }
+      },
+    );
   }
 }
 
diff --git a/packages/flutter/lib/src/rendering/custom_paint.dart b/packages/flutter/lib/src/rendering/custom_paint.dart
index a3d5a07..2173557 100644
--- a/packages/flutter/lib/src/rendering/custom_paint.dart
+++ b/packages/flutter/lib/src/rendering/custom_paint.dart
@@ -1051,6 +1051,12 @@
     if (properties.inputType != null) {
       config.inputType = properties.inputType!;
     }
+    if (properties.minValue != null) {
+      config.minValue = properties.minValue;
+    }
+    if (properties.maxValue != null) {
+      config.maxValue = properties.maxValue;
+    }
     if (properties.onTap != null) {
       config.onTap = properties.onTap;
     }
diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart
index 32669a3..52a1358 100644
--- a/packages/flutter/lib/src/rendering/object.dart
+++ b/packages/flutter/lib/src/rendering/object.dart
@@ -4938,6 +4938,12 @@
     if (_properties.inputType != null) {
       config.inputType = _properties.inputType!;
     }
+    if (_properties.minValue != null) {
+      config.minValue = _properties.minValue;
+    }
+    if (_properties.maxValue != null) {
+      config.maxValue = _properties.maxValue;
+    }
 
     // Registering _perform* as action handlers instead of the user provided
     // ones to ensure that changing a user provided handler from a non-null to
diff --git a/packages/flutter/lib/src/semantics/semantics.dart b/packages/flutter/lib/src/semantics/semantics.dart
index b45ea0f..0d09006 100644
--- a/packages/flutter/lib/src/semantics/semantics.dart
+++ b/packages/flutter/lib/src/semantics/semantics.dart
@@ -182,14 +182,14 @@
       SemanticsRole.navigation => _semanticsNavigation,
       SemanticsRole.region => _semanticsRegion,
       SemanticsRole.form => _noCheckRequired,
+      SemanticsRole.loadingSpinner => _noCheckRequired,
+      SemanticsRole.progressBar => _semanticsProgressBar,
       // TODO(chunhtai): add checks when the roles are used in framework.
       // https://github.com/flutter/flutter/issues/159741.
       SemanticsRole.dragHandle => _unimplemented,
       SemanticsRole.spinButton => _unimplemented,
       SemanticsRole.comboBox => _unimplemented,
       SemanticsRole.tooltip => _unimplemented,
-      SemanticsRole.loadingSpinner => _unimplemented,
-      SemanticsRole.progressBar => _unimplemented,
       SemanticsRole.hotKey => _unimplemented,
     }(node);
 
@@ -205,6 +205,48 @@
 
   static FlutterError? _noCheckRequired(SemanticsNode node) => null;
 
+  static FlutterError? _semanticsProgressBar(SemanticsNode node) {
+    final SemanticsData data = node.getSemanticsData();
+
+    // Check if value is present
+    if (data.value.isEmpty) {
+      return FlutterError('A progress bar must have a value');
+    }
+
+    // Check if minValue and maxValue are present
+    if (data.minValue == null) {
+      return FlutterError('A progress bar must have a minValue');
+    }
+
+    if (data.maxValue == null) {
+      return FlutterError('A progress bar must have a maxValue');
+    }
+
+    // Validate that value is within min and max range
+    try {
+      final double currentValue = double.parse(data.value);
+      final double minVal = double.parse(data.minValue!);
+      final double maxVal = double.parse(data.maxValue!);
+
+      if (currentValue < minVal || currentValue > maxVal) {
+        return FlutterError(
+          'Progress bar value ($currentValue) must be between minValue ($minVal) and maxValue ($maxVal)',
+        );
+      }
+
+      if (minVal >= maxVal) {
+        return FlutterError('Progress bar minValue ($minVal) must be less than maxValue ($maxVal)');
+      }
+    } catch (e) {
+      return FlutterError(
+        'Progress bar value, minValue, and maxValue must be valid numbers. '
+        'value: "${data.value}", minValue: "${data.minValue}", maxValue: "${data.maxValue}"',
+      );
+    }
+
+    return null;
+  }
+
   static FlutterError? _semanticsTab(SemanticsNode node) {
     final SemanticsData data = node.getSemanticsData();
     if (data.flagsCollection.isSelected == Tristate.none) {
@@ -1022,6 +1064,8 @@
     required this.validationResult,
     required this.inputType,
     required this.locale,
+    required this.minValue,
+    required this.maxValue,
     this.tags,
     this.transform,
     this.customSemanticsActionIds,
@@ -1297,6 +1341,12 @@
   /// content of this semantics node.
   final Locale? locale;
 
+  /// {@macro flutter.semantics.SemanticsProperties.maxValue}
+  final String? maxValue;
+
+  /// {@macro flutter.semantics.SemanticsProperties.minValue}
+  final String? minValue;
+
   /// Whether [flags] contains the given flag.
   @Deprecated(
     'Use flagsCollection instead. '
@@ -1381,6 +1431,8 @@
         ),
       );
     }
+    properties.add(StringProperty('minValue', minValue, defaultValue: null));
+    properties.add(StringProperty('maxValue', maxValue, defaultValue: null));
   }
 
   @override
@@ -1416,7 +1468,9 @@
         other.validationResult == validationResult &&
         other.inputType == inputType &&
         _sortedListsEqual(other.customSemanticsActionIds, customSemanticsActionIds) &&
-        setEquals<String>(controlsNodes, other.controlsNodes);
+        setEquals<String>(controlsNodes, other.controlsNodes) &&
+        minValue == other.minValue &&
+        maxValue == other.maxValue;
   }
 
   @override
@@ -1453,6 +1507,8 @@
       inputType,
       traversalParentIdentifier,
       traversalChildIdentifier,
+      minValue,
+      maxValue,
     ),
   );
 
@@ -1635,6 +1691,8 @@
     this.onExpand,
     this.onCollapse,
     this.customSemanticsActions,
+    this.minValue,
+    this.maxValue,
   }) : assert(
          label == null || attributedLabel == null,
          'Only one of label or attributedLabel should be provided',
@@ -2566,6 +2624,28 @@
   /// {@endtemplate}
   final SemanticsInputType? inputType;
 
+  /// {@template flutter.semantics.SemanticsProperties.maxValue}
+  /// The maximum value of the node.
+  ///
+  /// Used in conjunction with [value] to define the current value and range
+  /// of a node. A typical usage is for progress indicators, where [value]
+  /// represents the current progress and [maxValue] defines the maximum
+  /// possible value.
+  ///
+  /// {@endtemplate}
+  final String? maxValue;
+
+  /// {@template flutter.semantics.SemanticsProperties.minValue}
+  /// The minimum value of the node.
+  ///
+  /// Used in conjunction with [value] to define the current value and range
+  /// of a node. A typical usage is for progress indicators, where [value]
+  /// represents the current progress and [minValue] defines the minimum
+  /// possible value.
+  ///
+  /// {@endtemplate}
+  final String? minValue;
+
   @override
   void debugFillProperties(DiagnosticPropertiesBuilder properties) {
     super.debugFillProperties(properties);
@@ -3227,7 +3307,9 @@
         _headingLevel != config._headingLevel ||
         _linkUrl != config._linkUrl ||
         _role != config.role ||
-        _validationResult != config.validationResult;
+        _validationResult != config.validationResult ||
+        _minValue != config._minValue ||
+        _maxValue != config._maxValue;
   }
 
   // TAGS, LABELS, ACTIONS
@@ -3517,6 +3599,14 @@
   Set<String>? get controlsNodes => _controlsNodes;
   Set<String>? _controlsNodes = _kEmptyConfig.controlsNodes;
 
+  /// {@macro flutter.semantics.SemanticsProperties.minValue}
+  String? get minValue => _minValue;
+  String? _minValue;
+
+  /// {@macro flutter.semantics.SemanticsProperties.maxValue}
+  String? get maxValue => _maxValue;
+  String? _maxValue;
+
   /// {@macro flutter.semantics.SemanticsProperties.validationResult}
   SemanticsValidationResult get validationResult => _validationResult;
   SemanticsValidationResult _validationResult = _kEmptyConfig.validationResult;
@@ -3604,6 +3694,8 @@
     _inputType = config._inputType;
     _locale = config.locale;
 
+    _minValue = config.minValue;
+    _maxValue = config.maxValue;
     _replaceChildren(childrenInInversePaintOrder ?? const <SemanticsNode>[]);
 
     if (mergeAllDescendantsIntoThisNodeValueChanged) {
@@ -3657,6 +3749,8 @@
     SemanticsValidationResult validationResult = _validationResult;
     SemanticsInputType inputType = _inputType;
     final Locale? locale = _locale;
+    String? minValue = _minValue;
+    String? maxValue = _maxValue;
     final Set<int> customSemanticsActionIds = <int>{};
     for (final CustomSemanticsAction action in _customSemanticsActions.keys) {
       customSemanticsActionIds.add(CustomSemanticsAction.getIdentifier(action));
@@ -3764,6 +3858,9 @@
           controlsNodes = <String>{...controlsNodes!, ...node._controlsNodes!};
         }
 
+        minValue ??= node._minValue;
+        maxValue ??= node._maxValue;
+
         if (validationResult == SemanticsValidationResult.none) {
           validationResult = node._validationResult;
         } else if (validationResult == SemanticsValidationResult.valid) {
@@ -3812,6 +3909,8 @@
       validationResult: validationResult,
       inputType: inputType,
       locale: locale,
+      minValue: minValue,
+      maxValue: maxValue,
     );
   }
 
@@ -4003,6 +4102,8 @@
       validationResult: data.validationResult,
       inputType: data.inputType,
       locale: data.locale,
+      minValue: data.minValue ?? '0',
+      maxValue: data.maxValue ?? '0',
     );
     _dirty = false;
   }
@@ -4296,6 +4397,8 @@
         ),
       );
     }
+    properties.add(StringProperty('minValue', _minValue, defaultValue: null));
+    properties.add(StringProperty('maxValue', _maxValue, defaultValue: null));
   }
 
   /// Returns a string representation of this node and its descendants.
@@ -6434,6 +6537,22 @@
     _hasBeenAnnotated = true;
   }
 
+  /// {@macro flutter.semantics.SemanticsProperties.maxValue}
+  String? get maxValue => _maxValue;
+  String? _maxValue;
+  set maxValue(String? value) {
+    _maxValue = value;
+    _hasBeenAnnotated = true;
+  }
+
+  /// {@macro flutter.semantics.SemanticsProperties.minValue}
+  String? get minValue => _minValue;
+  String? _minValue;
+  set minValue(String? value) {
+    _minValue = value;
+    _hasBeenAnnotated = true;
+  }
+
   // TAGS
 
   /// The set of tags that this configuration wants to add to all child
@@ -6534,6 +6653,12 @@
     if (_hasExplicitRole && other._hasExplicitRole) {
       return false;
     }
+    if (_minValue != null && other._minValue != null) {
+      return false;
+    }
+    if (_maxValue != null && other._maxValue != null) {
+      return false;
+    }
     return true;
   }
 
@@ -6643,6 +6768,8 @@
     _accessiblityFocusBlockType = _accessiblityFocusBlockType._merge(
       child._accessiblityFocusBlockType,
     );
+    _minValue ??= child._minValue;
+    _maxValue ??= child._maxValue;
 
     _hasBeenAnnotated = hasBeenAnnotated || child.hasBeenAnnotated;
   }
@@ -6689,7 +6816,9 @@
       .._role = _role
       .._controlsNodes = _controlsNodes
       .._validationResult = _validationResult
-      .._inputType = _inputType;
+      .._inputType = _inputType
+      .._minValue = _minValue
+      .._maxValue = _maxValue;
   }
 }
 
diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart
index 089907c..e8f4cc1 100644
--- a/packages/flutter/lib/src/widgets/basic.dart
+++ b/packages/flutter/lib/src/widgets/basic.dart
@@ -4050,6 +4050,8 @@
     required SemanticsValidationResult validationResult,
     required ui.SemanticsInputType? inputType,
     required Locale? localeForSubtree,
+    required String? minValue,
+    required String? maxValue,
   }) : this.fromProperties(
          key: key,
          child: child,
@@ -4134,6 +4136,8 @@
            controlsNodes: controlsNodes,
            validationResult: validationResult,
            inputType: inputType,
+           minValue: minValue,
+           maxValue: maxValue,
          ),
        );
 
@@ -4384,6 +4388,8 @@
     super.validationResult = SemanticsValidationResult.none,
     super.inputType,
     super.localeForSubtree,
+    super.minValue,
+    super.maxValue,
   }) : super(child: sliver);
 
   /// {@macro flutter.widgets.SemanticsBase.fromProperties}
@@ -7962,6 +7968,8 @@
     super.validationResult = SemanticsValidationResult.none,
     super.inputType,
     super.localeForSubtree,
+    super.minValue,
+    super.maxValue,
   });
 
   /// {@macro flutter.widgets.SemanticsBase.fromProperties}
diff --git a/packages/flutter/test/material/progress_indicator_test.dart b/packages/flutter/test/material/progress_indicator_test.dart
index d181102..1355078 100644
--- a/packages/flutter/test/material/progress_indicator_test.dart
+++ b/packages/flutter/test/material/progress_indicator_test.dart
@@ -378,7 +378,12 @@
 
       expect(
         tester.getSemantics(find.byType(CircularProgressIndicator)),
-        matchesSemantics(value: '0%', textDirection: TextDirection.ltr),
+        matchesSemantics(
+          value: '0',
+          textDirection: TextDirection.ltr,
+          minValue: '0',
+          maxValue: '100',
+        ),
       );
       handle.dispose();
     },
@@ -940,7 +945,7 @@
     final SemanticsHandle handle = tester.ensureSemantics();
     final GlobalKey key = GlobalKey();
     const String label = 'Label';
-    const String value = '25%';
+    const String value = '25';
     await tester.pumpWidget(
       Theme(
         data: theme,
@@ -982,7 +987,7 @@
 
     expect(
       tester.getSemantics(find.byKey(key)),
-      matchesSemantics(textDirection: TextDirection.ltr, label: label, value: '25%'),
+      matchesSemantics(textDirection: TextDirection.ltr, label: label, value: '25'),
     );
 
     handle.dispose();
@@ -1037,7 +1042,7 @@
     final SemanticsHandle handle = tester.ensureSemantics();
     final GlobalKey key = GlobalKey();
     const String label = 'Label';
-    const String value = '25%';
+    const String value = '25';
     await tester.pumpWidget(
       Theme(
         data: theme,
@@ -1065,7 +1070,7 @@
     final SemanticsHandle handle = tester.ensureSemantics();
     final GlobalKey key = GlobalKey();
     const String label = 'Label';
-    const String value = '25%';
+    const String value = '25';
     await tester.pumpWidget(
       Theme(
         data: theme,
diff --git a/packages/flutter/test/semantics/semantics_test.dart b/packages/flutter/test/semantics/semantics_test.dart
index 2d70a03..e7f62cd 100644
--- a/packages/flutter/test/semantics/semantics_test.dart
+++ b/packages/flutter/test/semantics/semantics_test.dart
@@ -723,7 +723,9 @@
       '   scrollPosition: null\n'
       '   scrollExtentMax: null\n'
       '   indexInParent: null\n'
-      '   headingLevel: 0\n',
+      '   headingLevel: 0\n'
+      '   minValue: null\n'
+      '   maxValue: null\n',
     );
 
     final SemanticsConfiguration config = SemanticsConfiguration()
@@ -871,7 +873,9 @@
       '   scrollPosition: null\n'
       '   scrollExtentMax: null\n'
       '   indexInParent: null\n'
-      '   headingLevel: 0\n',
+      '   headingLevel: 0\n'
+      '   minValue: null\n'
+      '   maxValue: null\n',
     );
   });
 
diff --git a/packages/flutter/test/semantics/semantics_update_test.dart b/packages/flutter/test/semantics/semantics_update_test.dart
index 7ba38a0..c1aeb06 100644
--- a/packages/flutter/test/semantics/semantics_update_test.dart
+++ b/packages/flutter/test/semantics/semantics_update_test.dart
@@ -234,6 +234,8 @@
     ui.SemanticsHitTestBehavior hitTestBehavior = ui.SemanticsHitTestBehavior.defer,
     required ui.SemanticsInputType inputType,
     required ui.Locale? locale,
+    required String minValue,
+    required String maxValue,
   }) {
     // Makes sure we don't send the same id twice.
     assert(!observations.containsKey(id));
diff --git a/packages/flutter/test/widgets/semantics_tester.dart b/packages/flutter/test/widgets/semantics_tester.dart
index 14ff6c8..9515e5c 100644
--- a/packages/flutter/test/widgets/semantics_tester.dart
+++ b/packages/flutter/test/widgets/semantics_tester.dart
@@ -713,6 +713,8 @@
     double? scrollExtentMin,
     int? currentValueLength,
     int? maxValueLength,
+    String? maxValue,
+    String? minValue,
     SemanticsNode? ancestor,
     SemanticsInputType? inputType,
   }) {
@@ -810,6 +812,12 @@
       if (inputType != null && node.inputType != inputType) {
         return false;
       }
+      if (maxValue != null && node.maxValue != maxValue) {
+        return false;
+      }
+      if (minValue != null && node.minValue != minValue) {
+        return false;
+      }
       return true;
     }
 
@@ -1120,6 +1128,8 @@
     this.maxValueLength,
     this.currentValueLength,
     this.inputType,
+    this.minValue,
+    this.maxValue,
   }) : assert(
          label != null ||
              value != null ||
@@ -1135,6 +1145,7 @@
              maxValueLength != null ||
              currentValueLength != null ||
              inputType != null,
+         minValue != null || maxValue != null,
        );
   final AttributedString? attributedLabel;
   final AttributedString? attributedValue;
@@ -1155,6 +1166,8 @@
   final int? currentValueLength;
   final int? maxValueLength;
   final SemanticsInputType? inputType;
+  final String? minValue;
+  final String? maxValue;
 
   @override
   bool matches(covariant SemanticsTester item, Map<dynamic, dynamic> matchState) {
@@ -1179,6 +1192,8 @@
           currentValueLength: currentValueLength,
           maxValueLength: maxValueLength,
           inputType: inputType,
+          minValue: minValue,
+          maxValue: maxValue,
         )
         .isNotEmpty;
   }
@@ -1215,6 +1230,8 @@
       if (currentValueLength != null) 'currentValueLength "$currentValueLength"',
       if (maxValueLength != null) 'maxValueLength "$maxValueLength"',
       if (inputType != null) 'inputType $inputType',
+      if (minValue != null) 'minValue "$minValue"',
+      if (maxValue != null) 'maxValue "$maxValue"',
     ];
     return strings.join(', ');
   }
@@ -1244,6 +1261,8 @@
   int? maxValueLength,
   int? currentValueLength,
   SemanticsInputType? inputType,
+  String? minValue,
+  String? maxValue,
 }) {
   return _IncludesNodeWith(
     label: label,
@@ -1265,5 +1284,7 @@
     maxValueLength: maxValueLength,
     currentValueLength: currentValueLength,
     inputType: inputType,
+    minValue: minValue,
+    maxValue: maxValue,
   );
 }
diff --git a/packages/flutter_test/lib/src/matchers.dart b/packages/flutter_test/lib/src/matchers.dart
index 0a7b373..b613434 100644
--- a/packages/flutter_test/lib/src/matchers.dart
+++ b/packages/flutter_test/lib/src/matchers.dart
@@ -687,6 +687,8 @@
   int? currentValueLength,
   SemanticsValidationResult validationResult = SemanticsValidationResult.none,
   ui.SemanticsInputType? inputType,
+  String? maxValue,
+  String? minValue,
   // Flags //
   bool hasCheckedState = false,
   bool isChecked = false,
@@ -772,6 +774,8 @@
     currentValueLength: currentValueLength,
     validationResult: validationResult,
     inputType: inputType,
+    minValue: minValue,
+    maxValue: maxValue,
     // Flags
     hasCheckedState: hasCheckedState,
     isChecked: isChecked,
@@ -887,6 +891,8 @@
   int? currentValueLength,
   SemanticsValidationResult? validationResult,
   ui.SemanticsInputType? inputType,
+  String? maxValue,
+  String? minValue,
   // Flags
   bool? hasCheckedState,
   bool? isChecked,
@@ -972,6 +978,8 @@
     currentValueLength: currentValueLength,
     validationResult: validationResult,
     inputType: inputType,
+    minValue: minValue,
+    maxValue: maxValue,
     // Flags
     hasCheckedState: hasCheckedState,
     isChecked: isChecked,
@@ -2404,6 +2412,8 @@
     required this.currentValueLength,
     required this.validationResult,
     required this.inputType,
+    required this.minValue,
+    required this.maxValue,
     // Flags
     required bool? hasCheckedState,
     required bool? isChecked,
@@ -2551,6 +2561,8 @@
   final ui.SemanticsInputType? inputType;
   final List<Matcher>? children;
   final SemanticsValidationResult? validationResult;
+  final String? maxValue;
+  final String? minValue;
 
   /// There are three possible states for these two maps:
   ///
@@ -2660,6 +2672,12 @@
     if (validationResult != null) {
       description.add(' with validation result: $validationResult');
     }
+    if (minValue != null) {
+      description.add(' with minValue: $minValue');
+    }
+    if (maxValue != null) {
+      description.add(' with maxValue: $maxValue');
+    }
     if (children != null) {
       description.add(' with children:\n  ');
       final List<_MatchesSemanticsData> childMatches = children!.cast<_MatchesSemanticsData>();
@@ -2796,6 +2814,12 @@
     if (inputType != null && inputType != data.inputType) {
       return failWithDescription(matchState, 'inputType was: ${data.inputType}');
     }
+    if (minValue != null && minValue != data.minValue) {
+      return failWithDescription(matchState, 'minValue was: ${data.minValue}');
+    }
+    if (maxValue != null && maxValue != data.maxValue) {
+      return failWithDescription(matchState, 'maxValue was: ${data.maxValue}');
+    }
     if (actions.isNotEmpty) {
       final List<SemanticsAction> unexpectedActions = <SemanticsAction>[];
       final List<SemanticsAction> missingActions = <SemanticsAction>[];
diff --git a/packages/flutter_test/test/matchers_test.dart b/packages/flutter_test/test/matchers_test.dart
index e5d9c1f..6d4b12b 100644
--- a/packages/flutter_test/test/matchers_test.dart
+++ b/packages/flutter_test/test/matchers_test.dart
@@ -755,6 +755,8 @@
         validationResult: SemanticsValidationResult.none,
         inputType: ui.SemanticsInputType.none,
         locale: null,
+        minValue: '0',
+        maxValue: '0',
       );
       final _FakeSemanticsNode node = _FakeSemanticsNode(data);
 
@@ -1057,6 +1059,8 @@
         validationResult: SemanticsValidationResult.none,
         inputType: ui.SemanticsInputType.none,
         locale: null,
+        minValue: '0',
+        maxValue: '0',
       );
       final _FakeSemanticsNode node = _FakeSemanticsNode(data);
 
@@ -1159,6 +1163,8 @@
         validationResult: SemanticsValidationResult.none,
         inputType: ui.SemanticsInputType.none,
         locale: null,
+        minValue: '0',
+        maxValue: '0',
       );
       final _FakeSemanticsNode node = _FakeSemanticsNode(data);
 
@@ -1266,6 +1272,8 @@
         validationResult: SemanticsValidationResult.none,
         inputType: ui.SemanticsInputType.none,
         locale: null,
+        minValue: '0',
+        maxValue: '0',
       );
       final _FakeSemanticsNode emptyNode = _FakeSemanticsNode(emptyData);
 
@@ -1301,6 +1309,8 @@
         validationResult: SemanticsValidationResult.none,
         inputType: ui.SemanticsInputType.none,
         locale: null,
+        minValue: '0',
+        maxValue: '0',
       );
       final _FakeSemanticsNode fullNode = _FakeSemanticsNode(fullData);
 
@@ -1435,6 +1445,8 @@
         validationResult: SemanticsValidationResult.none,
         inputType: ui.SemanticsInputType.none,
         locale: null,
+        minValue: '0',
+        maxValue: '0',
       );
       final _FakeSemanticsNode node = _FakeSemanticsNode(data);