I have the code below, which I shortened as much as possible to make it easier to deal with. I want to scroll down to show the default appBar, not the background. I did some solutions, but it didn't work. Switch between them with a smooth motion.
I want to use the same existing code because I built on it.
I have attached an illustration of the problem

The main code:
import 'package:flutter/material.dart';
import 'home_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
HomePage code:
import 'package:flutter/material.dart';
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
const SliverPersistentHeader(pinned: true, delegate: SliverHeaderDelegateComponent(expandedHeight: 300)),
SliverList(
delegate: SliverChildListDelegate(
[
Container(
height: 1000,
color: Colors.blue.withOpacity(0.5),
child: const Center(child: Text('Body')),
)
],
),
),
],
),
);
}
}
The SliverHeaderDelegateComponent code :
class SliverHeaderDelegateComponent extends SliverPersistentHeaderDelegate {
final double expandedHeight;
const SliverHeaderDelegateComponent({required this.expandedHeight});
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
final appBarSize = expandedHeight - shrinkOffset;
final proportion = 2 - (expandedHeight / appBarSize);
final percent = proportion < 0 || proportion > 1 ? 0.0 : proportion;
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) => SizedBox(
height: expandedHeight + expandedHeight / 2,
child: Stack(
clipBehavior: Clip.none,
children: [
Container(
height: 500,
decoration: const BoxDecoration(
color: Colors.black,
image: DecorationImage(
image: NetworkImage(
'https://www.digitalartsonline.co.uk/cmsdata/slideshow/3662115/baby-driver-rory-hi-res.jpg'),
fit: BoxFit.cover,
),
),
),
PositionedDirectional(
start: 0.0,
end: 0.0,
top: appBarSize > 0 ? appBarSize : 0,
bottom: -100,
child: Opacity(
opacity: percent,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 30 * percent),
child: const Card(
elevation: 20.0,
child: Center(
child: Text("Widget"),
),
),
),
),
),
],
),
),
);
}
@override
double get maxExtent => expandedHeight + expandedHeight / 2;
@override
double get minExtent => kToolbarHeight;
@override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
return true;
}
}
Here is the solution using SliverHeaderDelegateComponent as you requested.
In this example, the AppBar is shown when collapsed, but you can uncomment the commented part if you want to show it on expand. (UPDATE: improved fading as requested in the comment section)
class SliverHeaderDelegateComponent extends SliverPersistentHeaderDelegate {
final double expandedHeight;
const SliverHeaderDelegateComponent({required this.expandedHeight});
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
final deadline = (expandedHeight + minExtent);
double percent = shrinkOffset > deadline ? 1 : shrinkOffset / deadline;
final appBarSize = expandedHeight - shrinkOffset;
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) => SizedBox(
height: expandedHeight + expandedHeight / 2,
child: Stack(
clipBehavior: Clip.none,
children: [
// shrinkOffset == 0 // if you want to show it on expand
shrinkOffset > expandedHeight + minExtent // show it on collapse
? AppBar(title: Text('App Bar'))
: Container(
height: 500,
decoration: const BoxDecoration(
color: Colors.black,
image: DecorationImage(
image: NetworkImage(
'https://www.digitalartsonline.co.uk/cmsdata/slideshow/3662115/baby-driver-rory-hi-res.jpg'),
fit: BoxFit.cover,
),
),
),
PositionedDirectional(
start: 0.0,
end: 0.0,
top: appBarSize > 0 ? appBarSize : 0,
bottom: -100,
child: Opacity(
opacity: 1 - percent,
// opacity: percent < 0.5 ? 1 : (1 - percent) * 2, // if you want to start fading when reach half way scroll
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 30 * percent),
child: const Card(
elevation: 20.0,
child: Center(
child: Text("Widget"),
),
),
),
),
),
],
),
),
);
}
@override
double get maxExtent => expandedHeight + expandedHeight / 2;
@override
double get minExtent => kToolbarHeight;
@override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
return true;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With