ScrollController creation dispatching for memory leaks tracking (#133759)
This commit is contained in:
parent
7fe4571af2
commit
414bff1403
@ -884,6 +884,7 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
|
|||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
focusScopeNode.dispose();
|
focusScopeNode.dispose();
|
||||||
|
primaryScrollController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,11 @@ class ScrollController extends ChangeNotifier {
|
|||||||
this.debugLabel,
|
this.debugLabel,
|
||||||
this.onAttach,
|
this.onAttach,
|
||||||
this.onDetach,
|
this.onDetach,
|
||||||
}) : _initialScrollOffset = initialScrollOffset;
|
}) : _initialScrollOffset = initialScrollOffset {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
maybeDispatchObjectCreation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The initial value to use for [offset].
|
/// The initial value to use for [offset].
|
||||||
///
|
///
|
||||||
|
@ -1609,40 +1609,45 @@ void main() {
|
|||||||
// Generates a MaterialApp with a SliverAppBar in a CustomScrollView.
|
// Generates a MaterialApp with a SliverAppBar in a CustomScrollView.
|
||||||
// The first cell of the scroll view contains a button at its top, and is
|
// The first cell of the scroll view contains a button at its top, and is
|
||||||
// initially scrolled so that it is beneath the SliverAppBar.
|
// initially scrolled so that it is beneath the SliverAppBar.
|
||||||
Widget buildWidget({
|
(ScrollController, Widget) buildWidget({
|
||||||
required bool forceMaterialTransparency,
|
required bool forceMaterialTransparency,
|
||||||
required VoidCallback onPressed
|
required VoidCallback onPressed
|
||||||
}) {
|
}) {
|
||||||
const double appBarHeight = 120;
|
const double appBarHeight = 120;
|
||||||
return MaterialApp(
|
final ScrollController controller = ScrollController(initialScrollOffset: appBarHeight);
|
||||||
home: Scaffold(
|
|
||||||
body: CustomScrollView(
|
return (
|
||||||
controller: ScrollController(initialScrollOffset:appBarHeight),
|
controller,
|
||||||
slivers: <Widget>[
|
MaterialApp(
|
||||||
SliverAppBar(
|
home: Scaffold(
|
||||||
collapsedHeight: appBarHeight,
|
body: CustomScrollView(
|
||||||
expandedHeight: appBarHeight,
|
controller: controller,
|
||||||
pinned: true,
|
slivers: <Widget>[
|
||||||
elevation: 0,
|
SliverAppBar(
|
||||||
backgroundColor: Colors.transparent,
|
collapsedHeight: appBarHeight,
|
||||||
forceMaterialTransparency: forceMaterialTransparency,
|
expandedHeight: appBarHeight,
|
||||||
title: const Text('AppBar'),
|
pinned: true,
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
forceMaterialTransparency: forceMaterialTransparency,
|
||||||
|
title: const Text('AppBar'),
|
||||||
|
),
|
||||||
|
SliverList(
|
||||||
|
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
|
||||||
|
return SizedBox(
|
||||||
|
height: appBarHeight,
|
||||||
|
child: index == 0
|
||||||
|
? Align(
|
||||||
|
alignment: Alignment.topCenter,
|
||||||
|
child: TextButton(onPressed: onPressed, child: const Text('press')))
|
||||||
|
: const SizedBox(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
childCount: 20,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
SliverList(
|
]),
|
||||||
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
|
),
|
||||||
return SizedBox(
|
|
||||||
height: appBarHeight,
|
|
||||||
child: index == 0
|
|
||||||
? Align(
|
|
||||||
alignment: Alignment.topCenter,
|
|
||||||
child: TextButton(onPressed: onPressed, child: const Text('press')))
|
|
||||||
: const SizedBox(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
childCount: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1650,7 +1655,7 @@ void main() {
|
|||||||
testWidgetsWithLeakTracking(
|
testWidgetsWithLeakTracking(
|
||||||
'forceMaterialTransparency == true allows gestures beneath the app bar', (WidgetTester tester) async {
|
'forceMaterialTransparency == true allows gestures beneath the app bar', (WidgetTester tester) async {
|
||||||
bool buttonWasPressed = false;
|
bool buttonWasPressed = false;
|
||||||
final Widget widget = buildWidget(
|
final (ScrollController controller, Widget widget) = buildWidget(
|
||||||
forceMaterialTransparency:true,
|
forceMaterialTransparency:true,
|
||||||
onPressed:() { buttonWasPressed = true; },
|
onPressed:() { buttonWasPressed = true; },
|
||||||
);
|
);
|
||||||
@ -1663,6 +1668,8 @@ void main() {
|
|||||||
await tester.tap(buttonFinder);
|
await tester.tap(buttonFinder);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(buttonWasPressed, isTrue);
|
expect(buttonWasPressed, isTrue);
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking(
|
testWidgetsWithLeakTracking(
|
||||||
@ -1672,7 +1679,7 @@ void main() {
|
|||||||
WidgetController.hitTestWarningShouldBeFatal = false;
|
WidgetController.hitTestWarningShouldBeFatal = false;
|
||||||
|
|
||||||
bool buttonWasPressed = false;
|
bool buttonWasPressed = false;
|
||||||
final Widget widget = buildWidget(
|
final (ScrollController controller, Widget widget) = buildWidget(
|
||||||
forceMaterialTransparency:false,
|
forceMaterialTransparency:false,
|
||||||
onPressed:() { buttonWasPressed = true; },
|
onPressed:() { buttonWasPressed = true; },
|
||||||
);
|
);
|
||||||
@ -1685,6 +1692,8 @@ void main() {
|
|||||||
await tester.tap(buttonFinder, warnIfMissed:false);
|
await tester.tap(buttonFinder, warnIfMissed:false);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(buttonWasPressed, isFalse);
|
expect(buttonWasPressed, isFalse);
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -4295,6 +4304,8 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(tester.takeException(), isNull);
|
expect(tester.takeException(), isNull);
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('does not trigger on horizontal scroll', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('does not trigger on horizontal scroll', (WidgetTester tester) async {
|
||||||
|
@ -84,7 +84,14 @@ void main() {
|
|||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(tabController.index, 2);
|
expect(tabController.index, 2);
|
||||||
expect(indicatorColors(tester), const <Color>[kUnselectedColor, kUnselectedColor, kSelectedColor]);
|
expect(indicatorColors(tester), const <Color>[kUnselectedColor, kUnselectedColor, kSelectedColor]);
|
||||||
});
|
},
|
||||||
|
// TODO(someone): remove after fixing
|
||||||
|
// https://github.com/flutter/flutter/issues/133755
|
||||||
|
leakTrackingTestConfig: const LeakTrackingTestConfig(
|
||||||
|
notDisposedAllowList: <String, int?>{
|
||||||
|
'PageController': 1,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('PageSelector responds correctly to TabController.animateTo()', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('PageSelector responds correctly to TabController.animateTo()', (WidgetTester tester) async {
|
||||||
final TabController tabController = TabController(
|
final TabController tabController = TabController(
|
||||||
@ -127,7 +134,14 @@ void main() {
|
|||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(tabController.index, 2);
|
expect(tabController.index, 2);
|
||||||
expect(indicatorColors(tester), const <Color>[kUnselectedColor, kUnselectedColor, kSelectedColor]);
|
expect(indicatorColors(tester), const <Color>[kUnselectedColor, kUnselectedColor, kSelectedColor]);
|
||||||
});
|
},
|
||||||
|
// TODO(someone): remove after fixing
|
||||||
|
// https://github.com/flutter/flutter/issues/133755
|
||||||
|
leakTrackingTestConfig: const LeakTrackingTestConfig(
|
||||||
|
notDisposedAllowList: <String, int?>{
|
||||||
|
'PageController': 1,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('PageSelector responds correctly to TabBarView drags', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('PageSelector responds correctly to TabBarView drags', (WidgetTester tester) async {
|
||||||
final TabController tabController = TabController(
|
final TabController tabController = TabController(
|
||||||
@ -185,8 +199,14 @@ void main() {
|
|||||||
await tester.fling(find.byType(TabBarView), const Offset(100.0, 0.0), 1000.0);
|
await tester.fling(find.byType(TabBarView), const Offset(100.0, 0.0), 1000.0);
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(indicatorColors(tester), const <Color>[kUnselectedColor, kSelectedColor, kUnselectedColor]);
|
expect(indicatorColors(tester), const <Color>[kUnselectedColor, kSelectedColor, kUnselectedColor]);
|
||||||
|
},
|
||||||
});
|
// TODO(someone): remove after fixing
|
||||||
|
// https://github.com/flutter/flutter/issues/133755
|
||||||
|
leakTrackingTestConfig: const LeakTrackingTestConfig(
|
||||||
|
notDisposedAllowList: <String, int?>{
|
||||||
|
'PageController': 1,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('PageSelector indicatorColors', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('PageSelector indicatorColors', (WidgetTester tester) async {
|
||||||
const Color kRed = Color(0xFFFF0000);
|
const Color kRed = Color(0xFFFF0000);
|
||||||
@ -205,7 +225,14 @@ void main() {
|
|||||||
tabController.index = 0;
|
tabController.index = 0;
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(indicatorColors(tester), const <Color>[kBlue, kRed, kRed]);
|
expect(indicatorColors(tester), const <Color>[kBlue, kRed, kRed]);
|
||||||
});
|
},
|
||||||
|
// TODO(someone): remove after fixing
|
||||||
|
// https://github.com/flutter/flutter/issues/133755
|
||||||
|
leakTrackingTestConfig: const LeakTrackingTestConfig(
|
||||||
|
notDisposedAllowList: <String, int?>{
|
||||||
|
'PageController': 1,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
testWidgets('PageSelector indicatorSize', (WidgetTester tester) async {
|
testWidgets('PageSelector indicatorSize', (WidgetTester tester) async {
|
||||||
final TabController tabController = TabController(
|
final TabController tabController = TabController(
|
||||||
@ -228,7 +255,7 @@ void main() {
|
|||||||
expect(tester.getSize(find.byType(TabPageSelector)).height, 24.0);
|
expect(tester.getSize(find.byType(TabPageSelector)).height, 24.0);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('PageSelector circle border', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('PageSelector circle border', (WidgetTester tester) async {
|
||||||
final TabController tabController = TabController(
|
final TabController tabController = TabController(
|
||||||
vsync: const TestVSync(),
|
vsync: const TestVSync(),
|
||||||
initialIndex: 1,
|
initialIndex: 1,
|
||||||
@ -272,5 +299,12 @@ void main() {
|
|||||||
for (final TabPageSelectorIndicator indicator in indicators) {
|
for (final TabPageSelectorIndicator indicator in indicators) {
|
||||||
expect(indicator.borderStyle, BorderStyle.solid);
|
expect(indicator.borderStyle, BorderStyle.solid);
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
// TODO(someone): remove after fixing
|
||||||
|
// https://github.com/flutter/flutter/issues/133755
|
||||||
|
leakTrackingTestConfig: const LeakTrackingTestConfig(
|
||||||
|
notDisposedAllowList: <String, int?>{
|
||||||
|
'PageController': 1,
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
@ -423,6 +423,8 @@ void main() {
|
|||||||
expect(controller.offset, greaterThan(lastScrollOffset));
|
expect(controller.offset, greaterThan(lastScrollOffset));
|
||||||
expect(controller.offset, lessThan(0.0));
|
expect(controller.offset, lessThan(0.0));
|
||||||
expect(refreshCalled, isTrue);
|
expect(refreshCalled, isTrue);
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
|
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('RefreshIndicator does not force child to relayout', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('RefreshIndicator does not force child to relayout', (WidgetTester tester) async {
|
||||||
@ -618,6 +620,8 @@ void main() {
|
|||||||
await tester.pump(const Duration(seconds: 1)); // finish the scroll animation
|
await tester.pump(const Duration(seconds: 1)); // finish the scroll animation
|
||||||
await tester.pump(const Duration(seconds: 1)); // finish the indicator settle animation
|
await tester.pump(const Duration(seconds: 1)); // finish the indicator settle animation
|
||||||
expect(tester.getCenter(find.byType(RefreshProgressIndicator)).dy, lessThan(300.0));
|
expect(tester.getCenter(find.byType(RefreshProgressIndicator)).dy, lessThan(300.0));
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('Reverse RefreshIndicator(anywhere mode) should be shown when dragging from non-zero scroll position', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Reverse RefreshIndicator(anywhere mode) should be shown when dragging from non-zero scroll position', (WidgetTester tester) async {
|
||||||
@ -654,6 +658,8 @@ void main() {
|
|||||||
await tester.pump(const Duration(seconds: 1)); // finish the scroll animation
|
await tester.pump(const Duration(seconds: 1)); // finish the scroll animation
|
||||||
await tester.pump(const Duration(seconds: 1)); // finish the indicator settle animation
|
await tester.pump(const Duration(seconds: 1)); // finish the indicator settle animation
|
||||||
expect(tester.getCenter(find.byType(RefreshProgressIndicator)).dy, lessThan(300.0));
|
expect(tester.getCenter(find.byType(RefreshProgressIndicator)).dy, lessThan(300.0));
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Regression test for https://github.com/flutter/flutter/issues/71936
|
// Regression test for https://github.com/flutter/flutter/issues/71936
|
||||||
@ -691,6 +697,8 @@ void main() {
|
|||||||
await tester.pump(const Duration(seconds: 1)); // finish the scroll animation
|
await tester.pump(const Duration(seconds: 1)); // finish the scroll animation
|
||||||
await tester.pump(const Duration(seconds: 1)); // finish the indicator settle animation
|
await tester.pump(const Duration(seconds: 1)); // finish the indicator settle animation
|
||||||
expect(find.byType(RefreshProgressIndicator), findsNothing);
|
expect(find.byType(RefreshProgressIndicator), findsNothing);
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('Top RefreshIndicator(onEdge mode) should not be shown when dragging from non-zero scroll position', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Top RefreshIndicator(onEdge mode) should not be shown when dragging from non-zero scroll position', (WidgetTester tester) async {
|
||||||
@ -725,6 +733,8 @@ void main() {
|
|||||||
await tester.pump(const Duration(seconds: 1)); // finish the scroll animation
|
await tester.pump(const Duration(seconds: 1)); // finish the scroll animation
|
||||||
await tester.pump(const Duration(seconds: 1)); // finish the indicator settle animation
|
await tester.pump(const Duration(seconds: 1)); // finish the indicator settle animation
|
||||||
expect(find.byType(RefreshProgressIndicator), findsNothing);
|
expect(find.byType(RefreshProgressIndicator), findsNothing);
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('Reverse RefreshIndicator(onEdge mode) should be shown when dragging from non-zero scroll position', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Reverse RefreshIndicator(onEdge mode) should be shown when dragging from non-zero scroll position', (WidgetTester tester) async {
|
||||||
@ -760,6 +770,8 @@ void main() {
|
|||||||
await tester.pump(const Duration(seconds: 1)); // finish the scroll animation
|
await tester.pump(const Duration(seconds: 1)); // finish the scroll animation
|
||||||
await tester.pump(const Duration(seconds: 1)); // finish the indicator settle animation
|
await tester.pump(const Duration(seconds: 1)); // finish the indicator settle animation
|
||||||
expect(find.byType(RefreshProgressIndicator), findsNothing);
|
expect(find.byType(RefreshProgressIndicator), findsNothing);
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('ScrollController.jumpTo should not trigger the refresh indicator', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('ScrollController.jumpTo should not trigger the refresh indicator', (WidgetTester tester) async {
|
||||||
@ -792,6 +804,8 @@ void main() {
|
|||||||
await tester.pump(const Duration(seconds: 1)); // finish the indicator hide animation
|
await tester.pump(const Duration(seconds: 1)); // finish the indicator hide animation
|
||||||
|
|
||||||
expect(refreshCalled, false);
|
expect(refreshCalled, false);
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('RefreshIndicator.adaptive', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('RefreshIndicator.adaptive', (WidgetTester tester) async {
|
||||||
|
@ -203,6 +203,8 @@ void main() {
|
|||||||
await tester.pumpWidget(viewWithScroll());
|
await tester.pumpWidget(viewWithScroll());
|
||||||
final AssertionError exception = tester.takeException() as AssertionError;
|
final AssertionError exception = tester.takeException() as AssertionError;
|
||||||
expect(exception, isAssertionError);
|
expect(exception, isAssertionError);
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('On first render with thumbVisibility: true, the thumb shows', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('On first render with thumbVisibility: true, the thumb shows', (WidgetTester tester) async {
|
||||||
@ -229,6 +231,8 @@ void main() {
|
|||||||
await tester.pumpWidget(viewWithScroll());
|
await tester.pumpWidget(viewWithScroll());
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(find.byType(Scrollbar), paints..rect());
|
expect(find.byType(Scrollbar), paints..rect());
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('On first render with thumbVisibility: true, the thumb shows with PrimaryScrollController', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('On first render with thumbVisibility: true, the thumb shows with PrimaryScrollController', (WidgetTester tester) async {
|
||||||
@ -261,6 +265,8 @@ void main() {
|
|||||||
await tester.pumpWidget(viewWithScroll());
|
await tester.pumpWidget(viewWithScroll());
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(find.byType(Scrollbar), paints..rect());
|
expect(find.byType(Scrollbar), paints..rect());
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking(
|
testWidgetsWithLeakTracking(
|
||||||
@ -314,6 +320,8 @@ void main() {
|
|||||||
await tester.pumpWidget(viewWithScroll());
|
await tester.pumpWidget(viewWithScroll());
|
||||||
final AssertionError exception = tester.takeException() as AssertionError;
|
final AssertionError exception = tester.takeException() as AssertionError;
|
||||||
expect(exception, isAssertionError);
|
expect(exception, isAssertionError);
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -341,6 +349,8 @@ void main() {
|
|||||||
await tester.pumpWidget(viewWithScroll());
|
await tester.pumpWidget(viewWithScroll());
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(find.byType(Scrollbar), paints..rect());
|
expect(find.byType(Scrollbar), paints..rect());
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('On first render with thumbVisibility: true, the thumb shows with PrimaryScrollController', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('On first render with thumbVisibility: true, the thumb shows with PrimaryScrollController', (WidgetTester tester) async {
|
||||||
@ -373,6 +383,8 @@ void main() {
|
|||||||
await tester.pumpWidget(viewWithScroll());
|
await tester.pumpWidget(viewWithScroll());
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(find.byType(Scrollbar), paints..rect());
|
expect(find.byType(Scrollbar), paints..rect());
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('On first render with thumbVisibility: false, the thumb is hidden', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('On first render with thumbVisibility: false, the thumb is hidden', (WidgetTester tester) async {
|
||||||
@ -399,6 +411,8 @@ void main() {
|
|||||||
await tester.pumpWidget(viewWithScroll());
|
await tester.pumpWidget(viewWithScroll());
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(find.byType(Scrollbar), isNot(paints..rect()));
|
expect(find.byType(Scrollbar), isNot(paints..rect()));
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking(
|
testWidgetsWithLeakTracking(
|
||||||
@ -452,6 +466,8 @@ void main() {
|
|||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
// Scrollbar is not showing after scroll finishes
|
// Scrollbar is not showing after scroll finishes
|
||||||
expect(find.byType(Scrollbar), isNot(paints..rect()));
|
expect(find.byType(Scrollbar), isNot(paints..rect()));
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -501,6 +517,8 @@ void main() {
|
|||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
// Scrollbar is not showing after scroll finishes
|
// Scrollbar is not showing after scroll finishes
|
||||||
expect(find.byType(Scrollbar), paints..rect());
|
expect(find.byType(Scrollbar), paints..rect());
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -561,6 +579,8 @@ void main() {
|
|||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
// Scrollbar thumb is showing after scroll finishes and timer ends.
|
// Scrollbar thumb is showing after scroll finishes and timer ends.
|
||||||
expect(find.byType(Scrollbar), paints..rect());
|
expect(find.byType(Scrollbar), paints..rect());
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -610,6 +630,8 @@ void main() {
|
|||||||
await tester.tap(find.byType(FloatingActionButton));
|
await tester.tap(find.byType(FloatingActionButton));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(materialScrollbar, isNot(paints..rect()));
|
expect(materialScrollbar, isNot(paints..rect()));
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -673,6 +695,8 @@ void main() {
|
|||||||
));
|
));
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('Tapping the track area pages the Scroll View', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Tapping the track area pages the Scroll View', (WidgetTester tester) async {
|
||||||
@ -763,6 +787,8 @@ void main() {
|
|||||||
color: _kAndroidThumbIdleColor,
|
color: _kAndroidThumbIdleColor,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Scrollbar never goes away until finger lift', (WidgetTester tester) async {
|
testWidgets('Scrollbar never goes away until finger lift', (WidgetTester tester) async {
|
||||||
@ -936,6 +962,8 @@ void main() {
|
|||||||
color: _kAndroidThumbIdleColor,
|
color: _kAndroidThumbIdleColor,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('Scrollbar thumb color completes a hover animation', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Scrollbar thumb color completes a hover animation', (WidgetTester tester) async {
|
||||||
@ -1331,6 +1359,8 @@ void main() {
|
|||||||
expect(find.byType(CupertinoScrollbar), paints..rrect());
|
expect(find.byType(CupertinoScrollbar), paints..rrect());
|
||||||
final CupertinoScrollbar scrollbar = tester.widget<CupertinoScrollbar>(find.byType(CupertinoScrollbar));
|
final CupertinoScrollbar scrollbar = tester.widget<CupertinoScrollbar>(find.byType(CupertinoScrollbar));
|
||||||
expect(scrollbar.controller, isNotNull);
|
expect(scrollbar.controller, isNotNull);
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
|
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
|
||||||
|
|
||||||
testWidgetsWithLeakTracking("Scrollbar doesn't show when scroll the inner scrollable widget", (WidgetTester tester) async {
|
testWidgetsWithLeakTracking("Scrollbar doesn't show when scroll the inner scrollable widget", (WidgetTester tester) async {
|
||||||
@ -1463,6 +1493,8 @@ void main() {
|
|||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
// The offset should not have changed.
|
// The offset should not have changed.
|
||||||
expect(scrollController.offset, scrollAmount);
|
expect(scrollController.offset, scrollAmount);
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.fuchsia }));
|
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.fuchsia }));
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('Scrollbar dragging is disabled by default on Android', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Scrollbar dragging is disabled by default on Android', (WidgetTester tester) async {
|
||||||
@ -1557,6 +1589,8 @@ void main() {
|
|||||||
// The offset should not have changed.
|
// The offset should not have changed.
|
||||||
expect(scrollController.offset, scrollAmount * 2);
|
expect(scrollController.offset, scrollAmount * 2);
|
||||||
expect(tapCount, 2);
|
expect(tapCount, 2);
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('Simultaneous dragging and pointer scrolling does not cause a crash', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Simultaneous dragging and pointer scrolling does not cause a crash', (WidgetTester tester) async {
|
||||||
@ -1729,6 +1763,8 @@ void main() {
|
|||||||
color: const Color(0xffbcbcbc),
|
color: const Color(0xffbcbcbc),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('Scrollbar.thumbVisibility triggers assertion when multiple ScrollPositions are attached.', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Scrollbar.thumbVisibility triggers assertion when multiple ScrollPositions are attached.', (WidgetTester tester) async {
|
||||||
@ -1796,7 +1832,16 @@ void main() {
|
|||||||
error.message,
|
error.message,
|
||||||
contains('The provided ScrollController is currently attached to more than one ScrollPosition.'),
|
contains('The provided ScrollController is currently attached to more than one ScrollPosition.'),
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
scrollController.dispose();
|
||||||
|
},
|
||||||
|
// TODO(someone): remove after fixing
|
||||||
|
// https://github.com/flutter/flutter/issues/133755
|
||||||
|
leakTrackingTestConfig: const LeakTrackingTestConfig(
|
||||||
|
notDisposedAllowList: <String, int?>{
|
||||||
|
'PageController': 2,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('Scrollbar scrollOrientation works correctly', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Scrollbar scrollOrientation works correctly', (WidgetTester tester) async {
|
||||||
final ScrollController scrollController = ScrollController();
|
final ScrollController scrollController = ScrollController();
|
||||||
@ -1844,5 +1889,7 @@ void main() {
|
|||||||
color: _kAndroidThumbIdleColor,
|
color: _kAndroidThumbIdleColor,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,8 @@ void main() {
|
|||||||
color: const Color(0x80000000),
|
color: const Color(0x80000000),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{
|
}, variant: const TargetPlatformVariant(<TargetPlatform>{
|
||||||
TargetPlatform.linux,
|
TargetPlatform.linux,
|
||||||
TargetPlatform.macOS,
|
TargetPlatform.macOS,
|
||||||
@ -204,6 +206,8 @@ void main() {
|
|||||||
color: const Color(0xff2196f3),
|
color: const Color(0xff2196f3),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{
|
}, variant: const TargetPlatformVariant(<TargetPlatform>{
|
||||||
TargetPlatform.linux,
|
TargetPlatform.linux,
|
||||||
TargetPlatform.macOS,
|
TargetPlatform.macOS,
|
||||||
@ -253,6 +257,8 @@ void main() {
|
|||||||
color: const Color(0xFF000000),
|
color: const Color(0xFF000000),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -301,6 +307,8 @@ void main() {
|
|||||||
color: _kDefaultIdleThumbColor,
|
color: _kDefaultIdleThumbColor,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.fuchsia }));
|
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.fuchsia }));
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('Scrollbar.interactive takes priority over ScrollbarTheme', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Scrollbar.interactive takes priority over ScrollbarTheme', (WidgetTester tester) async {
|
||||||
@ -349,6 +357,8 @@ void main() {
|
|||||||
color: _kDefaultIdleThumbColor,
|
color: _kDefaultIdleThumbColor,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.fuchsia }));
|
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.fuchsia }));
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('Scrollbar widget properties take priority over theme', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('Scrollbar widget properties take priority over theme', (WidgetTester tester) async {
|
||||||
@ -442,6 +452,8 @@ void main() {
|
|||||||
color: const Color(0x80000000),
|
color: const Color(0x80000000),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{
|
}, variant: const TargetPlatformVariant(<TargetPlatform>{
|
||||||
TargetPlatform.linux,
|
TargetPlatform.linux,
|
||||||
TargetPlatform.macOS,
|
TargetPlatform.macOS,
|
||||||
@ -451,30 +463,34 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
testWidgetsWithLeakTracking('ThemeData colorScheme is used when no ScrollbarTheme is set', (WidgetTester tester) async {
|
testWidgetsWithLeakTracking('ThemeData colorScheme is used when no ScrollbarTheme is set', (WidgetTester tester) async {
|
||||||
Widget buildFrame(ThemeData appTheme) {
|
(ScrollController, Widget) buildFrame(ThemeData appTheme) {
|
||||||
final ScrollController scrollController = ScrollController();
|
final ScrollController scrollController = ScrollController();
|
||||||
return MaterialApp(
|
return (
|
||||||
theme: appTheme,
|
scrollController,
|
||||||
home: ScrollConfiguration(
|
MaterialApp(
|
||||||
behavior: const NoScrollbarBehavior(),
|
theme: appTheme,
|
||||||
child: Scrollbar(
|
home: ScrollConfiguration(
|
||||||
thumbVisibility: true,
|
behavior: const NoScrollbarBehavior(),
|
||||||
showTrackOnHover: true,
|
child: Scrollbar(
|
||||||
controller: scrollController,
|
thumbVisibility: true,
|
||||||
child: SingleChildScrollView(
|
showTrackOnHover: true,
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
child: const SizedBox(width: 4000.0, height: 4000.0),
|
child: SingleChildScrollView(
|
||||||
|
controller: scrollController,
|
||||||
|
child: const SizedBox(width: 4000.0, height: 4000.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scrollbar defaults for light themes:
|
// Scrollbar defaults for light themes:
|
||||||
// - coloring based on ColorScheme.onSurface
|
// - coloring based on ColorScheme.onSurface
|
||||||
await tester.pumpWidget(buildFrame(ThemeData(
|
final (ScrollController controller1, Widget frame1) = buildFrame(ThemeData(
|
||||||
colorScheme: const ColorScheme.light(),
|
colorScheme: const ColorScheme.light(),
|
||||||
)));
|
));
|
||||||
|
await tester.pumpWidget(frame1);
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
// Idle scrollbar behavior
|
// Idle scrollbar behavior
|
||||||
expect(
|
expect(
|
||||||
@ -545,9 +561,10 @@ void main() {
|
|||||||
|
|
||||||
// Scrollbar defaults for dark themes:
|
// Scrollbar defaults for dark themes:
|
||||||
// - coloring slightly different based on ColorScheme.onSurface
|
// - coloring slightly different based on ColorScheme.onSurface
|
||||||
await tester.pumpWidget(buildFrame(ThemeData(
|
final (ScrollController controller2, Widget frame2) = buildFrame(ThemeData(
|
||||||
colorScheme: const ColorScheme.dark(),
|
colorScheme: const ColorScheme.dark(),
|
||||||
)));
|
));
|
||||||
|
await tester.pumpWidget(frame2);
|
||||||
await tester.pumpAndSettle(); // Theme change animation
|
await tester.pumpAndSettle(); // Theme change animation
|
||||||
|
|
||||||
// Idle scrollbar behavior
|
// Idle scrollbar behavior
|
||||||
@ -610,6 +627,9 @@ void main() {
|
|||||||
color: const Color(0xa6ffffff),
|
color: const Color(0xa6ffffff),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
controller1.dispose();
|
||||||
|
controller2.dispose();
|
||||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{
|
}, variant: const TargetPlatformVariant(<TargetPlatform>{
|
||||||
TargetPlatform.linux,
|
TargetPlatform.linux,
|
||||||
TargetPlatform.macOS,
|
TargetPlatform.macOS,
|
||||||
@ -656,6 +676,8 @@ void main() {
|
|||||||
)
|
)
|
||||||
..rrect(color: const Color(0xff4caf50)),
|
..rrect(color: const Color(0xff4caf50)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
scrollController.dispose();
|
||||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{
|
}, variant: const TargetPlatformVariant(<TargetPlatform>{
|
||||||
TargetPlatform.linux,
|
TargetPlatform.linux,
|
||||||
TargetPlatform.macOS,
|
TargetPlatform.macOS,
|
||||||
|
@ -81,5 +81,12 @@ void main() {
|
|||||||
// should not crash.
|
// should not crash.
|
||||||
await tester.tap(find.text('Tab 2'));
|
await tester.tap(find.text('Tab 2'));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
});
|
},
|
||||||
|
// TODO(someone): remove after fixing
|
||||||
|
// https://github.com/flutter/flutter/issues/133755
|
||||||
|
leakTrackingTestConfig: const LeakTrackingTestConfig(
|
||||||
|
notDisposedAllowList: <String, int?>{
|
||||||
|
'PageController': 1,
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
@ -393,4 +394,21 @@ void main() {
|
|||||||
// should have been true
|
// should have been true
|
||||||
expect(isScrolling, isTrue);
|
expect(isScrolling, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('$ScrollController dispatches object creation in constructor', () {
|
||||||
|
final List<ObjectEvent> events = <ObjectEvent>[];
|
||||||
|
void listener(ObjectEvent event) {
|
||||||
|
if (event.object.runtimeType == ScrollController) {
|
||||||
|
events.add(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MemoryAllocations.instance.addListener(listener);
|
||||||
|
|
||||||
|
final ScrollController controller = ScrollController();
|
||||||
|
|
||||||
|
expect(events, hasLength(1));
|
||||||
|
|
||||||
|
controller.dispose();
|
||||||
|
MemoryAllocations.instance.removeListener(listener);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user