Reputation: 71
My goal is to use some kind of Profile Page where I have a SliverAppBar at the top which has a tabBar as bottom to switch between the Lists or elements shown below the AppBar. I found that this is archived by NestedScrollView but noticed a strange behavior when either having just a few elements and scrolling up, where it still scrolls up under the tab bar, even tho there are no new elements to show at the bottom, or when you have a lot of items and want to use some kind of persistent App Bar in one of the lists, to for example have a textfield which should be used to sort or filter the list.
I tried to use SliverOverlapAbsorber, but it didn't solve the problem. So below I just show the sample code without it: I thought about inserting a Container that changes it's height according to the scrolling position, to 'solve' this issue, but I'd prefer the proper way to do this..
here's a gif to illustrate the problem: https://gfycat.com/decentoccasionalhyena https://gfycat.com/fastslightekaltadeta
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:widgettests/widgets/sliverHeader.dart';
class NestedTabbarSliverOverpaling extends StatefulWidget {
@override
_NestedTabbarSliverOverpalingState createState() => _NestedTabbarSliverOverpalingState();
}
class _NestedTabbarSliverOverpalingState extends State<NestedTabbarSliverOverpaling> with TickerProviderStateMixin {
ScrollController _scrollController;
TabController _tabController;
@override
void initState() {
super.initState();
_scrollController = new ScrollController();
_tabController = new TabController(length: 2, vsync: this);
}
@override
void dispose() {
_scrollController.dispose();
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
controller: _scrollController,
physics: BouncingScrollPhysics(),
dragStartBehavior: DragStartBehavior.start,
headerSliverBuilder: (context, innerBoxIsScrolled) =>
<Widget>[
SliverAppBar(
pinned: true,
expandedHeight: 100,
bottom: PreferredSize(
preferredSize: Size.fromHeight(-5),
child: TabBar(
controller: _tabController,
labelColor: Colors.white,
tabs: <Widget>[Tab(text: '1st List',), Tab(text: '2nd List',),]
),
),
),
],
body: TabBarView(
controller: _tabController,
children: <Widget>[
SafeArea(
top: false,
bottom: false,
child: _buildfirstList(context),
),
SafeArea(
top: false,
bottom: false,
child: _build2ndList(context),
),
],
),
),
);
}
Widget _buildfirstList(BuildContext context) {
final children = <Widget>[
CustomScrollView(
key: PageStorageKey<String>('1st'),
physics: ClampingScrollPhysics(),
slivers: <Widget>[
SliverAppBar(
backgroundColor: Colors.blueGrey,
floating: true,
snap: true,
title: TextField(
showCursor: false,
),
titleSpacing: 2,
),
_List(),
//SliverFillRemaining(),
],
),
];
return Stack(
children: children,
);
}
Widget _build2ndList(BuildContext context) {
final children = <Widget>[
CustomScrollView(
key: PageStorageKey<String>('2nd'),
physics: ClampingScrollPhysics(),
slivers: <Widget>[
_List(),
//SliverFillRemaining(),
],
),
];
return Stack(
children: children,
);
}
Widget _List() {
return SliverList(
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
return new ListTile(
title: Text('tile no. ${index}'),
);
},
childCount: 55,
),
);
}
}
Upvotes: 2
Views: 2245