Fix type issue with RestorableNumN (and its subclasses) (#72862)
* Fix type issue with restorableN values
diff --git a/packages/flutter/lib/src/widgets/restoration_properties.dart b/packages/flutter/lib/src/widgets/restoration_properties.dart
index 7e170f6..f065388 100644
--- a/packages/flutter/lib/src/widgets/restoration_properties.dart
+++ b/packages/flutter/lib/src/widgets/restoration_properties.dart
@@ -306,11 +306,11 @@
/// See also:
///
/// * [RestorableNum] for the non-nullable version of this class.
-class RestorableNumN<T extends num?> extends _RestorablePrimitiveValueN<num?> {
+class RestorableNumN<T extends num?> extends _RestorablePrimitiveValueN<T> {
/// Creates a [RestorableNumN].
///
/// {@macro flutter.widgets.RestorableNum.constructor}
- RestorableNumN(num? defaultValue) : super(defaultValue);
+ RestorableNumN(T defaultValue) : super(defaultValue);
}
/// A [RestorableProperty] that knows how to store and restore a [double]
@@ -321,7 +321,7 @@
/// See also:
///
/// * [RestorableDouble] for the non-nullable version of this class.
-class RestorableDoubleN extends RestorableNumN<double> {
+class RestorableDoubleN extends RestorableNumN<double?> {
/// Creates a [RestorableDoubleN].
///
/// {@macro flutter.widgets.RestorableNum.constructor}
@@ -336,7 +336,7 @@
/// See also:
///
/// * [RestorableInt] for the non-nullable version of this class.
-class RestorableIntN extends RestorableNumN<int> {
+class RestorableIntN extends RestorableNumN<int?> {
/// Creates a [RestorableIntN].
///
/// {@macro flutter.widgets.RestorableNum.constructor}
diff --git a/packages/flutter/test/widgets/restorable_property_test.dart b/packages/flutter/test/widgets/restorable_property_test.dart
index 028134a..3ecf452 100644
--- a/packages/flutter/test/widgets/restorable_property_test.dart
+++ b/packages/flutter/test/widgets/restorable_property_test.dart
@@ -13,7 +13,7 @@
expect(() => RestorableInt(1).value, throwsAssertionError);
expect(() => RestorableString('hello').value, throwsAssertionError);
expect(() => RestorableBool(true).value, throwsAssertionError);
- expect(() => RestorableNumN<num>(0).value, throwsAssertionError);
+ expect(() => RestorableNumN<num?>(0).value, throwsAssertionError);
expect(() => RestorableDoubleN(1.0).value, throwsAssertionError);
expect(() => RestorableIntN(1).value, throwsAssertionError);
expect(() => RestorableStringN('hello').value, throwsAssertionError);
@@ -308,6 +308,43 @@
});
expect(state.objectValue.didUpdateValueCallCount, 1);
});
+
+
+ testWidgets('RestorableN types are properly defined', (WidgetTester tester) async {
+ await tester.pumpWidget(const RootRestorationScope(
+ restorationId: 'root-child',
+ child: _RestorableWidget(),
+ ));
+
+ expect(find.text('hello world'), findsOneWidget);
+ final _RestorableWidgetState state = tester.state(find.byType(_RestorableWidget));
+ state.setProperties(() {
+ state.nullableIntValue.value = 24;
+ state.nullableDoubleValue.value = 1.5;
+ });
+
+ // The following types of asserts do not work. They pass even when the
+ // type of `value` is a `num` and not an `int` because `num` is a
+ // superclass of `int`. This test is intended to prevent a regression
+ // where RestorableIntN's value is of type `num?`, but it is passed into
+ // a function which requires an `int?` value. This resulted in Dart
+ // compile-time errors.
+ //
+ // expect(state.nullableIntValue.value, isA<int>());
+ // expect(state.nullableIntValue.value.runtimeType, int);
+
+ // A function that takes a nullable int value.
+ void takesInt(int? value) {}
+ // The following would result in a Dart compile-time error if `value` is
+ // a `num?` instead of an `int?`.
+ takesInt(state.nullableIntValue.value);
+
+ // A function that takes a nullable double value.
+ void takesDouble(double? value) {}
+ // The following would result in a Dart compile-time error if `value` is
+ // a `num?` instead of a `double?`.
+ takesDouble(state.nullableDoubleValue.value);
+ });
}
class _TestRestorableValue extends RestorableValue<Object?> {
@@ -348,7 +385,7 @@
final RestorableInt intValue = RestorableInt(42);
final RestorableString stringValue = RestorableString('hello world');
final RestorableBool boolValue = RestorableBool(false);
- final RestorableNumN<num> nullableNumValue = RestorableNumN<num>(null);
+ final RestorableNumN<num?> nullableNumValue = RestorableNumN<num?>(null);
final RestorableDoubleN nullableDoubleValue = RestorableDoubleN(null);
final RestorableIntN nullableIntValue = RestorableIntN(null);
final RestorableStringN nullableStringValue = RestorableStringN(null);