【问题标题】:Cannot set dynamic height for TabBarView in flutter无法在颤动中为 TabBarView 设置动态高度
【发布时间】:2020-01-10 06:30:05
【问题描述】:

我正在尝试创建位于页面中间的 TabBar(描述小部件必须位于顶部)。

问题是我必须手动设置包含 TabBarView 的 Container 小部件的高度。如果我离开它没有这个高度,我会收到错误Horizontal viewport was given unbounded height.

顶级小部件:

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: CustomAppBar(),
      body: SingleChildScrollView(
        child: Column(
          children: <Widget>[Description(), Tabs()],
        ),
      ),
    );
  }

标签小部件:

class Tabs extends StatelessWidget {
  final _tabs = [
    Tab(
      icon: Icon(Icons.menu),
      text: 'Menu',
    ),
    Tab(
      icon: Icon(Icons.mode_comment),
      text: 'Reviews',
    )
  ];

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
        length: _tabs.length,
        child: Column(
          children: <Widget>[
            TabBar(
              labelColor: PickColors.black,
              indicatorSize: TabBarIndicatorSize.tab,
              tabs: _tabs,
            ),
            Container(
              width: double.infinity,
              height: 200, // I need to remove this and make height dynamic
              child: TabBarView(
                children: <Widget>[MenuTab(), ReviewsTab()],
              ),
            ),
          ],
        ));
  }
}

由于标签内容是动态的,因此高度也是动态的。我不能在这里使用静态高度。

对于具有静态高度的容器是否有替代方案?如何使标签的高度动态化?

【问题讨论】:

  • @Eugene 没有帮助。还有其他想法吗?

标签: flutter dart tabbar


【解决方案1】:

我发现这对我来说很好用

import 'package:cdc/ui/shared/app_color.dart';
import 'package:flutter/material.dart';

class OrderDetails extends StatefulWidget {
  @override
  _OrderDetailsState createState() => _OrderDetailsState();
}

class _OrderDetailsState extends State<OrderDetails>
    with SingleTickerProviderStateMixin {
  final List<Widget> myTabs = [
    Tab(text: 'one'),
    Tab(text: 'two'),
  ];

  TabController _tabController;
  int _tabIndex = 0;

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

  @override
  void initState() {
    _tabController = TabController(length: 2, vsync: this);
    _tabController.addListener(_handleTabSelection);
    super.initState();
  }

  _handleTabSelection() {
    if (_tabController.indexIsChanging) {
      setState(() {
        _tabIndex = _tabController.index;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Order Detials'),
        backgroundColor: kPrimaryColor,
      ),
      body: ListView(
        padding: EdgeInsets.all(15),
        children: <Widget>[
          Container(
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(14.0),
              color: const Color(0xffffffff),
              boxShadow: [
                BoxShadow(
                  color: const Color(0x2e000000),
                  offset: Offset(0, 0),
                  blurRadius: 10,
                ),
              ],
            ),
            child: Column(
              children: <Widget>[
                TabBar(
                  controller: _tabController,
                  labelColor: Colors.redAccent,
                  tabs: myTabs,
                ),
                Container(
                  child: [
                    Text('First tab'),
                    Column(
                      children:
                          List.generate(20, (index) => Text('line: $index'))
                              .toList(),
                    ),
                  ][_tabIndex],
                ),
              ],
            ),
          ),
          Container(child: Text('another component')),
        ],
      ),
    );
  }
}

【讨论】:

    【解决方案2】:

    您可以用Expanded 小部件包装TabBarView

    确保为TabBarView 使用正确的父子小部件

    我知道已经晚了,希望它对某人有所帮助。

    【讨论】:

      【解决方案3】:

      我已通过将 SingleChildScrollView 更改为 ListView 并编写我自己的 TabView 小部件来解决此问题,该小部件包含堆栈包装器中的选项卡。

      顶级小部件主体包装从 Column 和 SingleChildScrollView 更改为 ListView:

        Widget build(BuildContext context) {
          SizeConfig().init(context);
          return Scaffold(
            appBar: RestaurantInfoAppBar(),
            body: ListView(
              children: <Widget>[Description(), Tabs()],
            ),
          );
        }
      

      Tabs 小部件 - 使用静态宽度包装器移除了 Container:

        Widget build(BuildContext context) {
          return DefaultTabController(
              length: _tabs.length,
              child: Column(
                children: <Widget>[
                  TabBar(
                    labelColor: PickColors.black,
                    indicatorSize: TabBarIndicatorSize.tab,
                    tabs: _tabs,
                  ),
                  TabsView(
                    tabIndex: _tabIndex,
                    firstTab: MenuTab(),
                    secondTab: ReviewsTab(),
                  )
                ],
              ));
        }
      

      新的自定义 TabsView 组件目前只处理两个选项卡(因为我只需要两个),但可以轻松更改为处理动态数量的选项卡:

      class TabsView extends StatelessWidget {
        TabsView(
            {Key key,
            @required this.tabIndex,
            @required this.firstTab,
            @required this.secondTab})
            : super(key: key);
      
        final int tabIndex;
        final Widget firstTab;
        final Widget secondTab;
      
        @override
        Widget build(BuildContext context) {
          return Stack(
            children: <Widget>[
              AnimatedContainer(
                child: firstTab,
                width: SizeConfig.screenWidth,
                padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
                transform: Matrix4.translationValues(
                    tabIndex == 0 ? 0 : -SizeConfig.screenWidth, 0, 0),
                duration: Duration(milliseconds: 300),
                curve: Curves.easeIn,
              ),
              AnimatedContainer(
                child: secondTab,
                width: SizeConfig.screenWidth,
                padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
                transform: Matrix4.translationValues(
                    tabIndex == 1 ? 0 : SizeConfig.screenWidth, 0, 0),
                duration: Duration(milliseconds: 300),
                curve: Curves.easeIn,
              )
            ],
          );
        }
      }
      

      附: SizeConfig 与 MediaQuery.of(context).size.width 相同。

      希望这可以帮助像我这样的人! :)

      【讨论】:

      • 还有其他方法吗?这是一个不支持 swype 的自定义小部件 :)
      • 上例中tabs改变时如何改变_tabIndex?
      • TabsView 正在占用内容较大的选项卡的高度,因此内容较少的另一个选项卡有一个空白空间。有什么解决办法吗?
      • 您可以使用 TabController _tabController 更改 _tabIndex。我一开始就用saple解决了这个问题。 stackoverflow.com/a/62813551/5319007
      • 好主意AnimatedContainer。像魅力一样工作
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-10-18
      • 1970-01-01
      • 2019-12-12
      • 2019-07-05
      • 2020-11-23
      • 2021-04-23
      • 1970-01-01
      相关资源
      最近更新 更多