I have a long text and I need to show the scrollbar by default when the user enters my page.
Currently, the bars not shown until the user click over the text and this, not good behavior because the user could leave the page without notice that there is some unread text.
My code:
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(15.0),
child: Center(
child: Column(
children: <Widget>[
Image.asset(
"assets/images/logo.png",
height: 200.0,
),
SizedBox(
height: 40,
),
Expanded(
child: Scrollbar(
child: SingleChildScrollView(
child: Text("Long Text Here ...",
textDirection: TextDirection.rtl,
style: TextStyle(fontSize: 17.2),
),
),
),
),
SizedBox(
height: 50,
),
Row(
children: <Widget>[
Expanded(
child: RaisedButton(
child: Text("Continue"),
onPressed: () {
MaterialPageRoute route = MaterialPageRoute(
builder: (BuildContext context) => MainPage());
Navigator.of(context).push(route);
},
),
),
SizedBox(
width: 20.0,
),
RaisedButton(
child: Text("Close"),
onPressed: () {
exit(0);
},
),
],
)
],
),
),
),
);
}```
As of Flutter version 1.17, on Scrollbar you can set isAlwaysShown to true, but you must set the same controller for your Scrollbar and your SingleChildScrollView (and that applies to any other scrollable Widget as well).
Have in mind that, for the Scrollbar to be visible, there must be enough items to scroll. If there are not, the Scrollbar won't be shown.
Full working example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: MyWidget(),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
final _scrollController = ScrollController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(15.0),
child: Center(
child: Column(
children: <Widget>[
// ...
Expanded(
child: Scrollbar(
controller: _scrollController, // <---- Here, the controller
isAlwaysShown: true, // <---- Required
child: SingleChildScrollView(
controller: _scrollController, // <---- Same as the Scrollbar controller
child: Text(
"Long Text Here ...",
textDirection: TextDirection.rtl,
style: TextStyle(fontSize: 17.2),
),
),
),
),
// ...
],
),
),
),
);
}
}
As of v2.9.0-1.0, thumbVisiblity is the proper field to set.
Note you can set this globally for your app (or a certain subtree) using ScrollbarTheme:
return MaterialApp(
...
theme: ThemeData(
...
scrollbarTheme: ScrollbarThemeData(
thumbVisibility: MaterialStateProperty.all<bool>(true),
)
)
)
It's good to prefer themes for styling like this, so avoid doing more than once.
You'll still need to add a Scrollbar and Controller as described in other answers though.
Use draggable_scrollbar package. It provides a dragable scrollbar with option to make the scrollbar always visible. For example, you can use the following code
DraggableScrollbar.arrows(
alwaysVisibleScrollThumb: true, //use this to make scroll thumb always visible
labelTextBuilder: (double offset) => Text("${offset ~/ 100}"),
controller: myScrollController,
child: ListView.builder(
controller: myScrollController,
itemCount: 1000,
itemExtent: 100.0,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(8.0),
child: Material(
elevation: 4.0,
borderRadius: BorderRadius.circular(4.0),
color: Colors.purple[index % 9 * 100],
child: Center(
child: Text(index.toString()),
),
),
);
},
),
);
'isAlwaysShown' is deprecated and shouldn't be used. Use thumbVisibility instead.
Example:
Scrollbar(
controller: ScrollController(),
thumbVisibility: true,
child: SingleChildScrollView(
child: Column(
thumbVisibility make true for show always scroll bar for list in scrollbar widget
Scrollbar(thumbVisibility: true,)
You can change the scrollbartheme to set flag isAlwaysShown true
scrollbarTheme: const ScrollbarThemeData(isAlwaysShown: true)
Related
I created an application in android studio to navigate from one screen to another.Here two stateless widgets are created as two screens and both contain a button to navigate pages each other.
However when i run the application a red screen is generated on my android phone I get an error saying
exception 'Column's children must not contain any null values, but a null value was found at index 0'.
I have provided my code below:
FIRST SCREEN
class FirstScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("First Screen"),
),
body: Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
center(
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage('assets/new 7wonders.jpg'),
fit: BoxFit.cover,
),
),
),
Text('New 7 Wonders',
style: TextStyle(fontSize: 40, fontStyle: FontStyle.italic),
),
RaisedButton(
child: Text("Bang Here"),
onPressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => SecondScreen()));
},
color: Colors.red,
textColor: Colors.yellow,
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
splashColor: Colors.grey,
)
],
),
),
),
);
}
center({BoxDecoration decoration}) {}
}
SECOND SCREEN
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Screen"),
),
body: RaisedButton(
child: Text("Go to First page"),
onPressed:() {
Navigator.pop(context);
},
),
);
}
}
Your center method should return a Widget, it is currently providing null to the Column.
Do this instead:
Widget center() {
// return a decorated box widget which gives you the decoration property
return Image(
image: AssetImage(
'assets/new 7wonders.jpg',),
fit: BoxFit.cover,
);
}
}
Then use in your Column like :
Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// call the center method which returns a Widget
center(),
Text(
'New 7 Wonders',
style: TextStyle(fontSize: 40, fontStyle: FontStyle.italic),
),
RaisedButton(
child: Text("Bang Here"),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => SecondScreen()));
},
color: Colors.red,
textColor: Colors.yellow,
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
splashColor: Colors.grey,
)
],
),
),
),
you have to return any widget in center
center({BoxDecoration decoration}) {
return Container();
}
You tryed write Center instead center in line 24?
And in Center must be will return for example Containter()
In 24th line, you returned null value. You can implement the center method like this;
return Container();
Remove center use this
Container(
height: 100, // height and width according to your ui
width:100,
child:Image.asset(('assets/new7wonders.jpg',fit: BoxFit.cover,), // use for local image from asset and please change image name in code as well as in asset folder.there should not be space between image name .
),
I have several modalBottomSheet in my app to show, most of them have simple widget tree except only one that has 10 DropDownBottom widgets in it
Each one of them load about 200 items, each item is a widget consist of two main widgets a text and an image
when I Press
onPressed: () {
showModalBottomSheet(context: context, builder: picksWidget, isScrollControlled: true);
}
It take about 3 seconds to open load the modalBottomSheet and it just appear into the emulator without the sliding up animation, other modalBottomSheets in the app load perfectly fine, here is an example of the code I use.
import 'package:flutter/material.dart';
import 'package:myapp/data/picked.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:myapp/data/picks.dart';
import 'package:myapp/data/Icons.dart';
Widget buildPickerBottomSheet(BuildContext context) {
return Wrap(children: <Widget>[
PickerList(),
]);
}
class PickerList extends StatelessWidget {
const PickerList({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
padding: EdgeInsets.all(20),
child: Row(
children: <Widget>[
AutoSizeText(
'Pick ',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
textAlign: TextAlign.right,
maxLines: 1,
),
],
),
),
SizedBox(
height: 5,
),
PickerRow(
typeOne: 'vr',
typeTwo: 'er',
type: 'r',
),
PickerRow(
typeOne: 'vq',
typeTwo: 'eq',
type: 'q',
),
PickerRow(
typeOne: 'vw',
typeTwo: 'ew',
type: 'w',
),
PickerRow(
typeOne: 'vz',
typeTwo: 'ez',
type: 'z',
),
PickerRow(
typeOne: 'vy',
typeTwo: 'ey',
type: 'y',
),
SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
RaisedButton(
child: Text('Add'),
onPressed: () async {
print('added');
},
),
RaisedButton(
child: Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
)
],
),
SizedBox(
height: 50,
)
],
);
}
}
class PickerRow extends StatelessWidget {
final String typeOne;
final String typeTwo;
final String type;
PickerRow({#required this.typeOne, #required this.typeTwo, #required this.type});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 20, right: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
DropDownMenu(
pickType: typeOne,
),
Container(
width: 2,
height: 30,
color: Colors.blue,
),
Container(
height: 30,
width: 30,
child: Image(
image: AssetImage(AppIcons.types[type]),
),
),
Container(
width: 2,
height: 30,
color: Colors.red,
),
DropDownMenu(
pickType: typeTwo,
),
],
),
);
}
}
class DropDownMenu extends StatefulWidget {
//which position will this pick for
final String pickType;
DropDownMenu({#required this.pickType});
#override
_DropDownMenuState createState() => _DropDownMenuState();
}
class _DropDownMenuState extends State<DropDownMenu> {
//get a list of the picks to display in the drop down
static List<DropdownMenuItem> getDropDownItems() {
List<DropdownMenuItem<String>> dropDownItems = [];
for (int i = 0; i < Picks.picksNames.length; i++) {
String pick = Picks.picksNames[i];
var newItem = DropdownMenuItem(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
CircleAvatar(
backgroundImage: AssetImage(AppIcons.picks[pick]),
radius: 15,
),
SizedBox(
width: 4,
),
AutoSizeText(
pick,
maxLines: 1,
),
],
),
value: pick,
);
dropDownItems.add(newItem);
}
return dropDownItems;
}
var items = getDropDownItems();
#override
Widget build(BuildContext context) {
String chosenItem = Picked.selection[widget.pickType];
return DropdownButton<String>(
value: chosenItem,
items: items,
onChanged: (value) {
setState(() {
chosenItem = value;
});
Picked.selection[widget.pickType] = value;
},
);
}
}
I am new to development in general so I appreciate if there is any resources on how to measure and improve performance on flutter apps.
Thanks.
It seems like your bottomModalSheet is too heavy. It loads only after the children have completed building.
Resize the images that you use inside the bottomModalSheet.
Usually the debug app is very slow compared to the release app.
Generate the apk of the app: flutter build apk --split-per-abi.
Install the generated apk build/app/outputs/apk/release/app-armeabi-v7a-release.apk and see if the problem still persists.
You should avoid rendering 2000 widgets at once.
First of all look at ListView widget and it's builder(). Try to refactor your code and put all your items inside this ListView. It will increase your performance a lot due to the reason that ListView.builder() allows you not to store all your widgets in memory all the time.
https://api.flutter.dev/flutter/widgets/ListView/ListView.builder.html
I would like to create a view that has to have a Column with a scroll view (e.g. something like SingleChildScrollView) and a footer regardless of the screen size. If the screen is big enough, it will use the empty space between the scroll and the footer, if not, it will expand and only make the widget above the footer scrollable.
It's more or less like Listview with scrolling Footer at the bottom but with a diference that I want the keyboard to overflow the footer and it also should stay in place.
Something like
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(left: 30.0, right: 30.0, top: 80.0),
child: Form(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
// Multiple widgets and form fields
],
),
),
),
),
Padding(
padding: const EdgeInsets.only(top: 50.0),
child: SafeArea(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
// Footer widgets
],
),
),
)
],
),
);
For those who were looking to implement just footer with scrollview in a simple way, below code might help :
Scaffold(
appBar: buildAppBar('Some cool appbar'),
body: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
PackageCard(),
PackageCard(),
PackageCard(),
],
),
),
),
Container(
child: Text('Your super cool Footer'),
color: Colors.amber,
)
],
),
);
Visual representation:-
---Column
|
|---Expanded--
|-SingleChildScrollView (column /YOUR SCROLLABLE VIEW)
|
|-Container(YOUR FOOTER)
I used expanded with SinglechildScrollView over here
Even though the Rémi answer being right, I've actually found an easier way to achieve what I was looking for by just combining the LayoutBuilder with the IntrinsicHeight.
class ScrollableFooter extends StatelessWidget {
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: constraints.copyWith(
minHeight: constraints.maxHeight,
maxHeight: double.infinity,
),
child: IntrinsicHeight(
child: Column(
children: <Widget>[
// Your body widgets here
Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: // Your footer widget
),
),
],
),
),
),
);
});
}
}
How I solved this was to wrap the fixed Footer and The SingleChildScrollView in a Stack widget then align the Footer accordingly.
class ScrollableFooter extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
SingleChildScrollView(
child: Container(
padding: EdgeInsets.all(5),
child: Column(
children: <Widget>[
// Your body widgets here
],
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: // Your fixed Footer here,
),
],
);
}
}
The accepted solution works in many cases, but it becomes tricky when you want to use something like a ListView because it can't provide an intrinsic height. I tried to find some different solution, and turns out I could, and it seems more flexible. I managed to solve this situation using slivers. Where the content is inside a sliver, and the footer is also inside a sliver.
Tip: Watch "The Boring Flutter Development Show, Ep. 12", which is all about slivers.
return Scaffold(
body: CustomScrollView(
shrinkWrap: true,
slivers: [
SliverToBoxAdapter(
child: Column(
children: [
//content widgets
],
),
),
SliverFillRemaining(
hasScrollBody: false,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
//footer widgets,
],
),
),
],
),
);
The difficulty is that Column and SingleChildScrollView have a hard time working together because one needs constraints and the other removes them.
The trick is to use a CustomMultiChildLayout and do the calculations yourself. Helped by MediaQuery to obtain the size of the keyboard, so that the footer can disappear to leave more room for the content.
Here's a reusable widget that does it for you:
class FooterLayout extends StatelessWidget {
const FooterLayout({
Key key,
#required this.body,
#required this.footer,
}) : super(key: key);
final Container body;
final Container footer;
#override
Widget build(BuildContext context) {
return CustomMultiChildLayout(
delegate: _FooterLayoutDelegate(MediaQuery.of(context).viewInsets),
children: <Widget>[
LayoutId(
id: _FooterLayout.body,
child: body,
),
LayoutId(
id: _FooterLayout.footer,
child: footer,
),
],
);
}
}
enum _FooterLayout {
footer,
body,
}
class _FooterLayoutDelegate extends MultiChildLayoutDelegate {
final EdgeInsets viewInsets;
_FooterLayoutDelegate(this.viewInsets);
#override
void performLayout(Size size) {
size = Size(size.width, size.height + viewInsets.bottom);
final footer =
layoutChild(_FooterLayout.footer, BoxConstraints.loose(size));
final bodyConstraints = BoxConstraints.tightFor(
height: size.height - max(footer.height, viewInsets.bottom),
width: size.width,
);
final body = layoutChild(_FooterLayout.body, bodyConstraints);
positionChild(_FooterLayout.body, Offset.zero);
positionChild(_FooterLayout.footer, Offset(0, body.height));
}
#override
bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) {
return true;
}
}
Used as such:
FooterLayout(
body: body,
footer: footer,
),
Although the accepted answer seems to work on mobile devices, problems occur when the width is (much) bigger than the height. When that happens, the IntrinsicHeight acts like an AspectRatio, and the height increases so the footer is pushed off the screen.
I think the problem is with the definition used by the IntrinsicHeight of its internal workings:
... instead size itself to a more reasonable height.
I can confirm that #Rémi's solutions works also in those cases.
It deserves to be a standard widget provided by the Flutter framework.
I'm trying to make all Text(length varies) in a fixed width box to fit and I want them to look at the same size as the longest word's size when FittedBox applied. So What I'm doing to achieve that is filling the rest of the word with empty space to match the length to the longest word. But that doesn't quite work as you see in the following image:
Here is the code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routes: {
'/': (context) => HomePage(),
},
);
}
}
class HomePage extends StatelessWidget {
final String padding = " " * 5;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: Center(
child: Container(
color: Colors.red,
height: 100.0,
width: 100.0,
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: FittedBox(
fit: BoxFit.scaleDown,
child: Text(
"${padding}Demo$padding",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.black),
),
),
),
Icon(Icons.mic, size: 24.0),
],
),
),
),
);
}
}
Do you have any idea what is the issue here?
It's really strange, but it seems to work correctly if you remove alignment or set it to start. (What cause this bug - I still don't know)
child: Text('${padding}Demo$padding',
style: TextStyle(color: Colors.black, decoration: TextDecoration.underline),)
I've added decoration to see these spaces
I'm probably missing something obvious here, but my BottomSheet only takes up the bottom half the screen, even though the widgets in it take up more space. So now there is scrolling behavior inside the BottomSheet. I'd like to be able to increase the BottomSheet so that the user doesn't have to scroll as much.
I also want to add a borderRadius to the top of my BottomSheet, so that it looks more "modal"-y or "tab"-like.
Code:
void _showBottomSheet(BuildContext context) {
showModalBottomSheet<Null>(
context: context,
builder: (BuildContext context) {
return _bottomSheetScreen; // defined earlier on
},
);
}
I've tried:
showModalBottomSheet<Null>(
context: context,
builder: (BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: _borderRadius,
),
height: 1000.0,
child: _bottomSheetScreen,
);
},
);
but it seems like that only affects the contents inside the BottomSheet, and does not customize the BottomSheet itself.
Default height for bottomSheet is half the screenSize
If you want your bottomSheet to EXPAND according to your content DYNAMICALLY
use below code
showModalBottomSheet<dynamic>(
isScrollControlled: true,
context: context,
builder: (BuildContext bc) {
return Wrap(
children: <Widget>[...]
)
}
)
This will automatically expand the bottomSheet according to content inside.
For adding a radius on top of bottomSheet return below code to `bottomSheet'
Container(
child: Container(
decoration: new BoxDecoration(
color: forDialog ? Color(0xFF737373) : Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(25.0),
topRight: const Radius.circular(25.0))),
child: yourWidget(),
),
)
Complete code meeting both requirements
showModalBottomSheet<dynamic>(
isScrollControlled: true,
context: context,
builder: (BuildContext bc) {
return Wrap(
children: <Widget>[
Container(
child: Container(
decoration: new BoxDecoration(
color: forDialog ? Color(0xFF737373) : Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(25.0),
topRight: const Radius.circular(25.0))),
child: yourWidget(),
),
)
]
)
}
)
It's possible this way
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => Container(
height: MediaQuery.of(context).size.height * 0.75,
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(25.0),
topRight: const Radius.circular(25.0),
),
),
child: Center(
child: Text("Modal content goes here"),
),
),
);
Set isScrollControlled: true and backgroundColor: Colors.transparent for the modal
Provide a Container with required height: as root widget to modal builder
Provide BoxDecoration with required borderRadius for the Container
You can use a Column Inside a SingleChildScrollView to dynamically change the height of bottom sheet and also it gets scrollable once it exceeds the available max height, make sure the isScrollControlled is set to true,
And for the border radius the shape property will help you add the borderRadius to the bottomsheet.
here's a dartpad example for the same
Future<void> _showBottomSheet() async {
return showModalBottomSheet(
isScrollControlled: true,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(13)),
backgroundColor: Colors.white,
context: context,
builder: (context) => SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: List.generate(kBoxes, (index) => _squareBox(index)))),
);
}
No need to wrap anything. Only set:
isScrollControlled: true in showModalBottomSheet
shrinkWrap: true, in ListView
Here is the minimal general code:
import 'package:flutter/material.dart';
Future<Widget> show123(BuildContext context) {
return showModalBottomSheet<dynamic>(
useRootNavigator: true,
isScrollControlled: true,
context: context,
builder: (BuildContext bc) {
return ListView(
shrinkWrap: true,
children: [
ListItem(),
ListItem(),
ListItem(),
],
);
});
}
You can get inspired by my case which depends on the number of AlbumRow dynamically. If the height reaches the maximum, you can get to the bottom by scrolling.
import 'package:flutter/material.dart';
Future<Widget> showBottomSheet(BuildContext context) {
return showModalBottomSheet<dynamic>(
useRootNavigator: true,
barrierColor: Colors.black.withOpacity(0.5),
isScrollControlled: true,
context: context,
builder: (BuildContext bc) {
return ConstrainedBox(
constraints: BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.9),
child: Container(
decoration: new BoxDecoration(
color: Colors.blue, borderRadius: new BorderRadius.only(topLeft: const Radius.circular(25.0), topRight: const Radius.circular(25.0))),
child: ListView(
shrinkWrap: true,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(30, 30, 30, 45),
child: Text(
'Choose Album',
textAlign: TextAlign.center,
),
),
AlbumRow(title: 'For Weekends arta iretnairstnaisetn aistn aisetn'),
AlbumRow(title: 'Creative'),
AlbumRow(title: 'Christmas'),
AlbumRow(title: 'For Weekends arta iretnairstnaisetn aistn aisetn'),
],
),
),
);
});
}
Use the Code Below
Note : If You are using column then use mainAxisSize: MainAxisSize.min
// make isScrollControlled : true
// if using column then make - mainAxisSize: MainAxisSize.min
showModalBottomSheet<dynamic>(
isScrollControlled: true,
context: context,
builder: (BuildContext bc) {
return YourWidget();
}
)
For changing the height of bottomsheet it's better to use the bottomsheet's constraints and isScrollControlled properties.
Like this:
showModalBottomSheet(
constraints: BoxConstraints.loose(Size(
MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height * 0.75)), // <= this is set to 3/4 of screen size.
isScrollControlled: true, // <= set to true. setting this without constrains may cause full screen bottomsheet.
context: context,
builder: (context) => yourWidget()
);
For border radius use the shape property:
showModalBottomSheet(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(45))), // <= set preferable radius.
context: context,
builder: (context) => yourWidget()
);
Use showBottomSheet instead of showModalBottomSheet
Create global key and a listener
final _scaffoldKey = new GlobalKey<ScaffoldState>();
VoidCallback _showPersBottomSheetCallBack;
Write your method to show the sheet
void _showBottomSheet() {
setState(() {
_showPersBottomSheetCallBack = null;
});
_scaffoldKey.currentState
.showBottomSheet((context) {
return new Container(
height: MediaQuery.of(context).size.height-100.0,
color: Colors.greenAccent,
child: new Center(
child: new Text("Hi BottomSheet"),
),
);
})
.closed
.whenComplete(() {
if (mounted) {
setState(() {
_showPersBottomSheetCallBack = _showBottomSheet;
});
}
});
}
initialize the listener
void initState() {
super.initState();
_showPersBottomSheetCallBack = _showBottomSheet;
}
Call the method wherever you required
new RaisedButton(
onPressed: _showPersBottomSheetCallBack,
child: new Text("Persistent"),
),
Hope it helps !
Lately I found an workaround for this. By setting the canvasColor property to Colors.transparent in your app's theme, we can make the BottomSheet's overlay disappear.
return new MaterialApp(
title: 'MyApp',
theme: new ThemeData(
primarySwatch: Colors.blue,
canvasColor: Colors.transparent,
),
//...
);
Once you set this up, you may use ClipRRect or Decoration with rounded corners.
here is the simplest code working in 2021
showModalBottomSheet(
context: context,
isScrollControlled: true, // <-- make bottom sheet resize to content height
shape: RoundedRectangleBorder( // <-- for border radius
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15.0),
topRight: Radius.circular(15.0),
),
),
builder: (BuildContext context) {
return Container() // <-- any widget you want
});
In the above code by #Shyju Madathil you need to add key in scaffold to make it work
return new Scaffold(
key: _scaffoldKey,
....
Based on Vicky's answer, Wrap could make alignments miserable. Use instead Column(mainAxisSize: MainAxisSize.min, children: [...]) in the widget. Implementing that in your example should look like:
void _showBottomSheet(BuildContext context) {
showModalBottomSheet<Null>(
context: context,
builder: (BuildContext context) {
return Column(
mainAxisAxisSize: MainAxisSize.min,
children: [
_bottomSheetScreen
]); // defined earlier on
},
);
}
If you want to control the scrolls with swipes, then try setting isScrollControlled: true on the showModalBottomSheet().
You can adjust the height by setting the height of your main container either by a constant ex : 800 or by using MediaQuery ex :
if i want to show only 2 /3 of the screen
MediaQuery.of(context).size.height -
(MediaQuery.of(context).size.height / 3)
for the radius first you have to set the
showModalBottomSheet(
backgroundColor: Colors.transparent,
and then you container color to White or any color you wanted , example :
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(16),
topRight: const Radius.circular(16))),
child:
You can adjust the height by setting isScrollControlled: true and wrapping the BottomSheet inside FractionallySizedBox. It would look something like this:
showModalBottomSheet<void>(
context: context,
//This
isScrollControlled: true,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter state) {
return FractionallySizedBox(
//Here specify the high of the BottomSheet
heightFactor: 0.9,
child:BottomSheet(
.
.
.
.
.
.
));
});
});
Simple way to do this:
showModalBottomSheet(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(15),
topLeft: Radius.circular(15),
),
),
context: context,
builder: (context) {
return Wrap(
children: [
Container(
height: 40,
child: Center(
child: Text(
"Edit Profile",
style: TextStyle(
fontWeight: FontWeight.bold,
),
),`
),
),
],
);
});
In my case, setting the 'isScrollable' parameter to "true" makes the bottomsheet go past halfway through the screen.
This is my solution.It can adjust height and has a max height.If the content over max height.It can be scrolled
showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.white,
// elevation: 10,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
)),
builder: (context) {
return ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 300),
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: List.generate(20, (index) => Text("data$index")),
)),
);
},
);