I really love using Slivers, but really have a Problem with SliverAppBar:
usually, I use the SliverAppBar Widget for creating a AppBar that has a Background Image with the Parallax effect. So all I use is the flexible space, which unfortunately gets blocked by the AppBar Portion whenever you scroll down.
I tried making the AppBar portion transparent by setting the color value to transparent, but all it does is make the whole thing, including the flexiblespace, transparent..
Is there a way to use only the flexiblespace of a SliverAppBar without fading in the AppBar portion when scrolling down?
here is the code I use:
SliverAppBar(
expandedHeight: 220,
pinned: false,
forceElevated: true,
backgroundColor: Colors.transparent,
stretch: true,
leading: Container(),
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.pin,
background:Stack(
children: <Widget>[
Image(
fit: BoxFit.cover,
width: MediaQuery.of(context).size.width,
image: AssetImage('assets/images/image2.png'),
),
Positioned(
bottom: 0,
child: Container(
height: 110,
width: MediaQuery.of(context).size.width,
color: Colors.grey,
),
),
Positioned(
bottom: 10,
left: 10,
child: CircleAvatar(
radius: 45,
backgroundImage: AssetImage('assets/images/image.png'),
),
),
Positioned(
bottom: 77,
left: 110,
child: Container(
width: MediaQuery.of(context).size.width -110 -60,
child: Text(
'Name...',
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
fontSize: 20,
color: Colors.white
),
),
),
),
Positioned(
bottom: 63,
left: 110,
child: Container(
width: MediaQuery.of(context).size.width -110 -60,
child: Text(
'description...',
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
fontSize: 14,
color: Colors.white70
),
),
),
),
Positioned(
bottom: 5,
left: 110,
child: Container(
width: MediaQuery.of(context).size.width-110,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
],
),
),
),
],
),
),
),
Hope these Images help explain my problem:
The flexiblespace looks as follows:Image 1
but once you scroll down, the flexible space fades away as seen in this picture:Image 2
I don't want to flexiblespace to fade away.
You've propably found the answer allready but I thought if someone like me encounter your question this might be a the answer you are looking for.
FlexibleSpaceBar include fade out animation and only way to exclude it is to write your own LayoutBuilder that will handle the collapse.
Here is a code example how you can create the custom flexibleSpace Widget with LayoutBuilder.
NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
SliverAppBar(
titleSpacing: 0,
pinned: false,
backgroundColor: Colors.transparent,
automaticallyImplyLeading: false,
elevation: 0.0,
actions: [
IconButton(
splashRadius: 20,
icon: Icon(
FontAwesomeIcons.cog,
color: Colors.grey[200],
),
onPressed: () {
},
),
],
toolbarHeight: kToolbarHeight,
expandedHeight: maxExtent,
flexibleSpace: LayoutBuilder(
builder: (context, constraints) {
double currentExtent = constraints.maxHeight;
final double deltaExtent = maxExtent - minExtent;
// 0.0 -> Expanded
// 1.0 -> Collapsed to toolbar
final double t =
(1.0 - (currentExtent - minExtent) / deltaExtent)
.clamp(0.0, 1.0) as double;
return Stack(
fit: StackFit.loose,
overflow: Overflow.clip,
children: [
Positioned(
top: _getCollapsePadding(t),
left: 0.0,
right: 0.0,
height: maxExtent,
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.start,
children: [
//Insert Widgets add to your flexible space
],
),
),
],
);
},
),
),
];
},
body: Container()
),
double get maxExtent => 300;
double get minExtent => kToolbarHeight;
double _getCollapsePadding(double t) {
final double deltaExtent = maxExtent - minExtent;
return -Tween<double>(begin: 0.0, end: deltaExtent / 4).transform(t);
}
Related
I use a SliverAppBar and use flexibleSpace title instead of the default sliver title, on portrait mode, it is perfectly fine as shown :
But when i get to landscape mode it causes a bottom overflow by 13px, VScode tells me the renderflex is caused by a column.
This is how it looks like in landscape :
It is so messy that when i discovered this bug i couldn't continue coding until i fix this and this is what i've been trying to do :(
I will give my SliverAppBar code and also the widget used in the sliverapp flexibleSpace title as snippet below
I have tried using Expanded instead of Flexible, but it causes even more errors.
I also tried using some screen utility packages in pub.dev but seem like i don't use it properly.
Main view with sliverapp :
class HomeView extends GetView<HomeController> {
#override
Widget build(BuildContext context) {
controller.initScrollController();
return WillPopScope(
onWillPop: Helper().onWillPop,
child: Scaffold(
body: RefreshIndicator(
onRefresh: () async {
Get.find<LaravelApiClient>().forceRefresh();
controller.refreshHome(showMessage: true);
Get.find<LaravelApiClient>().unForceRefresh();
},
child: CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
controller: controller.scrollController,
shrinkWrap: false,
slivers: <Widget>[
SliverAppBar(
backgroundColor: Color(0xffFFFFFF),
expandedHeight: MediaQuery.of(context).size.height * 0.18,
elevation: 0.5,
floating: false,
iconTheme: IconThemeData(color: Get.theme.primaryColor),
actions: [NotificationsButtonWidget()],
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.parallax,
title: MainProfileDetails(),//i suspect this is the widget causing the bug
),
),
SliverToBoxAdapter(
child: Wrap(
children: [
JobSummaryView(),
//BookingsListWidget(),
],
),
),
],
)),
),
);
}
}
MainProfileDetails() code:
class MainProfileDetails extends StatelessWidget {
const MainProfileDetails({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Obx(() {
return Padding(
padding: const EdgeInsets.only(left: 5.0),
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Stack(
children: [
GestureDetector(
onTap: () {
Get.toNamed(Routes.PROFILE);
},
child: Container(
child: Stack(
children: [
SizedBox(
width: 60,
height: 60,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(80)),
child: CachedNetworkImage(
height: 100,
width: double.infinity,
fit: BoxFit.cover,
imageUrl: Get.find<AuthService>()
.user
.value
.avatar
.thumb,
placeholder: (context, url) => Image.asset(
'assets/img/loading.gif',
fit: BoxFit.cover,
width: double.infinity,
height: 80,
),
errorWidget: (context, url, error) =>
Icon(Icons.error_outline),
),
),
),
Positioned(
top: 35,
left: 30,
right: 0,
child: Get.find<AuthService>()
.user
.value
.verifiedPhone ??
false
? Icon(Icons.check_circle,
color: Color(0xffB0BEC1), size: 24)
: Icon(Icons.error_outline),
)
],
),
),
),
],
),
SizedBox(
width: 10,
),
Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
flex: 2,
child: Padding(
padding: const EdgeInsets.only(left: 1.0),
child: Text(
'Hello, ${Get.find<AuthService>().user.value.name}',
style: GoogleFonts.poppins(
color: Color(0xff34495E), fontSize: 9),
),
),
),
Flexible(
flex: 2,
child: Padding(
padding: const EdgeInsets.only(top: 1.0, bottom: 1.0),
child: Text(
'Good Stitching',
style: GoogleFonts.poppins(
fontSize: MediaQuery.of(context).size.width * 0.04,
color: Color(0xff000000),
fontWeight: FontWeight.w600),
),
),
),
Flexible(
child: Container(
decoration: BoxDecoration(
color: Color(0xffeeeeee),
borderRadius: BorderRadius.circular(15),
),
child: Padding(
padding: const EdgeInsets.only(
top: 3.0, bottom: 3.0, left: 10.0, right: 10.0),
child: Get.find<AuthService>().user.value.verifiedPhone ??
false
? Text(
'Verified',
style: GoogleFonts.poppins(
fontSize:
MediaQuery.of(context).size.width * 0.025,
fontStyle: FontStyle.italic),
)
: Text(
'Unverified',
style: GoogleFonts.poppins(
fontSize:
MediaQuery.of(context).size.width * 0.025,
fontStyle: FontStyle.italic),
),
),
)),
],
),
//NotificationsButtonWidget(),
],
),
);
});
}
}
Please i need your time and assistance on this one. Thank you!
After many hours of asking for help here, i decided to go with simple AppBar in flutter as SliverAppBar flexibleSpace title is only customisable to a limit.
And that was goodbye to orientation issues.
Thanks everyone for your support.
CODE:
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.blue,
body: SafeArea(
child: SingleChildScrollView(
physics: ClampingScrollPhysics(),
child: Align(
alignment: Alignment.center,
child: Container(
width: Get.width,
height: Get.width * 16 / 9,
child: Stack(
children: [
Positioned(top: 16, left: 16, child: BackButton()),
Positioned(
left: 0,
bottom: 0,
child: IconButton(
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 0),
icon: Icon(
Icons.delete_forever,
color: Colors.greenAccent,
size: 32,
),
onPressed: () {
},
),
),
],
),
),
),
),
),
);
}
There is some margin on IconButton.
I think IconButton should be tight on bottom left.
How to remove margin IconButton and screen side?
Actually it's the default padding of the Icon Button only...
That is, the stack is placing the button at bottom left but icon in the button is padded by default.
You can try using Gesture detector with that icon as a child...
You will get different result...
Please correct me if i am wrong..
I am trying to replicate the text shrink effect that comes naturally with a SliverAppBar with an image such that the background image of the sliverhead shrinks to a leading icon in the appbar of the sliverAppBar.
I have tried using the AnimatedPostioned flutter widget along with a scroll controller that toggles the state of the isSkrink boolean when the page is scrolled.
Below is AnimatedPosition widget:
AnimatedPositioned(
width: isShrink ? 10.0 : 200.0,
height: isShrink ? 10.0 : 200.0,
duration: Duration(seconds: 2),
curve: Curves.linear,
child: Stack(
children: [
Container(
child: Image.asset(
'assets/images/user-avatar.png'),
),
],
),
),
Below is the SliverAppBar with the AnimatedPositioned in the stack and the user icon in the leading param of the SliverAppBar
SliverAppBar(
leading: isShrink
? Padding(
padding: EdgeInsets.all(10),
child: CircleAvatar(
backgroundImage:
AssetImage('assets/images/user-avatar.png'),
backgroundColor: Colors.white,
radius: 3,
),
)
: Container(),
floating: true,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Row(
children: <Widget>[
Text(
"Username",
style: bodyText1Style(
context,
color: isShrink ? Colors.black : Colors.white,
fontSize: isShrink ? 18 : 15,
fontWeight: FontWeight.w600,
),
),
],
),
background: Stack(
children: <Widget>[
AnimatedPositioned(
width: isShrink ? 10.0 : 200.0,
height: isShrink ? 10.0 : 200.0,
duration: Duration(seconds: 2),
curve: Curves.linear,
child: Stack(
children: [
Container(
child: Image.asset(
'assets/images/user-avatar.png'),
),
],
),
),
],
),
),
expandedHeight: size.height * 0.35,
);
This is the result of the code above:
After a long research I was able to come with this using https://stackoverflow.com/a/59323713/3636615 as reference:
Scaffold(
body: CustomScrollView(
slivers: <Widget>[
TransitionAppBar(
// extent: 250,
extent: 300,
avatar: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/images/user-avatar.png'), // NetworkImage(user.imageUrl),
fit: BoxFit.cover)),
),
title: "Emmanuel Olu-Flourish",
),
SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return Container(
child: ListTile(
title: Text("${index}a"),
));
}, childCount: 25))
],
),
);
Check out the TransitionAppBar implementation here:
https://gist.github.com/Oluflourish/2f0789bd2e8ee576a2d6364d709c1c14
Below is demo of the result:
I am trying to scale down the image a bit so that it won't be cropped in grid item element. Actual image size is 300x300.
below is my code where I am explicitly trying to reduce the image width and height, I have also attempted many combinations with the fit type. I do not see any visible impact though. Can you let me know how should I achieve this? Thank you for your patience.
#override
Widget build(BuildContext context) {
return Scaffold(
body: GridView.count(
padding: const EdgeInsets.fromLTRB(0, 10, 0, 0),
childAspectRatio: 1.5,
crossAxisCount: 2,
children: List.generate(
8,
(index) {
return Center(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(5),
child: CircleAvatar(
backgroundImage: Image.asset(
'assets/icons/antenna_icon.png',
height: 30,
width: 30,
fit: BoxFit.fitWidth,
).image,
backgroundColor: Colors.grey.shade300,
radius: 50,
)),
Text(
"Evve",
style: GoogleFonts.roboto(color: Colors.black87, fontSize: 15),
),
],
));
},
),
));
}
You can use Transform.scale as the child of the CircleAvatar and set the image as the child of the Transform.scale:
CircleAvatar(
child: Transform.scale(
scale: 0.8,
child: Image.asset(
'assets/icons/antenna_icon.png',
height: 30,
width: 30,
fit: BoxFit.fitWidth,
),
),
backgroundColor: Colors.grey.shade300,
radius: 50,
)
I need to put icon into CircleAvatar Widget to allow user upload his image.
Something like this:
This is my code:
child: CircleAvatar(
child: Stack(
children: <Widget>[
Positioned(
bottom: 0,
right: 0,
left: 0,
height: 33,
child: Container(
height: 20,
width: 30,
color: Color.fromRGBO(0, 0, 0, .74),
child: Center(
child:
Icon(Icons.photo_camera, color: Colors.grey),
),
),
)
],
),
radius: 68.0,
backgroundImage:
NetworkImage('https://via.placeholder.com/300'),
backgroundColor: Colors.transparent,
)
But I have this result:
Internal box with camera icon overflow from parent widget (CircleAvatar);
What you want can be simply done with - ClipOval
Your Code:
body: Center(
child: CircleAvatar(
child: ClipOval(
child: Stack(
children: <Widget>[
Image.network('https://via.placeholder.com/300'),
Positioned(
bottom: 0,
right: 0,
left: 0,
height: 33,
child: GestureDetector(
onTap: (){
print('upload Clicked');
},
child: Container(
height: 20,
width: 30,
color: Color.fromRGBO(0, 0, 0, .74),
child: Center(
child: Icon(Icons.photo_camera, color: Colors.grey),
),
),
),
),
],
),
),
radius: 68.0,
// backgroundImage: NetworkImage('https://via.placeholder.com/300'),
backgroundColor: Colors.transparent,
),
),
Output:
You can create your Custom Clipper to give the circular bottom to your widget.
You need to use the ClipPath widget for it, pass your widget in your case Container containing your camera icon.
and in clipper pass your CustomClipper.
ClipPath
return ClipPath(
child: your_widget,
clipper: BottomWaveClipper(),
);
CustomCliper
class BottomCircleClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
//your path to draw circle
}
#override
bool shouldReclip(TriangleClipper oldClipper) => false;
}
Here are some links that will help you.
https://iirokrankka.com/2017/09/04/clipping-widgets-with-bezier-curves-in-flutter/
https://medium.com/flutter-community/clipping-in-flutter-e9eaa6b1721a