-
Notifications
You must be signed in to change notification settings - Fork 29.8k
Closed
Closed
Copy link
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work listc: regressionIt was better in the past than it is nowIt was better in the past than it is nowfound in release: 3.24Found to occur in 3.24Found to occur in 3.24found in release: 3.25Found to occur in 3.25Found to occur in 3.25frameworkflutter/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 onr: fixedIssue is closed as already fixed in a newer versionIssue is closed as already fixed in a newer versionteam-frameworkOwned by Framework teamOwned by Framework teamtriaged-frameworkTriaged by Framework teamTriaged by Framework team
Description
Steps to reproduce
- Run the code sample
- Scroll by dragging one of the scrollbars
- Try to scroll by dragging the other scrollbar
- (When trying to drag the same scrollbar twice, it works properly the second time)
Expected results
Dragging the scrollbar should always move the scrollbar and scrollview in the first dragging attempt.
Actual results
The scrollbar jolts a bit but then the scrollbar isn't draggable anymore until you try again.
Code sample
Code sample
import 'dart:math' as math;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
debugShowCheckedModeBanner: false,
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({
super.key,
required this.title,
});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final ScrollController _horizontalController = ScrollController();
final ScrollController _verticalController = ScrollController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Scrollbar(
controller: _verticalController,
child: Scrollbar(
controller: _horizontalController,
child: ScrollConfiguration(
behavior: const MaterialScrollBehavior().copyWith(
dragDevices: PointerDeviceKind.values.toSet(),
),
child: TwoDimensionalGridView(
horizontalDetails: ScrollableDetails.horizontal(
controller: _horizontalController,
),
verticalDetails: ScrollableDetails.vertical(
controller: _verticalController,
),
diagonalDragBehavior: DiagonalDragBehavior.free,
delegate: TwoDimensionalChildBuilderDelegate(
maxXIndex: 9,
maxYIndex: 9,
builder: (BuildContext context, ChildVicinity vicinity) {
return Container(
color: vicinity.xIndex.isEven && vicinity.yIndex.isEven
? Colors.amber[200]
: (vicinity.xIndex.isOdd && vicinity.yIndex.isOdd
? Colors.purple[200]
: Colors.blueGrey[200]),
height: 200,
width: 200,
child: Center(
child: Text(
'Row ${vicinity.yIndex}: Column ${vicinity.xIndex}',
),
),
);
},
),
),
),
),
),
);
}
}
class TwoDimensionalGridView extends TwoDimensionalScrollView {
const TwoDimensionalGridView({
super.key,
super.primary,
super.mainAxis = Axis.vertical,
super.verticalDetails = const ScrollableDetails.vertical(),
super.horizontalDetails = const ScrollableDetails.horizontal(),
required TwoDimensionalChildBuilderDelegate delegate,
super.cacheExtent,
super.diagonalDragBehavior = DiagonalDragBehavior.none,
super.dragStartBehavior = DragStartBehavior.start,
super.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
super.clipBehavior = Clip.hardEdge,
}) : super(delegate: delegate);
@override
Widget buildViewport(
BuildContext context,
ViewportOffset verticalOffset,
ViewportOffset horizontalOffset,
) {
return TwoDimensionalGridViewport(
horizontalOffset: horizontalOffset,
horizontalAxisDirection: horizontalDetails.direction,
verticalOffset: verticalOffset,
verticalAxisDirection: verticalDetails.direction,
mainAxis: mainAxis,
delegate: delegate as TwoDimensionalChildBuilderDelegate,
cacheExtent: cacheExtent,
clipBehavior: clipBehavior,
);
}
}
class TwoDimensionalGridViewport extends TwoDimensionalViewport {
const TwoDimensionalGridViewport({
super.key,
required super.verticalOffset,
required super.verticalAxisDirection,
required super.horizontalOffset,
required super.horizontalAxisDirection,
required TwoDimensionalChildBuilderDelegate super.delegate,
required super.mainAxis,
super.cacheExtent,
super.clipBehavior = Clip.hardEdge,
});
@override
RenderTwoDimensionalViewport createRenderObject(BuildContext context) {
return RenderTwoDimensionalGridViewport(
horizontalOffset: horizontalOffset,
horizontalAxisDirection: horizontalAxisDirection,
verticalOffset: verticalOffset,
verticalAxisDirection: verticalAxisDirection,
mainAxis: mainAxis,
delegate: delegate as TwoDimensionalChildBuilderDelegate,
childManager: context as TwoDimensionalChildManager,
cacheExtent: cacheExtent,
clipBehavior: clipBehavior,
);
}
@override
void updateRenderObject(
BuildContext context,
RenderTwoDimensionalGridViewport renderObject,
) {
renderObject
..horizontalOffset = horizontalOffset
..horizontalAxisDirection = horizontalAxisDirection
..verticalOffset = verticalOffset
..verticalAxisDirection = verticalAxisDirection
..mainAxis = mainAxis
..delegate = delegate
..cacheExtent = cacheExtent
..clipBehavior = clipBehavior;
}
}
class RenderTwoDimensionalGridViewport extends RenderTwoDimensionalViewport {
RenderTwoDimensionalGridViewport({
required super.horizontalOffset,
required super.horizontalAxisDirection,
required super.verticalOffset,
required super.verticalAxisDirection,
required TwoDimensionalChildBuilderDelegate delegate,
required super.mainAxis,
required super.childManager,
super.cacheExtent,
super.clipBehavior = Clip.hardEdge,
}) : super(delegate: delegate);
@override
void layoutChildSequence() {
final double horizontalPixels = horizontalOffset.pixels;
final double verticalPixels = verticalOffset.pixels;
final double viewportWidth = viewportDimension.width + cacheExtent;
final double viewportHeight = viewportDimension.height + cacheExtent;
final TwoDimensionalChildBuilderDelegate builderDelegate =
delegate as TwoDimensionalChildBuilderDelegate;
final int maxRowIndex = builderDelegate.maxYIndex!;
final int maxColumnIndex = builderDelegate.maxXIndex!;
final int leadingColumn = math.max((horizontalPixels / 200).floor(), 0);
final int leadingRow = math.max((verticalPixels / 200).floor(), 0);
final int trailingColumn = math.min(
((horizontalPixels + viewportWidth) / 200).ceil(),
maxColumnIndex,
);
final int trailingRow = math.min(
((verticalPixels + viewportHeight) / 200).ceil(),
maxRowIndex,
);
double xLayoutOffset = (leadingColumn * 200) - horizontalOffset.pixels;
for (int column = leadingColumn; column <= trailingColumn; column++) {
double yLayoutOffset = (leadingRow * 200) - verticalOffset.pixels;
for (int row = leadingRow; row <= trailingRow; row++) {
final ChildVicinity vicinity =
ChildVicinity(xIndex: column, yIndex: row);
final RenderBox child = buildOrObtainChildFor(vicinity)!;
child.layout(constraints.loosen());
parentDataOf(child).layoutOffset = Offset(xLayoutOffset, yLayoutOffset);
yLayoutOffset += 200;
}
xLayoutOffset += 200;
}
final double verticalExtent = 200 * (maxRowIndex + 1);
verticalOffset.applyContentDimensions(
0.0,
clampDouble(
verticalExtent - viewportDimension.height, 0.0, double.infinity),
);
final double horizontalExtent = 200 * (maxColumnIndex + 1);
horizontalOffset.applyContentDimensions(
0.0,
clampDouble(
horizontalExtent - viewportDimension.width, 0.0, double.infinity),
);
}
}
Screenshots or Video
Screenshots / Video demonstration
Flutter_Scrollbar_Move_Bug.mp4
Additional information
It seems that this issue only occurs on newer Flutter versions because I made a recording for another bug concerning the TwoDimesinoalGridView and there I had a slightly older Flutter version but there scrollbar dragging works as expected. The last working version is 3.23.0-0.1.pre
Flutter Doctor output
Doctor output
[✓] Flutter (Channel stable, 3.24.1, on Fedora Linux 40 (Workstation Edition)
6.10.6-200.fc40.x86_64, locale de_DE.UTF-8)
• Flutter version 3.24.1 on channel stable at ~/.local/share/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 5874a72aa4 (13 days ago), 2024-08-20 16:46:00 -0500
• Engine revision c9b9d5780d
• Dart version 3.5.1
• DevTools version 2.37.2
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.2)
• Android SDK at ~/.local/share/android-sdk
• Platform android-34, build-tools 33.0.2
• ANDROID_HOME = ~/.local/share/android-sdk/
• Java binary at: /usr/bin/java
• Java version OpenJDK Runtime Environment (Red_Hat-17.0.12.0.7-2) (build 17.0.12+7)
• All Android licenses accepted.
[✗] Chrome - develop for the web (Cannot find Chrome executable at google-chrome)
! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.
[✓] Linux toolchain - develop for Linux desktop
• clang version 18.1.6 (Fedora 18.1.6-3.fc40)
• cmake version 3.28.2
• ninja version 1.12.1
• pkg-config version 2.1.1
[!] Android Studio (not installed)
• Android Studio not found; download from
https://developer.android.com/studio/index.html
(or visit https://flutter.dev/to/linux-android-setup for detailed instructions).
[✓] Connected device (1 available)
• Linux (desktop) • linux • linux-x64 • Fedora Linux 40 (Workstation Edition)
6.10.6-200.fc40.x86_64
[✓] Network resources
• All expected network resources are available.
! Doctor found issues in 2 categories.
Metadata
Metadata
Assignees
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work listc: regressionIt was better in the past than it is nowIt was better in the past than it is nowfound in release: 3.24Found to occur in 3.24Found to occur in 3.24found in release: 3.25Found to occur in 3.25Found to occur in 3.25frameworkflutter/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 onr: fixedIssue is closed as already fixed in a newer versionIssue is closed as already fixed in a newer versionteam-frameworkOwned by Framework teamOwned by Framework teamtriaged-frameworkTriaged by Framework teamTriaged by Framework team