我的问题是我想要在 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()
]),
),)
],
),
));
}
}
您可以使用
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(),
],
),
),
)
更多解释请参考这个答案
TabBarView 需要有限的高度,而 SingleChildScrollView 无法提供。问题是您正在 SingleChildScrollView 内的列中使用扩展。
来自类似问题的答案可能对您有帮助
答案就在错误本身。当该列位于可滚动视图内时,该列会尝试收缩包裹其内容,但由于您使用 Expanded 作为该列的子项,因此它的工作方式与尝试收缩包裹其子项的列相反。两者都试图实现完全相反的任务。奥格
这导致了此错误,因为这两个指令彼此完全相反。 如错误日志中所述,请尝试以下操作: 考虑将
mainAxisSize
设置为 MainAxisSize.min
(对于列)并使用 FlexFit.loose
适合灵活(使用灵活而不是扩展)。
我遇到了类似的问题。我的解决方案是使用容器而不是 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());
}
}
对我来说,使用包 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,
),
],
)
)