Flutter:嵌套滚动条和 Windows 水平滚动条不起作用

Futter: Nested Scroll Bars and Windows Horizontal Scroll Not Working

我有 2 个 issues/queries 关于实现特定效果。

  1. 我想达到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,
            ),
          );
        }
      }
      
    
    
   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 [],
         ),
       ),
     ),
   ],
 );

如果我的解决方案不起作用,您能否指出我应该努力实现相同目标的方向(不要求实际代码,因此根据指南,这不是一个广泛的问题)。

  1. 在 windows 上,当水平滚动时,Flutter 会更改小部件的焦点,而不是将滚动事件传递给 Horizo​​ntal ScrollViews。在这方面有什么可以做的吗?在 Flutter Web 上未观察到此行为。

  2. 此外,我确实想让小部件响应触摸和鼠标输入以进行平移(鼠标输入不是必需的,因为可以忽略它们以支持将小部件滚动到第 2 点) .那么,您是否建议将 GestureDetectorStack 的组合与子小部件放在定位小部件中进行平移?还是使用 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