Flutter:嵌套滚动条和 Windows 水平滚动条不起作用
Futter: Nested Scroll Bars and Windows Horizontal Scroll Not Working
我有 2 个 issues/queries 关于实现特定效果。
我想达到this的效果,我已经实现了一个解决方案(老实说是两个)但是它们都不起作用。
详情如下:
这是我实现的第一个解决方案的代码,在这里你可以看到两个滚动条,但是当水平滚动条滚动时(不是通过触控板,因为触控板水平滚动不起作用),垂直方向也会滚动。
class ScrollPanWidget extends StatefulWidget {
final Widget child;
const ScrollPanWidget({Key? key, required this.child}) : super(key: key);
@override
State<ScrollPanWidget> createState() => _ScrollPanWidgetState();
}
class _ScrollPanWidgetState extends State<ScrollPanWidget> {
late ScrollController verticalController, horizontalController;
@override
void initState() {
super.initState();
verticalController = ScrollController();
horizontalController = ScrollController();
}
@override
Widget build(BuildContext context) {
return _HorizontalPart(
horizontalController: horizontalController,
child: _VerticalPart(
verticalController: verticalController, child: widget.child),
);
}
}
class _HorizontalPart extends StatelessWidget {
final ScrollController horizontalController;
final Widget child;
const _HorizontalPart(
{Key? key, required this.horizontalController, required this.child})
: super(key: key);
@override
Widget build(BuildContext context) {
return RawScrollbar(
thumbVisibility: true,
trackColor: Colors.grey,
thumbColor: Theme.of(context).colorScheme.primary.withOpacity(0.8),
trackBorderColor: Colors.blueGrey,
controller: horizontalController,
scrollbarOrientation: ScrollbarOrientation.top,
child: SingleChildScrollView(
controller: horizontalController,
scrollDirection: Axis.horizontal,
child: child,
),
);
}
}
class _VerticalPart extends StatelessWidget {
final ScrollController verticalController;
final Widget child;
const _VerticalPart(
{Key? key, required this.verticalController, required this.child})
: super(key: key);
@override
Widget build(BuildContext context) {
return RawScrollbar(
thumbVisibility: true,
trackColor: Colors.grey,
thumbColor: Theme.of(context).colorScheme.primary.withOpacity(0.8),
trackBorderColor: Colors.blueGrey,
scrollbarOrientation: ScrollbarOrientation.left,
controller: verticalController,
child: SingleChildScrollView(
controller: verticalController,
scrollDirection: Axis.vertical,
child: child,
),
);
}
}
- 这里是我实现的方案二,可以替换成ScrollPanWidget的build方法。在此解决方案中,其中一个滚动条未显示。图片 here
Stack(
children: [
SingleChildScrollView(
controller: verticalController,
scrollDirection: Axis.vertical,
child: SingleChildScrollView(
controller: horizontalController,
scrollDirection: Axis.horizontal,
child: widget.child,
),
),
RawScrollbar(
thumbVisibility: true,
trackColor: Colors.grey,
thumbColor: Theme.of(context).colorScheme.primary.withOpacity(0.8),
trackBorderColor: Colors.blueGrey,
controller: verticalController,
scrollbarOrientation: ScrollbarOrientation.right,
child: RawScrollbar(
thumbVisibility: true,
trackColor: Colors.grey,
thumbColor: Theme.of(context).colorScheme.primary.withOpacity(0.8),
trackBorderColor: Colors.blueGrey,
scrollbarOrientation: ScrollbarOrientation.bottom,
controller: horizontalController,
child: Column(
mainAxisSize: MainAxisSize.max,
children: const [],
),
),
),
],
);
如果我的解决方案不起作用,您能否指出我应该努力实现相同目标的方向(不要求实际代码,因此根据指南,这不是一个广泛的问题)。
在 windows 上,当水平滚动时,Flutter 会更改小部件的焦点,而不是将滚动事件传递给 Horizontal ScrollViews。在这方面有什么可以做的吗?在 Flutter Web 上未观察到此行为。
此外,我确实想让小部件响应触摸和鼠标输入以进行平移(鼠标输入不是必需的,因为可以忽略它们以支持将小部件滚动到第 2 点) .那么,您是否建议将 GestureDetector
和 Stack
的组合与子小部件放在定位小部件中进行平移?还是使用 GestureDetector 和 ScrollController 平移子 Widget 更好?
提前感谢您的帮助!
完整代码:
import 'package:flutter/material.dart';
class NestedScrollbarsNotShowingApp extends StatefulWidget {
const NestedScrollbarsNotShowingApp({Key? key}) : super(key: key);
@override
State<NestedScrollbarsNotShowingApp> createState() =>
_NestedScrollbarsNotShowingAppState();
}
class _NestedScrollbarsNotShowingAppState
extends State<NestedScrollbarsNotShowingApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
home: Scaffold(
appBar: AppBar(
title: const Text('Classic Material App Bar'),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: ScrollPanWidget(
child: Container(
color: Colors.red,
height: 1200,
width: 1500,
),
),
)),
),
);
}
}
class ScrollPanWidget extends StatefulWidget {
final Widget child;
const ScrollPanWidget({Key? key, required this.child}) : super(key: key);
@override
State<ScrollPanWidget> createState() => _ScrollPanWidgetState();
}
class _ScrollPanWidgetState extends State<ScrollPanWidget> {
late ScrollController verticalController, horizontalController;
@override
void initState() {
super.initState();
verticalController = ScrollController();
horizontalController = ScrollController();
}
@override
Widget build(BuildContext context) {
return _HorizontalPart(
horizontalController: horizontalController,
child: _VerticalPart(
verticalController: verticalController, child: widget.child),
);
}
solutionThatDoesntWork() {
return Stack(
children: [
SingleChildScrollView(
controller: verticalController,
scrollDirection: Axis.vertical,
child: SingleChildScrollView(
controller: horizontalController,
scrollDirection: Axis.horizontal,
child: widget.child,
),
),
RawScrollbar(
thumbVisibility: true,
trackColor: Colors.grey,
thumbColor: Theme.of(context).colorScheme.primary.withOpacity(0.8),
trackBorderColor: Colors.blueGrey,
controller: verticalController,
scrollbarOrientation: ScrollbarOrientation.right,
child: RawScrollbar(
thumbVisibility: true,
trackColor: Colors.grey,
thumbColor: Theme.of(context).colorScheme.primary.withOpacity(0.8),
trackBorderColor: Colors.blueGrey,
scrollbarOrientation: ScrollbarOrientation.bottom,
controller: horizontalController,
child: Column(
mainAxisSize: MainAxisSize.max,
children: const [],
),
),
),
],
);
}
}
class _HorizontalPart extends StatelessWidget {
final ScrollController horizontalController;
final Widget child;
const _HorizontalPart(
{Key? key, required this.horizontalController, required this.child})
: super(key: key);
@override
Widget build(BuildContext context) {
return RawScrollbar(
thumbVisibility: true,
trackColor: Colors.grey,
thumbColor: Theme.of(context).colorScheme.primary.withOpacity(0.8),
trackBorderColor: Colors.blueGrey,
controller: horizontalController,
scrollbarOrientation: ScrollbarOrientation.top,
child: SingleChildScrollView(
controller: horizontalController,
scrollDirection: Axis.horizontal,
child: child,
),
);
}
}
class _VerticalPart extends StatelessWidget {
final ScrollController verticalController;
final Widget child;
const _VerticalPart(
{Key? key, required this.verticalController, required this.child})
: super(key: key);
@override
Widget build(BuildContext context) {
return RawScrollbar(
thumbVisibility: true,
trackColor: Colors.grey,
thumbColor: Theme.of(context).colorScheme.primary.withOpacity(0.8),
trackBorderColor: Colors.blueGrey,
scrollbarOrientation: ScrollbarOrientation.left,
controller: verticalController,
child: SingleChildScrollView(
controller: verticalController,
scrollDirection: Axis.vertical,
child: child,
),
);
}
}
所以,我实际上自己实现了解决方案并将其发布为 pub 包,dual_scroll。您可以通过以下方式直接使用:
从导入包开始
import 'package:dual_scroll/dual_scroll.dart';
通过包装您希望以下列方式滚动的小部件来使用:
return DualScroll(
verticalScrollbar: ScrollBar.defaultScrollBar(),
horizontalScrollbar: ScrollBar.defaultScrollBar(),
child: Container(), /* Your child widget here*/
);
有关高级用法,请参阅 pub.dev
我有 2 个 issues/queries 关于实现特定效果。
我想达到this的效果,我已经实现了一个解决方案(老实说是两个)但是它们都不起作用。 详情如下:
这是我实现的第一个解决方案的代码,在这里你可以看到两个滚动条,但是当水平滚动条滚动时(不是通过触控板,因为触控板水平滚动不起作用),垂直方向也会滚动。
class ScrollPanWidget extends StatefulWidget { final Widget child; const ScrollPanWidget({Key? key, required this.child}) : super(key: key); @override State<ScrollPanWidget> createState() => _ScrollPanWidgetState(); } class _ScrollPanWidgetState extends State<ScrollPanWidget> { late ScrollController verticalController, horizontalController; @override void initState() { super.initState(); verticalController = ScrollController(); horizontalController = ScrollController(); } @override Widget build(BuildContext context) { return _HorizontalPart( horizontalController: horizontalController, child: _VerticalPart( verticalController: verticalController, child: widget.child), ); } } class _HorizontalPart extends StatelessWidget { final ScrollController horizontalController; final Widget child; const _HorizontalPart( {Key? key, required this.horizontalController, required this.child}) : super(key: key); @override Widget build(BuildContext context) { return RawScrollbar( thumbVisibility: true, trackColor: Colors.grey, thumbColor: Theme.of(context).colorScheme.primary.withOpacity(0.8), trackBorderColor: Colors.blueGrey, controller: horizontalController, scrollbarOrientation: ScrollbarOrientation.top, child: SingleChildScrollView( controller: horizontalController, scrollDirection: Axis.horizontal, child: child, ), ); } } class _VerticalPart extends StatelessWidget { final ScrollController verticalController; final Widget child; const _VerticalPart( {Key? key, required this.verticalController, required this.child}) : super(key: key); @override Widget build(BuildContext context) { return RawScrollbar( thumbVisibility: true, trackColor: Colors.grey, thumbColor: Theme.of(context).colorScheme.primary.withOpacity(0.8), trackBorderColor: Colors.blueGrey, scrollbarOrientation: ScrollbarOrientation.left, controller: verticalController, child: SingleChildScrollView( controller: verticalController, scrollDirection: Axis.vertical, child: child, ), ); } }
- 这里是我实现的方案二,可以替换成ScrollPanWidget的build方法。在此解决方案中,其中一个滚动条未显示。图片 here
Stack(
children: [
SingleChildScrollView(
controller: verticalController,
scrollDirection: Axis.vertical,
child: SingleChildScrollView(
controller: horizontalController,
scrollDirection: Axis.horizontal,
child: widget.child,
),
),
RawScrollbar(
thumbVisibility: true,
trackColor: Colors.grey,
thumbColor: Theme.of(context).colorScheme.primary.withOpacity(0.8),
trackBorderColor: Colors.blueGrey,
controller: verticalController,
scrollbarOrientation: ScrollbarOrientation.right,
child: RawScrollbar(
thumbVisibility: true,
trackColor: Colors.grey,
thumbColor: Theme.of(context).colorScheme.primary.withOpacity(0.8),
trackBorderColor: Colors.blueGrey,
scrollbarOrientation: ScrollbarOrientation.bottom,
controller: horizontalController,
child: Column(
mainAxisSize: MainAxisSize.max,
children: const [],
),
),
),
],
);
如果我的解决方案不起作用,您能否指出我应该努力实现相同目标的方向(不要求实际代码,因此根据指南,这不是一个广泛的问题)。
在 windows 上,当水平滚动时,Flutter 会更改小部件的焦点,而不是将滚动事件传递给 Horizontal ScrollViews。在这方面有什么可以做的吗?在 Flutter Web 上未观察到此行为。
此外,我确实想让小部件响应触摸和鼠标输入以进行平移(鼠标输入不是必需的,因为可以忽略它们以支持将小部件滚动到第 2 点) .那么,您是否建议将
GestureDetector
和Stack
的组合与子小部件放在定位小部件中进行平移?还是使用 GestureDetector 和 ScrollController 平移子 Widget 更好?
提前感谢您的帮助!
完整代码:
import 'package:flutter/material.dart';
class NestedScrollbarsNotShowingApp extends StatefulWidget {
const NestedScrollbarsNotShowingApp({Key? key}) : super(key: key);
@override
State<NestedScrollbarsNotShowingApp> createState() =>
_NestedScrollbarsNotShowingAppState();
}
class _NestedScrollbarsNotShowingAppState
extends State<NestedScrollbarsNotShowingApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
home: Scaffold(
appBar: AppBar(
title: const Text('Classic Material App Bar'),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: ScrollPanWidget(
child: Container(
color: Colors.red,
height: 1200,
width: 1500,
),
),
)),
),
);
}
}
class ScrollPanWidget extends StatefulWidget {
final Widget child;
const ScrollPanWidget({Key? key, required this.child}) : super(key: key);
@override
State<ScrollPanWidget> createState() => _ScrollPanWidgetState();
}
class _ScrollPanWidgetState extends State<ScrollPanWidget> {
late ScrollController verticalController, horizontalController;
@override
void initState() {
super.initState();
verticalController = ScrollController();
horizontalController = ScrollController();
}
@override
Widget build(BuildContext context) {
return _HorizontalPart(
horizontalController: horizontalController,
child: _VerticalPart(
verticalController: verticalController, child: widget.child),
);
}
solutionThatDoesntWork() {
return Stack(
children: [
SingleChildScrollView(
controller: verticalController,
scrollDirection: Axis.vertical,
child: SingleChildScrollView(
controller: horizontalController,
scrollDirection: Axis.horizontal,
child: widget.child,
),
),
RawScrollbar(
thumbVisibility: true,
trackColor: Colors.grey,
thumbColor: Theme.of(context).colorScheme.primary.withOpacity(0.8),
trackBorderColor: Colors.blueGrey,
controller: verticalController,
scrollbarOrientation: ScrollbarOrientation.right,
child: RawScrollbar(
thumbVisibility: true,
trackColor: Colors.grey,
thumbColor: Theme.of(context).colorScheme.primary.withOpacity(0.8),
trackBorderColor: Colors.blueGrey,
scrollbarOrientation: ScrollbarOrientation.bottom,
controller: horizontalController,
child: Column(
mainAxisSize: MainAxisSize.max,
children: const [],
),
),
),
],
);
}
}
class _HorizontalPart extends StatelessWidget {
final ScrollController horizontalController;
final Widget child;
const _HorizontalPart(
{Key? key, required this.horizontalController, required this.child})
: super(key: key);
@override
Widget build(BuildContext context) {
return RawScrollbar(
thumbVisibility: true,
trackColor: Colors.grey,
thumbColor: Theme.of(context).colorScheme.primary.withOpacity(0.8),
trackBorderColor: Colors.blueGrey,
controller: horizontalController,
scrollbarOrientation: ScrollbarOrientation.top,
child: SingleChildScrollView(
controller: horizontalController,
scrollDirection: Axis.horizontal,
child: child,
),
);
}
}
class _VerticalPart extends StatelessWidget {
final ScrollController verticalController;
final Widget child;
const _VerticalPart(
{Key? key, required this.verticalController, required this.child})
: super(key: key);
@override
Widget build(BuildContext context) {
return RawScrollbar(
thumbVisibility: true,
trackColor: Colors.grey,
thumbColor: Theme.of(context).colorScheme.primary.withOpacity(0.8),
trackBorderColor: Colors.blueGrey,
scrollbarOrientation: ScrollbarOrientation.left,
controller: verticalController,
child: SingleChildScrollView(
controller: verticalController,
scrollDirection: Axis.vertical,
child: child,
),
);
}
}
所以,我实际上自己实现了解决方案并将其发布为 pub 包,dual_scroll。您可以通过以下方式直接使用:
从导入包开始
import 'package:dual_scroll/dual_scroll.dart';
通过包装您希望以下列方式滚动的小部件来使用:
return DualScroll(
verticalScrollbar: ScrollBar.defaultScrollBar(),
horizontalScrollbar: ScrollBar.defaultScrollBar(),
child: Container(), /* Your child widget here*/
);
有关高级用法,请参阅 pub.dev