I am trying to put a listView inside ModalBottomSheet in flutter and I want there to be a divider in the top of the ModalBottomSheet that doesn't move when the listView moves. And I keep getting an error
here is what I am trying to do:
ModalBottomSheet(
-Container(), // which shouldn't scroll
-ListView(), // should scroll
)
tried:
to make ModalBottomSheet.isScrollControlled = false
BottomModalSheet
Widget _showModalBottomSheet() => DraggableScrollableSheet(
expand: false,
key: UniqueKey(),
initialChildSize: 0.7,
maxChildSize: 0.9,
minChildSize: .5,
builder: (context, controller) => Column(
children: [
Container(
height: 59,
color: Colors.cyanAccent,
),
Expanded(
child: ListView(
controller: controller,
children: [
...List.generate(
40,
(index) => Container(
height: 100,
color:
index.isEven ? Colors.deepOrange : Colors.deepPurple,
),
)
],
),
),
],
),
);
And Use
await showModalBottomSheet(
isScrollControlled: true,
isDismissible: true,
backgroundColor: Colors.transparent,
context: context,
builder: (context) => _showModalBottomSheet(),
);
Related
I am trying to make my ListView of items inside my bottom sheet scrollable. I make it work with DraggableScrollableSheet widget but there's this awkward white space. Can someone review my code and tell me what's wrong?
onPressed: () {
showModalBottomSheet(
isDismissible: true,
isScrollControlled: true,
context: context,
shape: const RoundedRectangleBorder(
borderRadius: const BorderRadius.vertical(
top: Radius.circular(20),
)),
builder: (context) => DraggableScrollableSheet(
initialChildSize: 0.7,
minChildSize: 0.2,
maxChildSize: 0.75,
builder: (_, controller) => Container(
padding: const EdgeInsets.all(16),
child: ListView(
children: [
Image.network(
imageURl,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return const CircularProgressIndicator();
},
errorBuilder: (context, error, stackTrace) =>
Text(errorMessage),
),
ListTile(
title: Text(
information1,
textAlign: TextAlign.justify,
),
),
ListTile(
title: Text(
information2,
textAlign: TextAlign.justify,
),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Container(
child: CupertinoButton.filled(
padding: EdgeInsets.all(15),
child: Text(information3),
onPressed: () async {
if (await canLaunch(linkURl)) {
await launch(
linkURl,
universalLinksOnly: true,
);
} else {
throw 'There was a problem to open the url.';
}
},
),
),
),
],
),
),
),
);
},
Sample on how it works:
As you can seen in the above video, there is an awkward white space on top of the image. What's causing it and how do I get rid of it?
Thanks!
The DraggableScrollableSheet widget has a property named expand that is set by default to 'true', you need to set it to expand = false.
I want to have a modal bottom sheet that I can scroll through as normal without closing the modal bottom sheet. Then, when I'm at the top of my list, I want to be able to close the Modal Bottom Sheet with a scroll gesture.
I would like to add the YouTube style modal bottom sheet shown in the video to my app, but unfortunately I don't know how to do it.
This is the link to the video that shows what I mean: https://drive.google.com/file/d/1977Ptq4Sox6WKGQkiTbCH_zydn8Mu05o/view?usp=sharing
This is inside my Profile Screen:
#override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: Container(
height: MediaQuery.of(context).size.height * 0.95,
margin: EdgeInsets.only(bottom: 5, left: 10, right: 10),
decoration: BoxDecoration(
border: Border.all(color: Colors.black, width: 1.5),
borderRadius: BorderRadius.circular(25),
color: Colors.white,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(23),
child: DraggableScrollableSheet(
initialChildSize: 1,
minChildSize: 0.5,
maxChildSize: 1,
expand: false,
builder: (BuildContext context, ScrollController _scrollController) {
return SingleChildScrollView(
controller: _scrollController,
child: Column(
children: [
Container(
height: 590,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(width: 1.5, color:
Colors.black),
),
color: Colors.white,
),
child: Stack(
children: [
InteractiveViewer(
transformationController:
_zoomProfilePictureController,
minScale: 1,
maxScale: 3,
onInteractionEnd: (ScaleEndDetails endDetails) {
setState(() {
_zoomProfilePictureController.value =
Matrix4.identity();
});
},
child: Swiper(
physics: NeverScrollableScrollPhysics(),
itemBuilder:
(BuildContext context, int imageIndex) {
return Hero(
tag: "Profile",
child: CachedNetworkImage(
imageUrl: widget.user.imageUrl[imageIndex],
fit: BoxFit.cover,
useOldImageOnUrlChange: true,
placeholder: (context, url) => Center(
child: CircularProgressIndicator(
strokeWidth: 3,
valueColor: AlwaysStoppedAnimation<Color>(
primaryColor.withOpacity(0.7),
),
),
),
errorWidget: (context, url, error) => Icon(
Icons.error_outline,
size: 30,
color: Colors.red,
),
),
);
},
itemCount: widget.user.imageUrl.length,
pagination: SwiperPagination(
alignment: Alignment.topCenter,
margin: EdgeInsets.all(0),
builder: DotSwiperPaginationBuilder(
color: Colors.white.withOpacity(0.7),
size: 8,
activeColor: widget.user.imageUrl.length > 1
? primaryColor
: Colors.transparent,
activeSize: 10,
),
),
control: widget.user.imageUrl.length > 1
? SwiperControl(
padding: const EdgeInsets.only(top: 250),
color: Colors.transparent,
disableColor: Colors.transparent,
size: 228,
)
: null,
loop: false,
),
),
This is how I call the BottomSheet:
ListTile(
onTap: () => showModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
builder: (context) {
return ProfileInformationHomeScreen(
secondUser,
widget.currentUser,
);
},
),
it's DraggableScrollableSheet.
create a widget for bottomSheet.
#override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
),
child: DraggableScrollableSheet(
///* it will be always visible 95% of screen,
///* if we drag down at 50% it will close the sheet
initialChildSize: 0.95,
minChildSize: 0.5,
maxChildSize: .95,
expand: false,
builder: (BuildContext context, c) {
return Container(
color: Colors.white,
child: ListView(
controller: c,
children: [
/// your widgets
call a method to showModalBottomSheet.
_showBottomSheet(BuildContext context) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => DestinationBottomSheetWidget(),
);
}
To close the buttomSheet you can drag or use a button inside DraggableScrollableSheet builder and use Navigator.of(context).pop().
I do like this and add item of ListView
/// heading
Padding(
padding: EdgeInsets.only(
left: 20,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Icon(
Icons.close,
color: Colors.grey,
),
),
Text(
"Title",
textAlign: TextAlign.center,
),
///* this empty widget will handle the spaces
Icon(
Icons.close,
color: Colors.grey.withOpacity(0.0),
),
],
),
),
for more flutter doc youtube
I want to create bottom sheet like Instagram post sharing page
everything work fine but I need to start flexibleSpace animation before DraggableScrollableSheet at the maxChildSize property and finished at maxChildSize
Stack(
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
Navigator.of(cotext).pop()
},
child: Container(
width: context.width,
height: context.height,
),
),
SizedBox.fromSize(
size: Size.fromHeight(context.height),
child: DraggableScrollableSheet(
minChildSize: 0.5,
maxChildSize: 1,
initialChildSize: 0.5,
builder: (context, scroll) {
return SafeArea(
child: Container(
color: Colors.white,
child: CustomScrollView(
controller: scroll,
slivers: [
SliverAppBar(
pinned: true,
title:title,
),
SliverList(
delegate:
SliverChildBuilderDelegate((context, index) {
return Object()
}, childCount: 100))
],
),
),
);
},
),
),
],
);
What I would like to do
I want to make it possible for the user to drag the blue area up with their finger.
Problem
cant drag up draggable
Architecture
NestedScrollView (
headerSliver:SliverAppBar(...)
body: Stack(
childern: [
Expanded(
child: SliverGrid(),
),
Draggable(),
],
),
),
What I checked
I confirmed that the touch works on the draggable. I made an InkWell with a builder and confirmed that the onPressed function works.
I tried with CustomScrollView as well, but it still didn't work.
code
NestedScrollView
NestedScrollView(
headerSliverBuilder: (context, innerBoxScrolled) {
return [
SliverAppBar(
leading: null,
elevation: 0,
expandedHeight: 160,
pinned: false,
flexibleSpace: TopNavBar(
height: 100,
),
bottom: AppBar(
leading: null,
toolbarHeight: 100,
elevation: 0,
actions: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AdTopTabBar(),
FilterBar(),
],
),
],
),
),
];
},
body: Stack(
children: [
Expanded(
child: HomeAdCollection(),
),
MyAdDraggable(),
],
),
),
Draggable
class MyAdDraggable extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DraggableScrollableSheet(
key: key,
initialChildSize: 0.05,
minChildSize: 0.05,
maxChildSize: 0.7,
expand: true,
builder: (context, scrollcontroller) {
return InkWell(
onTap: () {
print('ff');
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
color: Colors.blue[100],
),
),
);
},
);
}
}
There must be at least one child in the DraggableScrollableSheet that uses scrolling. Otherwise, you can't drag it up.
When I put ListView as a child in DraggableScrollableSheet, it works well!
I'm using a Flutter modal bottom sheet to display some options for the user to select.
I have a Column with a list of ListTiles as the content of the bottom sheet.
My problem is that if I have more than 6 ListTiles, some are cut off and not displayed.
Is there a way to make the bottom Sheet scrollable?
Just change your Column into a ListView, like so:
return ListView(
children: <Widget>[
...
]
);
What if I don't want the content of the sheet to be scrolled, but the sheet itself?
If you want the user to be able to swipe up the bottom sheet to fill the screen, I'm afraid this isn't possible in the current implementation of the modular bottom sheet.
I've found a solution implementing the code below
void _showBottomSheet(BuildContext context) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) {
return GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
color: Color.fromRGBO(0, 0, 0, 0.001),
child: GestureDetector(
onTap: () {},
child: DraggableScrollableSheet(
initialChildSize: 0.4,
minChildSize: 0.2,
maxChildSize: 0.75,
builder: (_, controller) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(25.0),
topRight: const Radius.circular(25.0),
),
),
child: Column(
children: [
Icon(
Icons.remove,
color: Colors.grey[600],
),
Expanded(
child: ListView.builder(
controller: controller,
itemCount: 100,
itemBuilder: (_, index) {
return Card(
child: Padding(
padding: EdgeInsets.all(8),
child: Text("Element at index($index)"),
),
);
},
),
),
],
),
);
},
),
),
),
);
},
);
}
If you want a model bottom sheet that can be scrolled.
You can use the isScrollControlled attribute of showModalBottomSheet to achieve the effect.
If you want a persistent bottom sheet that can be scrolled.
You can use the DraggableScrollableSheet
Example:
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('DraggableScrollableSheet'),
),
body: SizedBox.expand(
child: DraggableScrollableSheet(
builder: (BuildContext context, ScrollController scrollController) {
return Container(
color: Colors.blue[100],
child: ListView.builder(
controller: scrollController,
itemCount: 25,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text('Item $index'));
},
),
);
},
),
),
);
}
}
Here is the official video from the Flutter team.
A live demo on DartPad can be found here.
Almost the same solution like this one but without the unnecessary layers of gesture detectors etc.
The important part is the expand: false, in DraggableScrollableSheet, because it defaults to true. This causes the bottom sheet to expand to full height in default config. With this set to false there is no need to wrap the bottom sheet with two gesture detectors to detect the outside tap.
Also in this case only one shape is required.
showModalBottomSheet(
context: context,
isScrollControlled: true,
isDismissible: true,
shape: const RoundedRectangleBorder(
borderRadius:
BorderRadius.vertical(top: Radius.circular(16))),
builder: (context) => DraggableScrollableSheet(
initialChildSize: 0.4,
minChildSize: 0.2,
maxChildSize: 0.75,
expand: false,
builder: (_, controller) => Column(
children: [
Icon(
Icons.remove,
color: Colors.grey[600],
),
Expanded(
child: ListView.builder(
controller: controller,
itemCount: 100,
itemBuilder: (_, index) {
return Card(
child: Padding(
padding: EdgeInsets.all(8),
child: Text("Element at index($index)"),
),
);
},
),
),
],
),
),
);