Flutter:SingleChildScrollView 内的 TabBarView

Posted

技术标签:

【中文标题】Flutter:SingleChildScrollView 内的 TabBarView【英文标题】:Flutter: TabBarView inside SingleChildScrollView 【发布时间】:2020-10-10 13:51:38 【问题描述】:

我的问题是我想要一个 TabBarView 在 SingleChildScrollView 中,但它给了我这个错误: RenderFlex 孩子有非零弹性,但传入的高度限制是无限的。

如果我删除 SingleChildScrollView 它可以工作,但我需要小部件,因为那时我想推送一个 ListView,它与 TabBar 一起滚动,就像 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()
            ]),
          ),)
        ],
      ),
    ));
  

【问题讨论】:

你能解决这个问题吗? 如何解决这个问题? 【参考方案1】:

我遇到了类似的问题。我的解决方案是使用容器而不是 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());
  


【讨论】:

【参考方案2】:

您可以使用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(),
        ],
      ),
    ),
  )

更多解释请参考this answer

【讨论】:

这woooooorrrrkkksssssss....终于。谢谢REVERIE!【参考方案3】:

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


similar question 的回答可能对您有所帮助

答案就在错误本身。当列在可滚动的视图内时,列会尝试收缩包装其内容,但由于您使用 Expanded 作为列的子级,它的工作方式与尝试收缩其子级的列相反。两者都试图完成一个完全相反的任务。呵呵

这是导致此错误的原因,因为这两个指令彼此完全相反。 如错误日志中所述,请尝试以下操作: 考虑将mainAxisSize 设置为MainAxisSize.min(用于列)并使用FlexFit.loose 适合灵活(使用灵活而不是扩展)。

【讨论】:

我试了一下,同样的错误。我在帖子中推送新代码。我是怎么做到的? 一次性使用容器给 TabBarView 一些高度 当然它适用于高度,但我想要一个扩展的TabBarView 做一件事从脚手架的顶部移除 singlechildscrollview 并将 Flexible 替换为 Expanded。在 Expanded 中保留 singlechildscrollview,然后是 TabBarView 它不起作用,我也想要里面的TabBar。我将更改添加到帖子中

以上是关于Flutter:SingleChildScrollView 内的 TabBarView的主要内容,如果未能解决你的问题,请参考以下文章

[Flutter] flutter项目一直卡在 Running Gradle task 'assembleDebug'...

flutter 日志输出,Flutter打印日志,flutter log,flutter 真机日志

Flutter开发 Flutter 包和插件 ( Flutter 包和插件简介 | 创建 Flutter 插件 | 创建 Dart 包 )

flutter与原生混编(iOS)

Flutter-布局

如何解决flutter gradle build error?C:\flutter\packages\flutter_tools\gradle\flutter.gradle' line: 991