无法在 flutter 中为 TabBarView 设置动态高度
Cannot set dynamic height for TabBarView in flutter
我正在尝试创建位于页面中间的 TabBar(描述小部件必须位于顶部)。
问题是我必须手动设置包含 TabBarView 的容器小部件的高度。如果我在没有这个高度的情况下保留它,我会收到错误 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()],
),
),
],
));
}
}
由于标签内容是动态的,因此高度也将是动态的。我不能在这里使用静态高度。
静态高度的Container有替代方案吗?我怎样才能让我的标签高度动态?
我已通过将 SingleChildScrollView 更改为 ListView 并编写我自己的 TabView 小部件解决了这个问题,该小部件在 Stack 包装器中包含选项卡。
顶级小部件主体包装器从 Column 和 SingleChildScrollView 更改为 ListView:
Widget build(BuildContext context) {
SizeConfig().init(context);
return Scaffold(
appBar: RestaurantInfoAppBar(),
body: ListView(
children: <Widget>[Description(), Tabs()],
),
);
}
选项卡小部件 - 删除了具有静态宽度包装器的容器:
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,
)
],
);
}
}
P.S。 SizeConfig 等同于 MediaQuery.of(context).size.width.
希望这对像我这样的人有帮助! :)
您可以用 Expanded
小部件包裹 TabBarView
。
确保为 TabBarView
使用正确的父子小部件
知道晚了,希望对大家有所帮助。
我发现这个对我来说工作得很好
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')),
],
),
);
}
}
我正在尝试创建位于页面中间的 TabBar(描述小部件必须位于顶部)。
问题是我必须手动设置包含 TabBarView 的容器小部件的高度。如果我在没有这个高度的情况下保留它,我会收到错误 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()],
),
),
],
));
}
}
由于标签内容是动态的,因此高度也将是动态的。我不能在这里使用静态高度。
静态高度的Container有替代方案吗?我怎样才能让我的标签高度动态?
我已通过将 SingleChildScrollView 更改为 ListView 并编写我自己的 TabView 小部件解决了这个问题,该小部件在 Stack 包装器中包含选项卡。
顶级小部件主体包装器从 Column 和 SingleChildScrollView 更改为 ListView:
Widget build(BuildContext context) {
SizeConfig().init(context);
return Scaffold(
appBar: RestaurantInfoAppBar(),
body: ListView(
children: <Widget>[Description(), Tabs()],
),
);
}
选项卡小部件 - 删除了具有静态宽度包装器的容器:
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,
)
],
);
}
}
P.S。 SizeConfig 等同于 MediaQuery.of(context).size.width.
希望这对像我这样的人有帮助! :)
您可以用 Expanded
小部件包裹 TabBarView
。
确保为 TabBarView
知道晚了,希望对大家有所帮助。
我发现这个对我来说工作得很好
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')),
],
),
);
}
}