I've got a screen having some content on top of a TabBar.
Both the content above TabBar and in TabBarView can be of dynamic height.
My use case is that the upper content should only be scrollable when all of the content is not visible and only up to the point that all of it becomes visible and not beyond that. So in the following example, only Tab 1 should be scrollable.
Setting the scrollphysics to NeverScrollableScrollPhysics wouldn't work since I can't determine the scroll behavior beforehand because of the dynamic height of the contents. Using SliverAppBar also doesn't work for the same reason.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
final length = 5;
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final List<String> _tabs = <String>['Tab 1', 'Tab 2', 'Tab 3'];
return DefaultTabController(
length: _tabs.length,
child: Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverOverlapAbsorber(
handle:
NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverToBoxAdapter(
child: Column(
children: [
Container(
width: double.infinity,
alignment: Alignment.center,
child: Column(
children: [
const Text('Upper Content'),
ListView.builder(
shrinkWrap: true,
itemCount: length,
itemBuilder: (_, __) => Container(
padding: const EdgeInsets.all(5),
alignment: Alignment.center,
child: const Text('Items'),
),
)
],
),
),
Container(
color: Colors.blue,
child: TabBar(
tabs: _tabs
.map(
(String name) => Tab(
text: name,
),
)
.toList(),
),
)
],
),
),
),
];
},
body: TabBarView(
children: _tabs.map((String name) {
return name.split(' ')[1] != '3'
? SafeArea(
top: false,
bottom: false,
child: Builder(
builder: (BuildContext context) {
return CustomScrollView(
key: PageStorageKey<String>(name),
slivers: <Widget>[
SliverOverlapInjector(
handle: NestedScrollView
.sliverOverlapAbsorberHandleFor(context),
),
SliverPadding(
padding: const EdgeInsets.all(8.0),
sliver: SliverFixedExtentList(
itemExtent: 48.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return ListTile(
title: Text('Item $index'),
);
},
childCount:
name.split(' ')[1] != '2' ? 15 : 5,
),
),
),
],
);
},
),
)
: Container(
height: 50,
width: 50,
color: Colors.yellow,
);
}).toList(),
),
),
),
);
}
}
