I had similar task at my project. The root of the problem is that you are trying to put TabBarView
(scrollable widget that trying to be as big as possible), in a column widget, that have no height.
One of solutions is get rid of scrollable widgets that wraps your TabBarView
in this example ListView
+ Column
. And use NestedScrollView
instead of them. You need to put _buildCarousel()
and TabBar
in the headerSliverBuilder
part, and TabBarView
inside the body of NestedScrollView
.
I don't know how is your design looks, but NestedScrollView
by default opens up to 100% height of screen view. So if you want to make an effect that everything is on one screen, it easer to just block scrolling, by change ScrollController
behavior when it’s needed.
In this example I’m blocking scrolling on third tab, but you can check visibility of the last item of the grid view. Just add the key to the last item, and check its position on the screen.
Hope it’s help.
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
final bodyGlobalKey = GlobalKey();
final List<Widget> myTabs = [
Tab(text: 'auto short'),
Tab(text: 'auto long'),
Tab(text: 'fixed'),
];
TabController _tabController;
ScrollController _scrollController;
bool fixedScroll;
Widget _buildCarousel() {
return Stack(
children: <Widget>[
Placeholder(fallbackHeight: 100),
Positioned.fill(child: Align(alignment: Alignment.center, child: Text('Slider'))),
],
);
}
@override
void initState() {
_scrollController = ScrollController();
_scrollController.addListener(_scrollListener);
_tabController = TabController(length: 3, vsync: this);
_tabController.addListener(_smoothScrollToTop);
super.initState();
}
@override
void dispose() {
_tabController.dispose();
_scrollController.dispose();
super.dispose();
}
_scrollListener() {
if (fixedScroll) {
_scrollController.jumpTo(0);
}
}
_smoothScrollToTop() {
_scrollController.animateTo(
0,
duration: Duration(microseconds: 300),
curve: Curves.ease,
);
setState(() {
fixedScroll = _tabController.index == 2;
});
}
_buildTabContext(int lineCount) => Container(
child: ListView.builder(
physics: const ClampingScrollPhysics(),
itemCount: lineCount,
itemBuilder: (BuildContext context, int index) {
return Text('some content');
},
),
);
@override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (context, value) {
return [
SliverToBoxAdapter(child: _buildCarousel()),
SliverToBoxAdapter(
child: TabBar(
controller: _tabController,
labelColor: Colors.redAccent,
isScrollable: true,
tabs: myTabs,
),
),
];
},
body: Container(
child: TabBarView(
controller: _tabController,
children: [_buildTabContext(2), _buildTabContext(200), _buildTabContext(2)],
),
),
),
);
}
}
.
Another way to get what you want could be creating custom TabView
widget.