not overlap SliverToBoxAdapter with a transparent SliverAppBar - flutter

Is it possible for a SliverToBoxAdapter to not be overlaped with a transparentSliverAppBar when scrolled upon?
Or give up on using SliverAppBar and just use AppBar instead?
Widget build(BuildContext context) {
return SafeArea(
child: Stack(
children: [
Image(
image: bkgImage,
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
alignment: Alignment.centerLeft,
),
Scaffold(
backgroundColor: Colors.transparent,
body: CustomScrollView(
slivers: [
SliverAppBar(
actions: <Widget>[
IconButton(
icon: const Icon(Icons.search),
onPressed: () {},
),
],
pinned: _pinned,
snap: _snap,
floating: _floating,
expandedHeight: 160,
backgroundColor: Colors.transparent,
elevation: 0.0,
flexibleSpace: FlexibleSpaceBar(
expandedTitleScale: 1.6,
titlePadding: EdgeInsets.all(18),
title: Text('Panahon'),
),
),
SliverList(
delegate: SliverChildListDelegate([
SizedBox(
height: 200,
child: Card(),
),
SizedBox(
height: 200,
child: Card(),
),
SizedBox(
height: 200,
child: Card(),
),
SizedBox(
height: 200,
child: Card(),
),
SizedBox(
height: 200,
child: Card(),
),
]),
)
],
),
),
],
),
);
}
The goal is this:

Precious view:
To get actual effect, we can use SliverPersistentHeaderDelegate. Concept is we need to pass image itself [both image must be screen size].
class MySliverHeaderDelegate extends SliverPersistentHeaderDelegate {
final double _maxExtent = 160;
final VoidCallback onActionTap;
final Image imageWidget;
MySliverHeaderDelegate({
required this.onActionTap,
required this.imageWidget,
});
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
debugPrint(shrinkOffset.toString());
return Stack(
children: [
Positioned(
top: 0,
left: 0,
right: 0,
child: imageWidget,
),
Align(
alignment: Alignment.bottomLeft,
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(
'Panahon',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: Colors.white,
),
),
),
),
// here provide actions
Positioned(
top: 0,
right: 0,
child: IconButton(
icon: const Icon(
Icons.search,
color: Colors.white,
),
onPressed: onActionTap,
),
),
],
);
}
#override
double get maxExtent => _maxExtent;
#override
double get minExtent => kToolbarHeight;
#override
bool shouldRebuild(covariant MySliverHeaderDelegate oldDelegate) {
return oldDelegate != this;
}
}
And use
Widget build(BuildContext context) {
final imageWidget = Image.asset(
"assets/images/image01.png",
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
alignment: Alignment.centerLeft,
);
return SafeArea(
child: Stack(
children: [
imageWidget,
Scaffold(
backgroundColor: Colors.transparent,
body: CustomScrollView(
slivers: [
SliverPersistentHeader(
pinned: true,
delegate: MySliverHeaderDelegate(
imageWidget: imageWidget,
onActionTap: () {
debugPrint("on Tap");
}),
),
//.....
],
),
),
],
),
);
}
Others way to handle this situation
Being SliverAppBar's backgroundColor:Colors.transparent you are able to see though appbar.
If you provide your preferable color, that will fix the issue in this case, but you will not get background effect on appBar.
SliverAppBar(
backgroundColor: Colors.green, // the one you like
For your case, you can use CupertinoSliverNavigationBar instead of using SliverAppBar. It will still block the background on scroll and it won't get that much height.
CupertinoSliverNavigationBar(
backgroundColor: Colors.transparent,
largeTitle: const Align(
alignment: Alignment.bottomLeft,
child: SizedBox(
child: Text('Panahon'),
),
),
trailing: IconButton(
icon: const Icon(Icons.search),
onPressed: () {},
),
),

Related

How can I align the children of a IndexedStack widget to the bottom of its inclosing widget?

The code below has 3 buttons and a container, Inside the container are two positioned widgets,
The bottom positioned widget contains a IndexedStack with 3 containers, When I click on the buttons I want the containers to show as per the code, all fine but the containers are aligned to the top of its parent widget, I want them to align to the bottom center.
I did use the Align widget to align to bottom center but couldn't get it to work,
I want the last three containers with red, blue and green align to the bottom of the yellow container, Right now it will align towards the mid/ mid top, Only the green aligns to the bottom center.
How can I achieve this ?
DART PAD
import 'package:flutter/material.dart';
class Istack extends StatefulWidget {
#override
_IstackState createState() => _IstackState();
}
class _IstackState extends State<Istack> {
int _selectedIndex = 0;
void _showContainer(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: 600,
// color: Colors.purpleAccent,
child: Column(
children: [
const SizedBox(
height: 50,
),
ElevatedButton(
onPressed: () => _showContainer(0),
child: const Text('Show Container 1'),
),
ElevatedButton(
onPressed: () => _showContainer(1),
child: const Text('Show Container 2'),
),
ElevatedButton(
onPressed: () => _showContainer(2),
child: const Text('Show Container 3'),
),
Container(
color: Colors.yellow,
height: 400,
child: Stack(
children: [
Positioned(
top: 0,
child: Container(
color: Color.fromARGB(255, 222, 136, 136),
height: 200,
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: IndexedStack(
index: _selectedIndex,
children: <Widget>[
Container(
height: 50,
color: Colors.red,
),
Container(
height: 100,
color: Colors.blue,
),
Container(
height: 300,
color: Colors.green,
),
],
),
),
],
),
),
],
),
),
);
}
}
You need to remove the fixed height on top and add Spacer() widget just before yellow widget, Spacer widget will push other.
And use alignment: Alignment.bottomCenter on IndexedStack
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
// color: Colors.purpleAccent,
child: Column(
children: [
const SizedBox(
height: 50,
),
ElevatedButton(
onPressed: () => _showContainer(0),
child: const Text('Show Container 1'),
),
ElevatedButton(
onPressed: () => _showContainer(1),
child: const Text('Show Container 2'),
),
ElevatedButton(
onPressed: () => _showContainer(2),
child: const Text('Show Container 3'),
),
Spacer(),
Container(
color: Colors.yellow,
height: 400,
alignment: Alignment.bottomCenter,
child: Stack(
children: [
Positioned(
top: 0,
child: Container(
color: Color.fromARGB(255, 222, 136, 136),
height: 200,
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: IndexedStack(
alignment: Alignment.bottomCenter
index: _selectedIndex,
children: <Widget>[
Container(
height: 50,
color: Colors.red,
),
Container(
height: 100,
color: Colors.blue,
),
Container(
height: 300,
color: Colors.green,
),
],
),
),
],
),
),
],
),
),
);
}

How to get an image above background while shadowing background in flutter

I have an image and I want it to appear on my screen, above my background, with keeping background a bit dark type like in the image. I am thinking on using AlertDialog to display the image, but if you think there is a better way of doing it or a specific widget for this, please do tell me. Also please tell me what do we name this kind of image which hovers over background and focusing itself in UI.
enter code here
Just for trying out, I used this in my screen's initstate, as I want it to appear as soon as my screen appears -
super.initState();
showDialog(
context: context,
builder: (BuildContext buildContext) {
return AlertDialog(
content: Center(
child: Container(
color: Colors.red,
width: 200,
height: 200,
),
),
);
}
);
initState doesn't contain context you can get context after first frame.
You can use addPostFrameCallback
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
showDialog(..);}
Or Future.delayed
Future.delayed(Duration.zero).then((value) {
showDialog(..); }
The dialog construction is :
showDialog(
context: context,
builder: (BuildContext buildContext) {
const double iconSize = 48;
return LayoutBuilder(
builder: (context, constraints) => AlertDialog(
backgroundColor: Colors.transparent,
elevation: 0,
contentPadding: EdgeInsets.zero,
content: SizedBox(
width: constraints.maxWidth * .75,
height: constraints.maxHeight * .75,
child: Stack(
children: [
///background image with fit Cover
Align(
alignment: Alignment.center,
child: Container(
width: constraints.maxWidth * .75 - iconSize,
height: constraints.maxHeight * .75 - iconSize,
color: Colors.cyanAccent,
),
),
Align(
alignment: Alignment.topRight,
child: GestureDetector(
onTap: () {
Navigator.of(context).pop();
},
child: Container(
decoration: const BoxDecoration(
color: Colors.white, shape: BoxShape.circle),
child: const Icon(
Icons.close,
size: iconSize,
),
),
),
)
],
),
),
),
);
},
);
You can use Stack widget withe three widget as children.
first children widget is your background screen.
second children widget is a Container widget with color .withOpacity().
third children widget is the image you are trying to show on top.
Like this:
return Scaffold(
body: Stack(
children: [
Container(
color: Colors.white,
),
Container(
color: Colors.black.withOpacity(0.5),
),
Center(
child: Container(
height: MediaQuery.of(context).size.height * 70 / 100,
width: MediaQuery.of(context).size.width * 80 / 100,
color: Colors.purple,
),
),
],
),
);
for Visibility issue you can use this.
bool isVisible = false;
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: Stack(
children: [
Container(
color: Colors.red,
child: Center(
child: Container(
child: TextButton(
onPressed: () {
setState(() {
isVisible = true;
});
},
child: Text('Show Widget'),
),
),
),
),
Visibility(
visible: isVisible,
child: GestureDetector(
onTap: () {
setState(() {
isVisible = false;
});
},
child: Container(
color: Colors.white.withOpacity(0.5),
),
),
),
Visibility(
visible: isVisible,
child: Center(
child: Container(
height: MediaQuery.of(context).size.height * 70 / 100,
width: MediaQuery.of(context).size.width * 80 / 100,
color: Colors.purple,
child: Stack(
children: [
Positioned(
top: 0.0,
right: 0.0,
child: IconButton(
icon: const Icon(
Icons.close,
color: Colors.white,
),
onPressed: () {
setState(() {
isVisible = false;
});
},
),
),
],
),
),
),
),
],
),
);
}

How to set custom location for floating action button in flutter?

Im trying to make a clone of sportify and I want to set a floating action button
Any help?
Can you put floating action button in custom location via Align Widget
Example:
Align(
alignment: Alignment.center,
child: FloatingActionButton(),
),
And you can get more specific locations through Chang center word
Try this ,
class PlayerScreen extends StatelessWidget {
const PlayerScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
slivers: <Widget>[
SliverAppBar(
stretch: true,
onStretchTrigger: () {
// Function callback for stretch
return Future<void>.value();
},
expandedHeight: 300.0,
flexibleSpace: FlexibleSpaceBar(
stretchModes: const <StretchMode>[
StretchMode.zoomBackground,
StretchMode.blurBackground,
StretchMode.fadeTitle,
],
centerTitle: true,
title: FloatingActionButton(
child: Icon(
Icons.play_arrow,
size: 30,
),
onPressed: () {},
backgroundColor: Colors.greenAccent[400],
),
background: Stack(
fit: StackFit.expand,
children: <Widget>[
Image.network(
'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg',
fit: BoxFit.cover,
),
const DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment(0.0, 0.5),
end: Alignment.center,
colors: <Color>[
Color(0x60000000),
Color(0x00000000),
],
),
),
),
],
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate((ctx, index) {
return ListTile(
leading: Icon(Icons.wb_sunny),
title: Text('Song $index'),
subtitle: Text("description"),
);
}, childCount: 20),
),
],
),
);
}
}

Shrink SliverAppBar Image animation Like Title Text in Flutter

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:

How to vertically center text/title in Flutter using SliverAppBar?

I am trying to set up a SliverAppBar in a CustomScrollView using Flutter, and can't get to vertically center the title.
I already tried this solution (and this SO question is exactly what I want to do) but fortunately, it didn't work for me.
Here is my build method:
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
expandedHeight: 200,
//backgroundColor: Colors.transparent,
flexibleSpace: FlexibleSpaceBar(
titlePadding: EdgeInsets.zero,
centerTitle: true,
title: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text("Should be centered", textAlign: TextAlign.center),
],
),
background: Image.asset("assets/earth.jpg", fit: BoxFit.cover),
),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.menu),
tooltip: "Menu",
onPressed: () {
// onPressed handler
},
),
],
),
SliverFixedExtentList(
itemExtent: 50,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.green,
child: Text("Index n°$index"),
);
},
),
)
],
),
);
}
I really don't understand what is wrong and why it isn't centered. I observed that the column is way too big when setting mainAxisSize to mainAxisSize.max.
Any idea?
Thanks in advance!
I tinkered around a bit in your code and was able to center it. So the main problem here was the expandedHeight. This height expands the SliverAppBar both upwards and downwards meaning that half of that 200 was always above the screen. Taking that into consideration, you would be trying to center the text in only the bottom half of the app bar. The simplest way was to just use Flexible to size the items relative to their container. Here's the working code:
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
expandedHeight: 200,
//backgroundColor: Colors.transparent,
flexibleSpace: FlexibleSpaceBar(
titlePadding: EdgeInsets.zero,
centerTitle: true,
title: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Flexible(
flex: 3,
child: Container(),
),
Flexible(
flex: 1,
child:
Text("Should be centered", textAlign: TextAlign.center),
),
Flexible(
flex: 1,
child: Container(),
),
],
),
background: Image.asset("assets/earth.png", fit: BoxFit.cover),
),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.menu),
tooltip: "Menu",
onPressed: () {
// onPressed handler
},
),
],
),
SliverFixedExtentList(
itemExtent: 50,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.green,
child: Text("Index n°$index"),
);
},
),
)
],
),
);
}
A way without empty containers
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(
titlePadding: EdgeInsets.zero,
centerTitle: true,
title: SizedBox(
height: 130,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Should be centered", textAlign: TextAlign.center),
],
),
),
background: Image.asset("assets/earth.png", fit: BoxFit.cover),
),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.menu),
tooltip: "Menu",
onPressed: () {
// onPressed handler
},
),
],
),
SliverFixedExtentList(
itemExtent: 50,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.green,
child: Text("Index n°$index"),
);
},
),
)
],
),
);
}
Instead of using a FlexibleSpaceBar use a different Widget.
flexibleSpace: Padding(
padding: EdgeInsets.all(4),
child: Container(),
}),
),
Kinda like what Pedro said earlier, you can instead use the flexibleSpace parameter in SliverAppBar, and then center things from there. This is what I did.
SliverAppBar(
flexibleSpace: Center(
child: Text("27 is my favorite number")
)
)