I'm having a hard time making a custom collapsing toolbar, attached below is a video for a normal case.
Then here's a screen record of the misbehavior, most of the time this happens.
Aside from the fact that the scrolling is not so snappy, you'll see in the second video that the sliver on the top is not completely collapsed.
Do you have any suggestion to improve the performance of the app and a solution for the bug?
here's my code inside the SliverPersistentHeaderDelegate
class DashboardHeaderPersistentDelegate extends SliverPersistentHeaderDelegate {
...
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
double shrinkPercentage = min(1, shrinkOffset / (maxExtent - minExtent));
double titleTopMargin = titleCollapsedTopPadding +
(titleExpandedTopPadding - titleCollapsedTopPadding) *
(1 - shrinkPercentage);
double titleFontSize = titleCollapsedFontSize +
(titleExpandedFontSize - titleCollapsedFontSize) *
(1 - shrinkPercentage);
double infoWidgetHeight = minExtent +
(maxExtent - minExtent) -
shrinkOffset -
titleTopMargin -
titleFontSize -
44;
double collapasedInfoOpacity = max(0, shrinkPercentage-.7)/.3;
return Material(
elevation: 0,
shadowColor: Colors.white,
child: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
height: titleFontSize,
alignment: Alignment.center,
child: Text(
'\$ 5329.05',
style: TextStyle(
fontFamily: 'Barlow',
fontSize: titleFontSize,
fontWeight: FontWeight.w500),
),
margin: EdgeInsets.only(top: titleTopMargin, bottom: 8),
),
Container(
height: shrinkPercentage == 1 ? 20 : infoWidgetHeight,
width: MediaQuery.of(context).size.width,
alignment: Alignment.center,
child: Stack(
alignment: Alignment.bottomCenter,
children: [
Opacity(
opacity: 1 - shrinkPercentage,
child: _buildInformationWidget(context),
),
Opacity(
opacity: collapasedInfoOpacity,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: _buildCollapsedInformationWidget(),
),
)
],
),
)
],
),
),
);
}
Widget _buildInformationWidget(BuildContext context) => ClipRect(
child: OverflowBox(
maxWidth: double.infinity,
maxHeight: double.infinity,
child: FittedBox(
fit: BoxFit.fitWidth,
alignment: Alignment.center,
child: Container(
width: MediaQuery.of(context).size.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'AVAILABLE BALANCE',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w900,
color: Colors.black26),
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
width: 100,
child: Text(
'\$ 11200',
textAlign: TextAlign.right,
style: TextStyle(
fontFamily: 'Barlow',
fontSize: 18,
fontWeight: FontWeight.w700,
color: Colors.green[400]),
),
),
Text(
' I ',
style: TextStyle(fontSize: 20, color: Colors.black12),
),
Container(
width: 100,
child: Text(
'\$ 400',
style: TextStyle(
fontFamily: 'Barlow',
fontSize: 18,
fontWeight: FontWeight.w700,
color: Colors.red[400]),
),
)
],
),
),
Container(
margin: EdgeInsets.only(left: 12, top: 12),
alignment: Alignment.centerLeft,
child: Text(
"CATEGORIES",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w900,
color: Colors.black26),
),
),
Container(
height: 88,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: categories.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(
left: (index == 0) ? 24.0 : 8.0,
right: (index == categories.length - 1)
? 24.0
: 8.0),
child: _buildCategoryItem(
categoriesIcons[index], categories[index], .9),
);
}),
)
],
),
),
),
),
);
Widget _buildCollapsedInformationWidget() => Row(
children: [
Text("Recent"),
Spacer(),
Container(
child: Text(
'\$ 11200',
textAlign: TextAlign.right,
style: TextStyle(
fontFamily: 'Barlow',
fontSize: 14,
fontWeight: FontWeight.w700,
color: Colors.green[400]),
),
),
Text(
' I ',
style: TextStyle(fontSize: 20, color: Colors.black12),
),
Container(
child: Text(
'\$ 400',
style: TextStyle(
fontFamily: 'Barlow',
fontSize: 14,
fontWeight: FontWeight.w700,
color: Colors.red[400]),
),
)
],
);
Widget _buildCategoryItem(
IconData data, String categoryTitle, double percentage) =>
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Stack(
alignment: Alignment.center,
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border.all(width: 1, color: Colors.black12),
borderRadius: BorderRadius.circular(28),
color: Colors.blue[400]),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Icon(
data,
size: 28,
color: Colors.white,
),
),
),
Container(
width: 40,
height: 40,
child: CircularProgressIndicator(
value: percentage,
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation(Colors.white),
),
)
],
),
Container(
width: 72,
alignment: Alignment.center,
child: Text(categoryTitle,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
maxLines: 1,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Colors.black45)),
)
],
);
...
}