Repository navigation
Flinging in PageView depends on rendering time, leading to inconsistent behaviour #174904
Copy link
Copy link
Open
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work listc: performanceRelates to speed or footprint issues (see "perf:" labels)Relates to speed or footprint issues (see "perf:" labels)f: gesturesflutter/packages/flutter/gestures repository.flutter/packages/flutter/gestures repository.found in release: 3.35Found to occur in 3.35Found to occur in 3.35found in release: 3.36Found to occur in 3.36Found to occur in 3.36frameworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work onperf: speedPerformance issues related to (mostly rendering) speedPerformance issues related to (mostly rendering) speedteam-frameworkOwned by Framework teamOwned by Framework teamtriaged-frameworkTriaged by Framework teamTriaged by Framework team
Description
Steps to reproduce
Run the example code.
Expected results
Heavy Build: disabled and Heavy Build: enabled (shown in the example) should not differ in their behaviour.
The flinging should work, regardless of long build times.
Actual results
Heavy Build: enabled flings back to original page, even when flinging with great velocity. This is undesired.
Code sample
Code sample
import 'dart:math';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'PageView Bug',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
),
home: ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(
// This is required to swipe left and right on windows
dragDevices: {PointerDeviceKind.touch, PointerDeviceKind.mouse},
),
child: Scaffold(
appBar: AppBar(
title: Text(
"PageView Bug - Fling not working correctly if Page takes more time to build",
),
),
body: Column(children: [Expanded(child: Content())]),
),
),
);
}
}
class Content extends StatefulWidget {
const Content({super.key});
@override
State<Content> createState() => _ContentState();
}
class _ContentState extends State<Content> {
ValueNotifier<bool> heavyVn = ValueNotifier<bool>(true);
final ValueNotifier<String> startEventVn = ValueNotifier<String>(
"ScrollStartNotification: -",
);
final ValueNotifier<String> userEventVn = ValueNotifier<String>(
"UserScrollNotification: -",
);
final ValueNotifier<String> updateEventVn = ValueNotifier<String>(
"ScrollUpdateNotification: -",
);
final ValueNotifier<String> endEventVn = ValueNotifier<String>(
"ScrollEndNotification: -",
);
void _handleScrollNotification(ScrollNotification notification) {
if (notification is ScrollStartNotification) {
startEventVn.value =
"ScrollStartNotification → Dragging=${notification.dragDetails != null}";
} else if (notification is UserScrollNotification) {
userEventVn.value =
"UserScrollNotification → Direction=${notification.direction}";
} else if (notification is ScrollUpdateNotification) {
updateEventVn.value =
"ScrollUpdateNotification → Δ=${notification.scrollDelta?.toStringAsFixed(2)}";
} else if (notification is ScrollEndNotification) {
endEventVn.value = "ScrollEndNotification → ${notification.toString()}";
}
}
void reset() {
startEventVn.value = "ScrollStartNotification →";
userEventVn.value = "UserScrollNotification →";
updateEventVn.value = "ScrollUpdateNotification →";
endEventVn.value = "ScrollEndNotification →";
}
Widget _buildEventText(ValueNotifier<String> vn) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: ValueListenableBuilder<String>(
valueListenable: vn,
builder: (context, value, _) => Text(
value,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
),
),
);
}
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<bool>(
valueListenable: heavyVn,
builder: (context, enableExpensiveBuildTime, child) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildEventText(startEventVn),
_buildEventText(userEventVn),
_buildEventText(updateEventVn),
_buildEventText(endEventVn),
Row(
children: [
Text("Heavy Build: ${heavyVn.value ? "enabled" : "disabled"}"),
Checkbox(
value: heavyVn.value,
onChanged: (value) {
reset();
heavyVn.value = value ?? true;
},
),
],
),
const Divider(),
Expanded(
child: NotificationListener<ScrollNotification>(
onNotification: (notification) {
_handleScrollNotification(notification);
return true;
},
child: FlutterPageView(heavy: enableExpensiveBuildTime),
),
),
],
),
);
}
}
class FlutterPageView extends StatelessWidget {
const FlutterPageView({super.key, required this.heavy});
final bool heavy;
@override
Widget build(BuildContext context) {
// this is purposefully inefficient
final list = List<int>.generate(
heavy ? 5000 : 10,
(int index) => index,
growable: false,
);
return PageView.builder(
itemBuilder: (context, index) {
return Container(
color: Colors.blue[(index * 100) % 1000],
child: Stack(
children: [
for (final item in list)
Positioned(
left: item.toDouble() * 10,
top: 120 + sin(item * 0.4) * 120 + item * 2.05,
child: Text(
index.toString(),
style: TextStyle(color: Colors.black, fontSize: item % 164),
),
),
],
),
);
},
itemCount: 20,
);
}
}
Screenshots or Video
page_view_corrupted.2025-09-03.22-33-46.mp4
Logs
Logs
[Paste your logs here]Flutter Doctor output
Doctor output
[√] Flutter (Channel stable, 3.35.2, on Microsoft Windows [Version 10.0.19045.3208], locale de-DE)
[√] Windows Version (10 64-Bit)
[√] Android toolchain - develop for Android devices (Android SDK version 35.0.1)
[√] Chrome - develop for the web
[√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.5.2)
[√] Android Studio (version 2024.3)
[√] VS Code, 64-bit edition (version 1.103.2)
[√] Connected device (3 available)
[√] Network resourcesReactions are currently unavailable
Metadata
Metadata
Assignees
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work listc: performanceRelates to speed or footprint issues (see "perf:" labels)Relates to speed or footprint issues (see "perf:" labels)f: gesturesflutter/packages/flutter/gestures repository.flutter/packages/flutter/gestures repository.found in release: 3.35Found to occur in 3.35Found to occur in 3.35found in release: 3.36Found to occur in 3.36Found to occur in 3.36frameworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work onperf: speedPerformance issues related to (mostly rendering) speedPerformance issues related to (mostly rendering) speedteam-frameworkOwned by Framework teamOwned by Framework teamtriaged-frameworkTriaged by Framework teamTriaged by Framework team
Activity
Eerey commentedon Sep 3, 2025
Just as a note:
I upgraded from Flutter 3.22.2 to 3.35.2 and this behaviour started to appear on all components that uses a PageView.
I wanted to implement my own ScrollPhysics to eliminate the problem. However, it appears to be impossible.
line 941https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/widgets/page_view.dartIf I understand it correctly, it is impossible to fully control the PageView with customized
ScrollPhysics, because the methods of the default_kPagePhysicswill always be prioritized over those that are set in the customizedwidget.physics:Documentation of
PageScrollPhysics applyTo(ScrollPhysics? ancestor)While testing I found out that
Simulation? createBallisticSimulation(ScrollMetrics position, double velocity)is executed twice. PageScrollPhysics first, then my Customized ScrollPhysics.This is undesired. I feel like I have to copy the entire PageView class and remove the _kPagePhysics to fully control my PageView. Am I missing something?
darshankawar commentedon Sep 4, 2025
Thanks for the detailed report. Seeing the same behavior as reported on latest sdk versions.
atototenten commentedon Sep 5, 2025
this behavior is everywhere in flutter ,due to its sync. rendering pipeline .
the item building in scrolling(including page-view) should be implicitly async. ,to disallow affecting/hijacking the other widgets .
like a well-traded-off react-fiber feature .
cc @Hixie
Eerey commentedon Sep 5, 2025
Up until now I was using flutter 3.22 and this did in fact not occur to me. This is a new phenomenon. If it worked in the past, it should work in the future.
This cannot always be guaranteed. Also, If a synced build is introduced by widgets outside of the
PageViewbut still visible on the screen, for example a complex Rive-Animation-Widget, the problem still persists. To me it does feel like a cheap performance hack, just to make a well-known gesture work again.Just as a note:
I tested this on touch devices with users and it was perceived as buggy and sluggish. Some users couldn't even fling to the next page.
atototenten commentedon Sep 5, 2025
this is understandable .
but this is not .im not talking about dart's async/await keywords ,but react's fiber concept .
this is also false ,and a mis-understanding .this "cheap" hack is a major feature of react ,which flutter lacks ,due to resource constraints ,and maybe politics .
kayson-argyle commentedon Dec 8, 2025
Any workarounds I can use or fixes in the works? I'm also experiencing this issue.
Eerey commentedon Dec 11, 2025
@kayson-argyle Not to my knowledge. It blocks my current migration plan, I have to stick to 3.22.
deckerst commentedon Mar 27, 2026
I experience the same phenomenon.
In my case, I see a difference between Flutter versions, as OP mentioned.
But I also see a difference whether thread merging is enabled (via the
DisableMergedPlatformUIThreadflag) while staying with Flutter v3.35.7:PageViewgesture works well,PageViewgesture aborts when loading the same page, but it became a "heavy build" because of threading.Eerey commentedon May 12, 2026
Hi there, just as an update I pulled 3.41.9 and this behaviour is still present.
This issue blocks us from moving from 3.22.2 to the most recent version.