Flutter:SingleChildScrollView 内的 TabBarView

问题描述 投票:0回答:4

我的问题是我想要在 SingleChildScrollView 中使用 TabBarView 但它给了我这个错误:

RenderFlex
子项具有非零弹性,但传入的高度约束是无界的。

如果我删除

SingleChildScrollView
它可以工作,但我需要这个小部件,因为然后我想推送一个随
TabBar
滚动的 ListView,就像 Instagram 一样。

代码:

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
  TabController tabController;

  @override
  void initState() {
    super.initState();
    tabController = new TabController(length: 2, vsync: this);
  }

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            body: SingleChildScrollView(
      child: Column(
        children: [
          TabBar(
            controller: tabController,
            tabs: [
              Tab(
                icon: Icon(
                  Icons.photo_library,
                  size: 30,
                ),
              ),
              Tab(
                icon: Icon(
                  Icons.perm_media,
                  size: 30,
                ),
              ),
            ],
            labelColor: Colors.deepOrange,
            unselectedLabelColor: Colors.black,
            indicatorColor: Colors.deepOrange,
          ),
          Expanded(
            child: TabBarView(controller: tabController, children: [
              Expanded(child: Container()),
              Expanded(child: Container())
            ]),
          ),
        ],
      ),
    )));
  }
}

变化:

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
  TabController tabController;

  @override
  void initState() {
    super.initState();
    tabController = new TabController(length: 2, vsync: this);
  }

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            body: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          TabBar(
            controller: tabController,
            tabs: [
              Tab(
                icon: Icon(
                  Icons.photo_library,
                  size: 30,
                ),
              ),
              Tab(
                icon: Icon(
                  Icons.perm_media,
                  size: 30,
                ),
              ),
            ],
            labelColor: Colors.deepOrange,
            unselectedLabelColor: Colors.black,
            indicatorColor: Colors.deepOrange,
          ),
          Expanded(
            child: SingleChildScrollView(child:TabBarView(controller: tabController, children: [
              Container(),
              Container()
            ]),
          ),)
        ],
      ),
    ));
  }
}
android flutter dart scroll widget
4个回答
20
投票

您可以使用

NestedScrollView
来解决此问题。

下面的代码帮助我解决了:

NestedScrollView(
      headerSliverBuilder: (context, value) {
        return [
          SliverToBoxAdapter(
              child: Header()
          ),
          SliverToBoxAdapter(
            child: TabBar(
              controller: _controller,
              tabs: [
                Tab(icon: Icon(Icons.x)),
                Tab(icon: Icon(Icons.y)),
                Tab(icon: Icon(Icons.z)),
              ],
            ),
          ),
        ];
      },
    body: Container(
      child: TabBarView(
        controller: _controller,
        children: <Widget>[
          page1(),
          page2(),
          page3(),
        ],
      ),
    ),
  )

更多解释请参考这个答案


1
投票

TabBarView 需要有限的高度,而 SingleChildScrollView 无法提供。问题是您正在 SingleChildScrollView 内的列中使用扩展。


来自类似问题的答案可能对您有帮助

答案就在错误本身。当该列位于可滚动视图内时,该列会尝试收缩包裹其内容,但由于您使用 Expanded 作为该列的子项,因此它的工作方式与尝试收缩包裹其子项的列相反。两者都试图实现完全相反的任务。奥格

这导致了此错误,因为这两个指令彼此完全相反。 如错误日志中所述,请尝试以下操作: 考虑将

mainAxisSize
设置为
MainAxisSize.min
(对于列)并使用
FlexFit.loose
适合灵活(使用灵活而不是扩展)。


0
投票

我遇到了类似的问题。我的解决方案是使用容器而不是 TabBarView 来显示子项(选项卡)。

你可以根据TabBar选择的索引来切换这个容器的内容。

当然,如果您需要滑动手势在选项卡之间移动,您也需要添加它,对我来说也不需要。代码看起来像这样。

import 'package:flutter/material.dart';

class Screen extends StatefulWidget {
  Screen({
    Key key,
  }) : super(key: key);

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

class _ScreenState extends State<Screen> with TickerProviderStateMixin {
  TabController _builderPageController;
  List<Widget> _tabs;
  List<Tab> _tabItems;
  int selectedtabIndex = 0;

  @override
  void initState() {
    super.initState();
    _initTabController();
    _setUpTabComponents();
    _preprareTabItems();
  }

  Column _showWidgets() {
    return Column(
      children: [
        Container(
          // your non tab widgets goes here
          height: 300,
          color: Colors.blueAccent,
        ),
        SizedBox(
          height: 10,
        ),
        Material(
          elevation: 3,
          child: TabBar(
            tabs: _tabItems,
            controller: _builderPageController,
            unselectedLabelColor: Colors.grey,
            labelColor: Colors.blueAccent,
            indicatorColor: Colors.blueAccent,
            labelStyle: TextStyle(
              fontWeight: FontWeight.w500,
              fontSize: 12.0,
            ),
            unselectedLabelStyle: TextStyle(fontWeight: FontWeight.normal, fontSize: 12.0),
          ),
        ),
        Container(
            padding: EdgeInsets.symmetric(horizontal: 16.0),
            child: Builder(
              builder: (context) {
                return _tabs[selectedtabIndex];
              },
            ))
      ],
    );
  }

  void _initTabController() {
    // inits Tabcontroller for Tabview
    _builderPageController = TabController(
      length: 3,
      vsync: this,
      initialIndex: selectedtabIndex,
    );
    _builderPageController.addListener(() {
      // updated index and calls the set state
      // to switch the content of the tab based on the index clicked
      selectedtabIndex = _builderPageController.index;
      setState(() {
        _builderPageController.index;
      });
    });
  }

  void _setUpTabComponents() {
    // sets up Tab headers
    _tabItems = [
      Tab(child: Text('Tab1')),
      Tab(child: Text('Tab2')),
      Tab(child: Text('Tab3')),
    ];
  }

  List<Widget> _preprareTabItems() {
    // views to show on each tab
    // remove height once you add your contents
    return _tabs = <Widget>[
      Container(
        // first tab content
        height: 400,
        color: Colors.white,
      ),
      Container(
        // second tab content
        height: 600,
        color: Colors.white,
      ),
      Container(
        // third  tab content
        height: 900,
        color: Colors.white,
      ),
    ];
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(child: _showWidgets());
  }
}


0
投票

对我来说,使用包 contentsize_tabbarview https://pub.dev/packages/contentsize_tabbarview 解决了这个问题。

我的 TabBarView 位于页面中间,TabBarView 上方有很多内容,因此使用 NestedScrollView 很不方便。此外,我的所有选项卡中都有 Listview。这也不适用于 NestedScrollView。

这个方法的缺点是我需要将shrinkWrap设置为true

SingleChildScrollView(
chilld:
    ContentSizeTabBarView(
        physics: NeverScrollableScrollPhysics(),
        controller: _tabController,
        children: [
            ListView(
                shrinkWrap: true,
             ),
            ListView(
                shrinkWrap: true,
             ),
        ],
    )
)
© www.soinside.com 2019 - 2024. All rights reserved.