I am using a CustomScrollView. I have added a few slivers in it but one of my children has to be a stack which has a list as on of its two children. I tried using SliverToBoxAdapter but that makes the list in the stack non scrollable which is the obvious behaviour. Is there a way to to write some kind of SliverToSliverAdapter? I tried reading the SliverPadding but its looks too complicated for me to understand. Below is my code:
CustomScrollView(
key: PageStorageKey<String>(name),
slivers: <Widget>[
SliverOverlapInjector(
handle:
NestedScrollView.sliverOverlapAbsorberHandleFor(context)),
SliverToBoxAdapter(
child: Stack(
children: <Widget>[
Container(
width: double.infinity,
height: 50,
decoration: BoxDecoration(color: pink),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: ListView.builder(
padding: const EdgeInsets.all(0),
shrinkWrap: true,
itemBuilder: (context, index) {
return buildSongRow(songs[index]);
},
itemCount: songs.length,
),
)
],
)),
],
);
Stack widget use parent's size by default. While parent widget is CustomScrollView that decide it's size mean, it will get infinite height. To solve this issue, we can wrap Stack with SizedBox(height:x).
Next issue comes on Stack about scrolling and widget visibility. Widget render prioritize bottom to top and same for the scroll event. While Container(height:50) is the first child of Stack, it will be paint behind ListView and in this case we can have full-scroll event on ListView.
We can move Container<height:50> at the bottom to paint it over ListView. In this case, scroll-event won't work on Container<h:50> portion.
We can wrap Container<h:50> and ListView with SizedBox by providing height and use Align or Positioned widget to place them on Stack. Also, we can increase itemCount+1 and use an empty SizedBox(height:50)(on index 0) that will take same amount of height as Container(height:50) and help to view only for 1st time on Stack.
I am using LayoutBuilder to get the size/constraints of screen.
return Scaffold(
body: LayoutBuilder(
builder: (context, constraints) => CustomScrollView(
slivers: [
const SliverAppBar(
title: Text("Stack On CustomScrollView"),
),
SliverToBoxAdapter(
child: SizedBox(
height: constraints.maxHeight * .3, // used by stack
child: Stack(
children: [
Align(
alignment: Alignment.topCenter,
child: Container(
// dont need to use sizedBox here, height is fixed by container's height:50
height: 50,
color: Colors.pink,
alignment: Alignment.center,
child: const Text("Stack Child: Container<height: 50>"),
),
),
Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
height: constraints.maxHeight * .3 - 50,
child: ListView.builder(
itemCount: 4,
itemBuilder: (context, index) => Container(
alignment: Alignment.center,
height: 50,
color: index.isEven
? Colors.deepOrange
: Colors.deepPurple,
child: Text("ListViewItem:$index"),
),
),
),
),
],
),
),
),
SliverList(
delegate: SliverChildListDelegate(
[
...List.generate(
44,
(index) => Container(
height: 100,
alignment: Alignment.center,
color: index.isEven ? Colors.cyanAccent : Colors.blueGrey,
child: Text("SliverList $index"),
),
),
],
),
),
],
),
),
);
Related
i want a slidingUpPanel similar to YouTube comments section. that not open with the scrolling of the content but open and close only from the drag handle by tap or by swipe up/down).
the drag handle is the top section of the slidingUpPanel :
so the draggable should happen only from this drag handle not from the whole body. (you can try the code below to know what i mean)
it means if i scroll the body(the listView builder) the slidingUpPanel keep its place (either open or closed) until i swip it from the drag handle.
my current code:
PanelController panelController = PanelController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Stack(
children: [
SlidingUpPanel(
controller: panelController,
maxHeight: MediaQuery.of(context).size.height * .9,
minHeight: MediaQuery.of(context).size.height * .7,
borderRadius: BorderRadius.vertical(top: Radius.circular(40.0)),
panelBuilder: (controller) => Stack(
alignment: Alignment.topCenter,
children: [
GestureDetector(
onTap: () {
panelController.isPanelOpen
? panelController.close()
: panelController.open();
},
child: Container(
color: Colors.transparent,
height: 36.0,
width: MediaQuery.of(context).size.width,
child: Center(
child: Container(
width: 46.77,
height: 4.0,
decoration: BoxDecoration(color: Colors.grey),
),
),
),
),
Padding(
padding: const EdgeInsets.only(top: 40.0),
child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
controller: controller,
padding: EdgeInsets.zero,
itemCount: 20,
itemBuilder: (context, index) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 70.0,
color: Colors.red,
),
),
],
);
}),
),
],
)),
],
),
));
}
[enter image description here][1]
Inside My column, there is a stack, a carousel slider,a gridview builder. I want to scroll all of them together. I tried to use singlechildscrollview as you can see in the code below. Please Someone help me how can I scroll those things together.
[1]: https://i.stack.imgur.com/FYexC.png` enter code here`
Scaffold(
backgroundColor: Colors.orange,
// Color(0xFFFFF176),
body: SingleChildScrollView(
child: Column(
children: [
Expanded(
child: Stack(
alignment: Alignment.bottomCenter,
children: [
CarouselSlider(
items: slideImage
.map((image) =>
Builder(builder: (BuildContext context) {
return Container(
height: 200,
width: 500,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40),
image: DecorationImage(
image: AssetImage(image),
fit: BoxFit.fill),
),
);
}))
.toList(),
options: CarouselOptions(
onPageChanged: (index, reason) {
setState(() {
activeIndex = index;
});
},
height: 300,
viewportFraction: 1,
autoPlay: true,
),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: AnimatedSmoothIndicator(
activeIndex: activeIndex, count: slideImage.length),
),
],
),
),
SizedBox(
height: 10,
),
Expanded(
child: GridView.builder(
shrinkWrap: true,
// physics: NeverScrollableScrollPhysics(),
itemCount: menuImage.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.8,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemBuilder: (context, index) => Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
color: Color(0xFFFFFDE7),
elevation: 10,
child: GridTile(
child: Column(
children: [
Image.asset(
menuImage[index],
fit: BoxFit.fill,
),
Text(
title[index],
style: TextStyle(fontSize: 18),
),
],
),
),
),
),
),
],
),
),
);
}
}
It would be best if you gave height to the column you are using singlechildeScrollview.
Wrap column widget in container or sizedbox widget and give it height.
column widget has children widget GridView.builder that is wrapped in expanded so gridview.builder will try to take all available height in the column which will be infinite because it's not defined and you will get column covering screen taking infinite screen height in short blank screen.
In flutter, Column will take height as much as it can, SingleChildScrollView child dont have height (infinite). Combine together, Flutter don't know how to render them correctly and causing error.
To solve it, try to determine the height of Column by widgget like SizedBox or determine the mainSize.min.
Another way to solve it is wrap Column by IntrinsicHeight but not recommend cause it heavy.
If it takes too much height. so give height to the column.
like this..
SingleChildScrollView(
child: SizedBox(
height: 500,
child: Column(
children: const [...],
),
),
)
your expanded widget is taking whole space of column try removing it or replacing it with Sizedbox.
In my Flutter app, I have a ListView:
return Scaffold(
body: SafeArea(
child: Container(
color: Colors.blueAccent,
child: ListView(
children: [
Container(
color: Colors.yellow,
height: 100
),
const SizedBox(height: 10),
// How to make this container fill up the remaining height?
Container(
color: Colors.black,
),
],
),
),
),
);
This produces the following view:
Question: How can I make the second box (with black background color) fill up the remaining space? If the content of the box exceeds the remaining space, the ScrollView will enable scrolling.
Is this possible?
There is a better option here using SliverFillRemaining in combination with CustomScrollView instead of a ListView
in your case the code will look like this.
Container(
color: Colors.blueAccent,
child: CustomScrollView(
shrinkWrap: true, // or false
slivers: <Widget>[
SliverToBoxAdapter(
child:Container(
color: Colors.yellow,
height: 100
),),
SliverToBoxAdapter(
child: SizedBox(height: 10),),
// use SliverFillRemaining to make this container fill up the remaining height?
SliverFillRemaining(
hasScrollBody: false, // if using a column, so the widgets in it, doesn't scroll
child:Container( //this container will fill the remaining space in the ViewPort
color: Colors.black,
),
) ,
],
),
),
Fill the remaining space of a ListView means filling the infinite height. I will prefer using Stack as body in this case, hope you can simply archive this.
How can we do without stack?
In this case, we can get the height and use it on Column and using Expanded on inner child that will fill the remaining spaces even for dynamic height.
I prefer using LayoutBuilder to get height.
body: LayoutBuilder(builder: (context, constraints) {
return SafeArea(
child: Container(
color: Colors.blueAccent,
child: ListView(
children: [
SizedBox(
// 1st child of listView
height: constraints.maxHeight,
child: Column(
children: [
Container(color: Colors.yellow, height: 100),
const SizedBox(height: 10),
Expanded(
child: Container(
color: Colors.black,
),
),
],
),
)
],
),
),
);
}),
You can get size and then set a proportion for each...
And set NeverScrollableScrollPhysics() to ensure no slight scrolling, as per below this appears to get what you are seeking.
Widget build(BuildContext context) {
Size _size = MediaQuery.of(context).size;
return Container(
child: Scaffold(
body: SafeArea(
child: Container(
color: Colors.blueAccent,
child: ListView(
physics: NeverScrollableScrollPhysics(),
children: [
Container(color: Colors.yellow, height: _size.height * 0.20),
SizedBox(height: _size.height * 0.10), // How to make this container fill up the remaining height?
Container(
height: _size.height * 0.70,
color: Colors.black,
),
],
),
),
),
));
}
}
Try below code hope its helpful to you. use MediaQuery here. add MediaQuery height to your black container
Refer blog also here for relative to screen size
Container(
color: Colors.blueAccent,
child: ListView(
shrinkWrap: true,
physics: ScrollPhysics(),
children: [
Container(
color: Colors.yellow,
height: 100,
),
const SizedBox(height: 10),
// How to make this container fill up the remaining height?
Container(
color: Colors.black,
height: MediaQuery.of(context).size.height,
),
],
),
),
Your result screen->
I am trying to achieve scroll in Flutter Web where I have few containers which are stacked and I use SingleChildScrollView to scroll the widget. However, when I scroll the first container everything working fine but the second one which is a child of the second container responds to the scroll without completing the initial one. And also is there a way to make a sticky header for the second container. How can I make the 3rd container(orange) to scroll after the 2nd(blue) one is finished scrolling? Here is what I am trying to achieve:
https://yobithemes.com/demo/html/freda/dark-video-index.html
And here what I got so far:
class MainScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
IntroScreen(),
SingleChildScrollView(
child: Container(
child: Column(
children: <Widget>[
SizedBox(
height: MediaQuery.of(context).size.height - 100,
),
Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
color: Colors.blue,
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: MediaQuery.of(context).size.height,
),
Container(
padding: EdgeInsets.only(top: 100),
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
color: Colors.orange,
),
],
),
),
),
],
),
),
),
],
),
);
}
}
You can achieve it by using sliver.
SliverToBoxAdapter fill the transparent area with screen height - app bar height.
SliverAppBar: make it sticky by setting floating and pin to true
class MainScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
IntroScreen(),
CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Container(
height: MediaQuery.of(context).size.height - 50,
),
),
SliverAppBar(
// toolbarHeight: 50,
floating: true,
pinned: true,
title: Container(
child: Center(child: Text('Header')),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => Container(
height: MediaQuery.of(context).size.height-50,
color: Colors.primaries[index % Colors.primaries.length],
),
),
),
],
),
],
),
);
}
}
I am tring to create ui as below image. but button hide behind the list. Also faces issue button not click when used Positioned widget for button.Please suggest how to create below UI using Stack widget with ListView & Floating button.
UI
Container(
color: Colors.grey,
child: Column(
children: <Widget>[
Stack(
children: <Widget>[
Column(
children: <Widget>[
Container(
height: 250.0,
width: MediaQuery.of(context).size.width,
child: Image.asset(
"images/1.jpg",
fit: BoxFit.cover,
),
)
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Container(
height: 220.0,
),
Container(
padding: EdgeInsets.only(right: 15.0),
child: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.favorite),
),
),
],
),
Column(
children: <Widget>[
Container(
height: 250.0,
),
Container(
height: MediaQuery.of(context).size.height - 250.0,
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text("Title $index"),
leading: Icon(Icons.home),
),
);
},
itemCount: 15,
),
),
],
)
],
),
],
),
);
As per documentation (https://docs.flutter.io/flutter/widgets/Stack-class.html):
The stack paints its children in order with the first child being at the bottom
So your Stack children's order is wrong. Your current order is:
Header
Button
List
But it should be:
Header
List
Button
Otherwise list is overlapping button.