Skip to content

Commit 2a8631a

Browse files
committed
Minor adjustments on 2D APIs (flutter#131358)
These tweaks came from flutter/packages#4536 - The TwoDimensionalChildBuilderDelegate asserts that maxXIndex and maxYIndex are null or >= 0 - The TwoDimensionalChildDelegate setter in RenderTwoDimensionalViewport has a covariant to allow type safety for subclasses of RenderTwoDimensionalViewport implementing with other subclasses of TwoDimensionalChildDelegate I'd like to cherry pick this so flutter/packages#4536 will not have to wait for it to reach stable.
1 parent ac71592 commit 2a8631a

File tree

3 files changed

+105
-2
lines changed

3 files changed

+105
-2
lines changed

packages/flutter/lib/src/widgets/scroll_delegate.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -933,7 +933,9 @@ class TwoDimensionalChildBuilderDelegate extends TwoDimensionalChildDelegate {
933933
required this.builder,
934934
int? maxXIndex,
935935
int? maxYIndex,
936-
}) : _maxYIndex = maxYIndex,
936+
}) : assert(maxYIndex == null || maxYIndex >= 0),
937+
assert(maxXIndex == null || maxXIndex >= 0),
938+
_maxYIndex = maxYIndex,
937939
_maxXIndex = maxXIndex;
938940

939941
/// Called to build children on demand.
@@ -974,6 +976,8 @@ class TwoDimensionalChildBuilderDelegate extends TwoDimensionalChildDelegate {
974976
/// [TwoDimensionalViewport] subclass to learn how this value is applied in
975977
/// the specific use case.
976978
///
979+
/// If not null, the value must be non-negative.
980+
///
977981
/// If the value changes, the delegate will call [notifyListeners]. This
978982
/// informs the [RenderTwoDimensionalViewport] that any cached information
979983
/// from the delegate is invalid.
@@ -993,6 +997,7 @@ class TwoDimensionalChildBuilderDelegate extends TwoDimensionalChildDelegate {
993997
if (value == maxXIndex) {
994998
return;
995999
}
1000+
assert(value == null || value >= 0);
9961001
_maxXIndex = value;
9971002
notifyListeners();
9981003
}
@@ -1015,6 +1020,7 @@ class TwoDimensionalChildBuilderDelegate extends TwoDimensionalChildDelegate {
10151020
if (maxYIndex == value) {
10161021
return;
10171022
}
1023+
assert(value == null || value >= 0);
10181024
_maxYIndex = value;
10191025
notifyListeners();
10201026
}

packages/flutter/lib/src/widgets/two_dimensional_viewport.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ abstract class RenderTwoDimensionalViewport extends RenderBox implements RenderA
612612
/// Supplies children for layout in the viewport.
613613
TwoDimensionalChildDelegate get delegate => _delegate;
614614
TwoDimensionalChildDelegate _delegate;
615-
set delegate(TwoDimensionalChildDelegate value) {
615+
set delegate(covariant TwoDimensionalChildDelegate value) {
616616
if (_delegate == value) {
617617
return;
618618
}

packages/flutter/test/widgets/two_dimensional_viewport_test.dart

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,78 @@ void main() {
111111
);
112112
}, variant: TargetPlatformVariant.all());
113113

114+
test('maxXIndex and maxYIndex assertions', () {
115+
final TwoDimensionalChildBuilderDelegate delegate = TwoDimensionalChildBuilderDelegate(
116+
maxXIndex: 0,
117+
maxYIndex: 0,
118+
builder: (BuildContext context, ChildVicinity vicinity) {
119+
return const SizedBox.shrink();
120+
}
121+
);
122+
// Update
123+
expect(
124+
() {
125+
delegate.maxXIndex = -1;
126+
},
127+
throwsA(
128+
isA<AssertionError>().having(
129+
(AssertionError error) => error.toString(),
130+
'description',
131+
contains('value == null || value >= 0'),
132+
),
133+
),
134+
);
135+
expect(
136+
() {
137+
delegate.maxYIndex = -1;
138+
},
139+
throwsA(
140+
isA<AssertionError>().having(
141+
(AssertionError error) => error.toString(),
142+
'description',
143+
contains('value == null || value >= 0'),
144+
),
145+
),
146+
);
147+
// Constructor
148+
expect(
149+
() {
150+
TwoDimensionalChildBuilderDelegate(
151+
maxXIndex: -1,
152+
maxYIndex: 0,
153+
builder: (BuildContext context, ChildVicinity vicinity) {
154+
return const SizedBox.shrink();
155+
}
156+
);
157+
},
158+
throwsA(
159+
isA<AssertionError>().having(
160+
(AssertionError error) => error.toString(),
161+
'description',
162+
contains('maxXIndex == null || maxXIndex >= 0'),
163+
),
164+
),
165+
);
166+
expect(
167+
() {
168+
TwoDimensionalChildBuilderDelegate(
169+
maxXIndex: 0,
170+
maxYIndex: -1,
171+
builder: (BuildContext context, ChildVicinity vicinity) {
172+
return const SizedBox.shrink();
173+
}
174+
);
175+
},
176+
throwsA(
177+
isA<AssertionError>().having(
178+
(AssertionError error) => error.toString(),
179+
'description',
180+
contains('maxYIndex == null || maxYIndex >= 0'),
181+
),
182+
),
183+
);
184+
});
185+
114186
testWidgets('throws an error when builder throws', (WidgetTester tester) async {
115187
final List<Object> exceptions = <Object>[];
116188
final FlutterExceptionHandler? oldHandler = FlutterError.onError;
@@ -1822,3 +1894,28 @@ Future<void> restoreScrollAndVerify(WidgetTester tester) async {
18221894
100.0,
18231895
);
18241896
}
1897+
1898+
// Validates covariant through analysis.
1899+
mixin _SomeDelegateMixin on TwoDimensionalChildDelegate {}
1900+
1901+
class _SomeRenderTwoDimensionalViewport extends RenderTwoDimensionalViewport { // ignore: unused_element
1902+
_SomeRenderTwoDimensionalViewport({
1903+
required super.horizontalOffset,
1904+
required super.horizontalAxisDirection,
1905+
required super.verticalOffset,
1906+
required super.verticalAxisDirection,
1907+
required _SomeDelegateMixin super.delegate,
1908+
required super.mainAxis,
1909+
required super.childManager,
1910+
});
1911+
1912+
@override
1913+
_SomeDelegateMixin get delegate => super.delegate as _SomeDelegateMixin;
1914+
@override
1915+
set delegate(_SomeDelegateMixin value) { // Analysis would fail without covariant
1916+
super.delegate = value;
1917+
}
1918+
1919+
@override
1920+
void layoutChildSequence() {}
1921+
}

0 commit comments

Comments
 (0)