I am developing an iOS application in flutter. I want a navigation bar which can be expandable. On expand there should be large title on left side and on collapse same title should be on top center. This thing is possible with CupertinoSliverNavigationBar
but I want to add a search field under the large title which should only appear when navigation bar should be expanded and on scroll up, first search bar should be scrolled up and then CupertinoSliverNavigationBar
. This is default behaviour in many iOS applications. Let me show an example
Default IOS Scrolling
You can notice in example when scroll up then first height of search bar gets decreases then navigation bar collapses and when scroll down then first navigation bar expands then height of search bar increases. This is what I achieved yet Achieved Result
This is my code
CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
controller: _scrollController,
slivers: <Widget> [
const CupertinoSliverNavigationBar(
largeTitle: Text('Products'),
stretch: true,
//backgroundColor: Colors.white,
border: Border(),
trailing: Icon(CupertinoIcons.add,color: CupertinoColors.systemBlue,size: 24,),
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: CupertinoSearchTextField(
controller: _controller,
onSubmitted: (String value) {
},
),
),
),
SliverFillRemaining(
child: _controller.text.isNotEmpty?
paymentList(state.productDataSearch!,state):
paymentList(state.productData!,state),
),
],
),
I achieved this by using NotificationListener and change the height of textfield according to the position of scroll
return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) {
if (scrollInfo is ScrollUpdateNotification) {
if (scrollInfo.metrics.pixels > previousScrollPosition) {
//print("going up ${scrollInfo.metrics.pixels}");
///up
if(isVisibleSearchBar > 0 && scrollInfo.metrics.pixels > 0){
setState(() {
isVisibleSearchBar = (40 - scrollInfo.metrics.pixels) >= 0?(40 - scrollInfo.metrics.pixels):0;
});
}
}
else if (scrollInfo.metrics.pixels <= previousScrollPosition) {
//print("going down ${scrollInfo.metrics.pixels}");
///down
if(isVisibleSearchBar < 40 && scrollInfo.metrics.pixels >= 0 && scrollInfo.metrics.pixels <= 40){
setState(() {
isVisibleSearchBar = (40 - scrollInfo.metrics.pixels) <= 40?(40 - scrollInfo.metrics.pixels):40;
});
}
}
setState(() {
previousScrollPosition = scrollInfo.metrics.pixels;
});
}
else if (scrollInfo is ScrollEndNotification) {
print("on edn isVisibleSearchBar $isVisibleSearchBar");
Future.delayed(Duration.zero, () {
if(isVisibleSearchBar < 20 && isVisibleSearchBar > 0){
setState(() {
isVisibleSearchBar = 0;
_scrollController.animateTo(60, duration: const Duration(milliseconds: 200), curve: Curves.ease);
});
}
else if(isVisibleSearchBar >= 20 && isVisibleSearchBar <= 40){
setState(() {
isVisibleSearchBar = 40;
_scrollController.animateTo(0, duration: const Duration(milliseconds: 200), curve: Curves.ease);
});
}
});
}
return true;
},
child: CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
controller: _scrollController,
anchor:0.06,
slivers: <Widget> [
CupertinoSliverNavigationBar(
largeTitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Products'),
AnimatedContainer(
duration: const Duration(milliseconds: 200),
height: isVisibleSearchBar,
child: Padding(
padding: const EdgeInsets.only(right: 15,top: 3),
child: CupertinoSearchTextField(
onChanged: (val){
print("client $val");
if(val.isNotEmpty){
EasyDebounce.debounce('search_name_debounce', const Duration(milliseconds: 300), () {
productBloc.add(SearchPayment(val));
setState(() {});
});
}
else{
EasyDebounce.debounce('search_name_debounce', const Duration(milliseconds: 300), () {
productBloc.add(const SetInitialSearch());
setState(() {});
});
}
},
itemSize:isVisibleSearchBar/2,
prefixIcon: AnimatedOpacity(
duration: const Duration(milliseconds: 200),
opacity: isVisibleSearchBar/40 > 1?1:
isVisibleSearchBar/40 < 0?0:isVisibleSearchBar/40,
child: const Icon(CupertinoIcons.search),
),
controller: _controller,
onSubmitted: (String value) {
},
),
),
),
],
),
stretch: true,
middle: const Text('Products'),
alwaysShowMiddle: false,
backgroundColor: Colors.white,
trailing: const Icon(CupertinoIcons.add,color: CupertinoColors.activeBlue,size: 24,),
),
SliverToBoxAdapter(
child: SafeArea(
top: false,
child: Scrollbar(
child: _controller.text.isNotEmpty?
paymentList(state.productDataSearch!,state):
paymentList(state.productData!,state),
),
),
),
],
),
);