how to set showModalBottomSheet to full height? - dart

I use showRoundedModalBottomSheet, how can I adjust this modal height till the appbar?

[Update]
In showModalBottomSheet(...) set the property isScrollControlled:true.
It will make bottomSheet to full height.
[Original answer]
You can Implement the FullScreenDialog instead.
Flutter Gallery app has an example of FullScreenDialog
You can open your Dialog using below code:
Navigator.of(context).push(new MaterialPageRoute<Null>(
builder: (BuildContext context) {
return Dialog();
},
fullscreenDialog: true
));
Check this blog post too for more:
Hope it will help you.

You can control the height by using FractionallySizedBox and setting the isScrollControlled to true.
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) {
return FractionallySizedBox(
heightFactor: 0.9,
child: Container(),
);
});

If you call showModalBottomSheet() with isScrollControlled: true, then the dialog will be allowed to occupy the whole height.
To adjust the height to the content, you can proceed as usually, for example, using Container and Wrap widgets.
Example:
final items = <Widget>[
ListTile(
leading: Icon(Icons.photo_camera),
title: Text('Camera'),
onTap: () {},
),
ListTile(
leading: Icon(Icons.photo_library),
title: Text('Select'),
onTap: () {},
),
ListTile(
leading: Icon(Icons.delete),
title: Text('Delete'),
onTap: () {},
),
Divider(),
if (true)
ListTile(
title: Text('Cancel'),
onTap: () {},
),
];
showModalBottomSheet(
context: context,
builder: (BuildContext _) {
return Container(
child: Wrap(
children: items,
),
);
},
isScrollControlled: true,
);

What worked for me was returning the modal's content wrapped in a DraggableScrollableSheet:
showModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
isScrollControlled: true,
isDismissible: true,
builder: (BuildContext context) {
return DraggableScrollableSheet(
initialChildSize: 0.75, //set this as you want
maxChildSize: 0.75, //set this as you want
minChildSize: 0.75, //set this as you want
expand: true,
builder: (context, scrollController) {
return Container(...); //whatever you're returning, does not have to be a Container
}
);
}
)

I guess the easiest way is:
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) => Wrap(children: [YourSheetWidget()]),
);

You open class BottomSheet in library of flutter and change maxHeight
from
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
return BoxConstraints(
minWidth: constraints.maxWidth,
maxWidth: constraints.maxWidth,
minHeight: 0.0,
maxHeight: constraints.maxHeight * 9.0 / 16.0
);}
to
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
return BoxConstraints(
minWidth: constraints.maxWidth,
maxWidth: constraints.maxWidth,
minHeight: 0.0,
maxHeight: constraints.maxHeight
);}
You can create a new class with other name and copy source code from class BottomSheet and change maxHeight

You can wrap the contents in a Container and provide height to full height
await showModalBottomSheet(
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30), topRight: Radius.circular(30))),
backgroundColor: Colors.white,
builder: (BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height,
child: ListView(
)
)
}
}

You can modify this method in the definition of the bottom sheet. Normally, it is 9.0, but as you can see here I changed it to 13.0. 16.0 is full screen.
#override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
return BoxConstraints(
minWidth: constraints.maxWidth,
maxWidth: constraints.maxWidth,
minHeight: 0.0,
maxHeight: isScrollControlled
? constraints.maxHeight
: constraints.maxHeight * 13.0 / 16.0,
);
}

Related

Disable drag down to close showModalBottomSheet

How do I disable / escape drag down gesture within the Bottom Sheet Modal so the user can interact within the modal without accidentally closing the modal?
Updated below with the actual modal bottom sheet.
return showModalBottomSheet(
context: context,
builder: (BuildContext context) {
...
}
}
Set enableDrag to false
bool enableDrag
If true, the bottom sheet can dragged up and down and dismissed by swiping downwards.
https://docs.flutter.io/flutter/material/BottomSheet/enableDrag.html
you can try to wrap builder's result with GestureDetector with onVerticalDragStart = (_) {}
showModalBottomSheet(
context: context,
builder: (context) => GestureDetector(
child: **any_widget_here**,
onVerticalDragStart: (_) {},
),
isDismissible: false,
isScrollControlled: true,
);
If you still want to have the scroll inside the modal without the user drag and close it, you can use this:
showModalBottomSheet(
context: context,
enableDrag: false,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
clipBehavior: Clip.antiAliasWithSaveLayer,
builder: (context) => DraggableScrollableSheet(
expand: false,
initialChildSize: 0.9,
minChildSize: 0.5,
maxChildSize: 0.9,
builder: (context, scrollController) {
return SingleChildScrollView(
child: new Container(
color: Colors.white,
child: buildTitleWidget(),
),
);
},
),
isDismissible: false,
isScrollControlled: true,
);
The trick is not to add scrollController to the SingleChildScrollView
builder: (context, scrollController) {
return SingleChildScrollView(
controller: scrollController <-- HERE
child: new Container(
color: Colors.white,
child: buildTitleWidget(),
),
);
},
I wanted a bottomsheet that is draggable up and down, but does not close on dragging down. My thought was what if as soon as it closes, I call it again? Here, recursion comes to rescue. So, first of all I created a function for my modalBottomSheet.
Future modalBottomSheetShow(BuildContext context) {
return showModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
builder: (context) => buildSheet(),
isDismissible: false,
elevation: 0,
);
}
Next, I used .whenComplete() method of showModalBottomSheet() to recursively call the modalBottomSheetShow() function.
Future modalBottomSheetShow(BuildContext context) {
return showModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
builder: (context) => buildSheet(),
isDismissible: false,
elevation: 0,
).whenComplete(() => modalBottomSheetShow(context));
}
Next, I simply call the modalBottomSheetShow() whenever I wanted a bottomsheet. It cannot be closed, until the recursion ends. Here is the entire code for reference:
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
static const idScreen = "HomePage";
#override
State<HomePage> createState() => _HomePageState();
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
modalBottomSheetShow(context);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
toolbarHeight: 0,
elevation: 0,
backgroundColor: Colors.black,
),
);
}
Widget buildSheet() {
return DraggableScrollableSheet(
initialChildSize: 0.6,
builder: (BuildContext context, ScrollController scrollController) {
return Container(
decoration: BoxDecoration(color: Colors.white, boxShadow: [
BoxShadow(
color: Color(0x6C000000),
spreadRadius: 5,
blurRadius: 20,
offset: Offset(0, 0),
)
]),
padding: EdgeInsets.all(16),
);
},
);
}
Future modalBottomSheetShow(BuildContext context) {
return showModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
builder: (context) => buildSheet(),
isDismissible: false,
elevation: 0,
).whenComplete(() => modalBottomSheetShow(context));
}
}

showGeneralDialog not working as expected

Code:
void showNow() {
showGeneralDialog(
context: context,
pageBuilder: (BuildContext buildContext, Animation<double> animation, Animation<double> secondaryAnimation) {
return Container(
width: 200.0,
height: 200.0,
color: Colors.orange,
);
},
barrierDismissible: true,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
barrierColor: Colors.black,
transitionDuration: const Duration(milliseconds: 200),
);
}
But this dialog takes up the entire space instead of 200.0 width and height that I used. I tried wrapping Container in Scaffold, but then barrierDismissible and barrierColor doesn't seem to work. I changed their values too but no luck.
Wrap your Container in Wrap Widget :
void showNow() {
showGeneralDialog(
context: context,
pageBuilder: (BuildContext buildContext, Animation<double> animation,
Animation<double> secondaryAnimation) {
return Wrap(
children: <Widget>[
Container(
width: 200.0,
height: 200.0,
color: Colors.orange,
),
],
);
},
barrierDismissible: true,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
barrierColor: Colors.black,
transitionDuration: const Duration(milliseconds: 200),
);
}

Scrollable flutter popup menu

I'm trying to use flutter popup menu button, but I can't seem to make it smaller with a scroll.
Is it doable? Or am I using the wrong widget to do it?
Image below as reference, would like to show only the first 4 / 5 items, and scroll to show the rest!
Thanks in advance!
You can create your own PopUp Widget instead.
A Card wrapped into a AnimatedContainer with specific dimensions and a ListView inside.
Place this widget on your screen using Stack and Positioned widgets so it will be above other elements on the top | right.
class CustomPopup extends StatefulWidget {
CustomPopup({
#required this.show,
#required this.items,
#required this.builderFunction,
});
final bool show;
final List<dynamic> items;
final Function(BuildContext context, dynamic item) builderFunction;
#override
_CustomPopupState createState() => _CustomPopupState();
}
class _CustomPopupState extends State<CustomPopup> {
#override
Widget build(BuildContext context) {
return Offstage(
offstage: !widget.show,
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
height: widget.show ? MediaQuery.of(context).size.height / 3 : 0,
width: MediaQuery.of(context).size.width / 3,
child: Card(
elevation: 3,
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: widget.items.length,
itemBuilder: (context, index) {
Widget item = widget.builderFunction(
context,
widget.items[index],
);
return item;
},
),
),
),
),
);
}
}
return Stack(
children: <Widget>[
Container(
color: Colors.blueAccent,
),
Positioned(
right: 0,
top: 60,
child: CustomPopup(
show: shouldShow,
items: [1, 2, 3, 4, 5, 6, 7, 8],
builderFunction: (context, item) {
return ListTile(
title: Text(item.toString()),
onTap: () {}
);
},
),
),
],
);
You can create this in two ways: the first one is PopupMenuButton widget and the second one is PopupRoute.
class HomePage extends StatefulWidget {
#override
_HomepageState createState() => _HomepageState();
}
class _HomepageState extends State {
Listitems = [1,2,3,4,5,6,7,8,9,10,11,12,13];
#override
Widget build(BuildContext context) {
return Scaffold(body: Center(
child: PopupMenuButton(
child: Icon(Icons.add_shopping_cart),
offset: Offset(-1.0, -220.0),
elevation: 0,
color: Colors.transparent,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))),
itemBuilder: (context) {
return <PopupMenuEntry<Widget>>[
PopupMenuItem<Widget>(
child: Container(
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10))),
child: Scrollbar(
child: ListView.builder(
padding: EdgeInsets.only(top: 20),
itemCount: items.length,
itemBuilder: (context, index) {
final trans = items[index];
return ListTile(
title: Text(
trans.toString(),
style: TextStyle(
fontSize: 16,
),
),
onTap: () {
//what would you like to do?
},
);
},
),
),
height: 250,
width: 500,
),
)
];
}),
)
You can also adjust the number of items you want to show by reducing or increasing height of the container. I also added a scrollbar just in case.
You can use maxHeight for constrains property.
...
PopupMenuButton(
constraints:
BoxConstraints(minWidth: context.maxWidth, maxHeight: 300),
...

How to create a transparent full screen dialog on top of activity - Flutter

I am trying to create a transparent full screen dialog on top of activity. I have tried following this thread but solution doesn't work.
In short , what I need is:
full screen dialog.
transparent background except for the widget I use for the dialog
here's my code:
To open dialog
void onNextBtnClick(){
var route = new MaterialPageRoute(
builder: (BuildContext context) =>
new GenreDialogUI(),fullscreenDialog: true
);
Navigator.of(context).push(route);
}
For Dialog view
import 'package:flutter/widgets.dart';
class HolePuncherPainter extends CustomPainter {
static final clearPaint = new Paint()
..color = Colors.transparent,
..blendMode = BlendMode.clear;
const HolePuncherPainter();
#override
void paint(Canvas canvas, Size size) {
canvas.drawRect(
new Rect.fromLTWH(0.0, 0.0, size.width, size.height), clearPaint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
class GenreDialogUI extends StatefulWidget {
#override
_GenreDialogUI createState() => new _GenreDialogUI();
}
class _GenreDialogUI extends State<GenreDialogUI> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: addAppbar(),
body: new CustomPaint(
painter: HolePuncherPainter(),
child: new Container(
color: Colors.transparent,
alignment: Alignment.center,
child: UtilCommonWidget.addText('Transparent Dialog', Colors.red, 22.0, 1),
),
),
);
}
}
Navigator.of(context).push(PageRouteBuilder(
opaque: false,
pageBuilder: (BuildContext context, _, __) {
return YourFullScreenAlertDialog()
}
));
YourFullScreenAlertDialog could be a widget that has a background color, Colors.transparent, like #creativecreatorormaybenot mentioned earlier.
For me the following worked:
showDialog(
context: context,
builder: (_) => Material(
type: MaterialType.transparency,
child: Center(
// Aligns the container to center
child: Container(
// A simplified version of dialog.
width: 100.0,
height: 56.0,
color: Colors.green,
child: Text('jojo'),
),
),
),
);
Screenshot (showGeneralDialog):
Code:
#override
Widget build(BuildContext context) {
return Scaffold(
body: SizedBox.expand(child: FlutterLogo()),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.open_in_new),
onPressed: () {
showGeneralDialog(
context: context,
barrierColor: Colors.black38,
barrierLabel: 'Label',
barrierDismissible: true,
pageBuilder: (_, __, ___) => Center(
child: Container(
color: Colors.white,
child: Material(
color: Colors.transparent,
child: Text('Dialog', style: TextStyle(color: Colors.black, fontSize: 40),),
),
),
),
);
},
),
);
}
This is my implementation.
From first screen call this.
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (_, __, ___) => const NoInternetScreen(),
opaque: false, ---->> opacity should be false
fullscreenDialog: true));
And in next screen set background color with opacity.
return Scaffold(
backgroundColor: white.withOpacity(0.85),
......
result

Fading Edge ListView - Flutter

has anyone come across something like fadingEdgeLength in Android for Flutter so that when you scroll up items start fading into the top of the screen?
Below is my interface built up of the Widgets.
If it helps these are the properties I'm referring to:
android:fadingEdgeLength="10dp"
android:requiresFadingEdge="horizontal">
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('CMS Users'),
),
body: ListView.builder(
padding: EdgeInsets.only(top: 20.0, left: 4.0),
itemExtent: 70.0,
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, int index) {
return Card(
elevation: 10.0,
child: InkWell(
onTap: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (BuildContext context) =>
new PeopleDetails("Profile Page", profiles[index]),
));
},
child: ListTile(
leading: CircleAvatar(
child: Text(profiles[index].getInitials()),
backgroundColor: Colors.deepPurple,
radius: 30.0,
),
title: Text(
data[index]["firstname"] + "." + data[index]["lastname"]),
subtitle: Text(
data[index]["email"] + "\n" + data[index]["phonenumber"]),
),
),
);
}),
);
}
}
As others have mentioned, you can put the ListView under a ShaderMask, but with minor extra parameterizations you can get much better results - at least if you want to achieve what I wanted.
Optionally you can set the [stops] list for the LinearGradient:
The [stops] list, if specified, must have the same length as [colors]. It specifies fractions of the vector from start to end, between 0.0 and 1.0, for each color.
Plus: There are blend modes, where the color channels of the source are ignored, only the opacity has an effect. BlendMode.dstOut is also such in the example below. As you can see in the screenshot, the purple color is not used concretely, only for the fractions of the vector.
You can play with the different [blendMode] settings, there are quite a few of them.
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: FadingListViewWidget(),
),
),
);
}
class FadingListViewWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Container(
height: 320,
child: ShaderMask(
shaderCallback: (Rect rect) {
return LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.purple, Colors.transparent, Colors.transparent, Colors.purple],
stops: [0.0, 0.1, 0.9, 1.0], // 10% purple, 80% transparent, 10% purple
).createShader(rect);
},
blendMode: BlendMode.dstOut,
child: ListView.builder(
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return Card(
color: Colors.orangeAccent,
child: ListTile(
title: Text('test test test test test test'),
),
);
},
),
),
),
);
}
}
You could apply a ShaderMask on top of ListView and use BlendMode to get what you want.
Widget animationsList() {
return Expanded(
child: ShaderMask(
shaderCallback: (Rect bounds) {
return LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[Colors.transparent,Colors.red],
).createShader(bounds);
},
child: Container(height: 200.0, width: 200.0, color: Colors.blue,),
blendMode: BlendMode.dstATop,
),
);
I had similar request so I created a library for this task.
You can find it here: fading_edge_scrollview
To use it you need to add a ScrollController to your ListView and then pass this ListView as child to FadingEdgeScrollView.fromScrollView constructor
Wrap the Listview with Stack, add the Listview as the first child, the second is Positioned Container with LinearGradient.
Sample from my code:
Stack:
return Stack(
children: <Widget>[
ListView(
scrollDirection: Axis.horizontal,
children: _myListOrderByDate,
),
FadeEndListview(),
],
);
The overlay class:
class FadeEndListview extends StatelessWidget {
const FadeEndListview({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Positioned(
right: 0,
width: 8.0,
height: kYoutubeThumbnailsHeight,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerRight,
end: Alignment.centerLeft,
stops: [0.0, 1.0],
colors: [
Theme.of(context).scaffoldBackgroundColor,
Theme.of(context).scaffoldBackgroundColor.withOpacity(0.0),
],
),
),
),
);
}
}
And it will look something like this:
Try to use
Text(
'This is big text, I am using Flutter and trying to fade text',
overflow: TextOverflow.fade,
maxLines: 1,
),

Resources