颤动网页选项卡栏滚动控制器不响应键盘滚动

人气:1,187 发布:2022-10-16 标签: flutter flutter-web flutter-scrollbar

问题描述

我已经创建了两个选项卡。 在每个选项卡中,我都有用滚动条包装的SingleChildScrollView。 我不能在两个选项卡中都有主滚动控制器,因为这会引发异常:&ScrollController附加到多个滚动视图。"; 对于选项卡一,我使用主滚动控制器,对于选项卡二,我创建了滚动控制器并附加了它。 对于带有主滚动控制器的Tab One,我可以通过键盘和拖动滚动条进行滚动。 但对于带有非主滚动控制器的Tab 2,我只能通过拖动滚动条来滚动。此选项卡不响应键盘上翻页/下翻页键。

请检查我下面的代码。指导我如何实现Tab 2的键盘滚动。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: TabExample(),
    );
  }
}

class TabExample extends StatefulWidget {
  const TabExample({Key key}) : super(key: key);

  @override
  _TabExampleState createState() => _TabExampleState();
}

class _TabExampleState extends State<TabExample> {
  ScrollController _scrollController;

  @override
  void initState() {
    _scrollController = ScrollController();
    super.initState();
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 2,
      child: Scaffold(
        appBar: AppBar(
          bottom: TabBar(
            tabs: [
              Tab(icon: Text('Tab ONE')),
              Tab(icon: Text('Tab TWO')),
            ],
          ),
          title: Text('Tabs Demo'),
        ),
        body: TabBarView(
          children: [
            _buildWidgetA(),
            _buildWidgetB(),
          ],
        ),
      ),
    );
  }

  Widget _buildWidgetA() {
    List<Widget> children = [];
    for (int i = 0; i < 20; i++) {
      children.add(
        Padding(
          padding: EdgeInsets.symmetric(vertical: 16),
          child: Container(
            height: 100,
            width: double.infinity,
            color: Colors.black,
          ),
        ),
      );
    }
    return Scrollbar(
      isAlwaysShown: true,
      showTrackOnHover: true,
      child: SingleChildScrollView(
        child: Column(
          children: children,
        ),
      ),
    );
  }

  Widget _buildWidgetB() {
    List<Widget> children = [];
    for (int i = 0; i < 20; i++) {
      children.add(
        Padding(
          padding: EdgeInsets.symmetric(vertical: 16),
          child: Container(
            height: 100,
            width: double.infinity,
            color: Colors.green,
          ),
        ),
      );
    }
    return Scrollbar(
      controller: _scrollController,
      isAlwaysShown: true,
      showTrackOnHover: true,
      child: SingleChildScrollView(
        controller: _scrollController,
        child: Column(
          children: children,
        ),
      ),
    );
  }
}

推荐答案

无需创建显式ScrollController即可实现此目的。

一个技巧是更改SingleChildScrollView将在Tab更改其索引时使用PrimaryScrollController

因此,当我们听到选项卡已更改为索引0时,我们将设置第一个SingleChildScrolViewprimary。当它变为1时,我们将另一个设置为primary

首先创建一个新的State变量,如下所示

int currentIndex = 0; // This will be the index of tab at a point in time

若要侦听Change事件,您需要将侦听器添加到TabController

DefaultTabController(
  length: 2,
  child: Builder(  // <---- Use a Builder Widget to get the context this this DefaultTabController
    builder: (ctx) {

      // Here we need to use ctx instead of context otherwise it will give null
      final TabController tabController = DefaultTabController.of(ctx);

      tabController.addListener(() {
        if (!tabController.indexIsChanging) {

          // When the tab has changed we are changing our currentIndex to the new index
          setState(() => currentIndex = tabController.index);
        }
      });

      return Scaffold(
        appBar: AppBar(
          bottom: TabBar(
            tabs: [
              Tab(icon: Text('Tab ONE')),
              Tab(icon: Text('Tab TWO')),
            ],
          ),
          title: Text('Tabs Demo'),
        ),
        body: TabBarView(
          children: [
            _buildWidgetA(),
            _buildWidgetB(),
          ],
        ),
      );
    },
  ),
);

最后,根据currentIndexprimary: true设置为每个SingleChildScrollView

对于_buildWidgetA

Scrollbar(
  isAlwaysShown: true,
  showTrackOnHover: true,
  child: SingleChildScrollView(
    primary: currentIndex == 0,  // <--- This will be primary if currentIndex = 0
    child: Column(
      children: children,
    ),
  ),
);

对于_buildWidgetB

Scrollbar(
  isAlwaysShown: true,
  showTrackOnHover: true,
  child: SingleChildScrollView(
    primary: currentIndex == 1,  // <--- This will be primary if currentIndex = 1
    child: Column(
      children: children,
    ),
  ),
);

现在,您应该能够用键盘控制这两个选项卡了。

完整代码here

152