I want to make a showModalBottomSheet with a container in Flutter. I want this container to have rounded the top borders but, when I tried this, there are a few small uncolored spaces in the corners. How can I delete them?
This is the code I used:
class OverlayWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ElevatedButton(
child: const Text('showModalBottomSheet'),
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height * 0.80,
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30.0),
topRight: Radius.circular(30.0),
),
),
child: Center(...),
);
},
);
},
);
}
}
The resultant widget is the following:
I want to delete the white spaces in the top border.
Thank you in advance
Reviewing the documentation I realize that the showModalBottomSheet function has a property called 'backgroundColor' 🤦♀️
Just adding:
backgroundColor: Colors.transparent,
to the showModalBottomSheet it works.
Thanks for the help anyway!
ThemeData has bottomSheetTheme parameter, you can override this as following :
MaterialApp(
...
theme: ThemeData(
...
bottomSheetTheme:
BottomSheetThemeData(backgroundColor: Colors.transparent),
),
...
),
This will give transparent color for the bottomSheet
Related
I'am trying to show webView in a showModalBottomSheet, using inAppWebView plugin for webView. So when the webView state changes bottomSheet should adjust its height. Currently I just made a hotFix by giving scroll to bottomSheet.
return return showModalBottomSheet(
enableDrag: false,
isDismissible: false,
isScrollControlled: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(20.r),
topLeft: Radius.circular(20.r),
),
),
context: context,
builder: (context) => BottomSheet(),
);
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height - (kToolbarHeight * 2),
decoration: BoxDecoration(
color: lmWhite,
borderRadius: BorderRadius.circular(20.r),
),
clipBehavior: Clip.hardEdge,
child: Stack(
children: [
InAppWebView(),
Opacity(),
],
),
);
}
Default height for bottomSheet is half the screenSize
If you want your bottomSheet to Expand according to your content 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.
I have the following list tile
Container buildContactTile(BuildContext context, Color backgroundColor, Color foregroundColor, UserModel contact,
ContactListController controller) {
final ThemeData theme = Theme.of(context);
return Container(
decoration: BoxDecoration(color: backgroundColor, borderRadius: const BorderRadius.all(Radius.circular(5.0))),
child: ListTile(
leading: ProfileAvatar(
backgroundColor: foregroundColor,
photoURL: contact.photoURL,
radius: 15.0,
fallback: ProfileAvatar.nameAvatar(displayName: contact.displayName),
),
title: Text(contact.displayName, style: theme.textTheme.bodyText1.apply(color: foregroundColor)),
trailing: _interactive ? Icon(Icons.add, color: foregroundColor) : null,
onTap: () => _interactive ? controller.updateSelectedContacts(contact) : null,
),
);
}
And I wrap it with a container to give it a different background color depending on the state of the widget. But what happens is that my splash does not account for the circular border radius so the splash color ends up appearing in the corners of the list tile. How can I make the splash color transparant, or make it account for the rounded corners?
When you press on the ListTile and hold you see the following in the corners
Wrap the entire Container in Theme and set ThemeData's splashColor and highlightColor to Colors.transparent.
Reference How to disable default Widget splash effect in Flutter?
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Theme(
data: ThemeData(
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
),
child: Container(
decoration: BoxDecoration(
color: Colors.blueGrey,
borderRadius: const BorderRadius.all(Radius.circular(15.0)),
),
child: ListTile(
leading: const Icon(Icons.flight_land),
title: const Text("Trix's airplane"),
subtitle: const Text('The airplane is only in Act II.'),
enabled: true,
onTap: () {
print('Something');
},
),
),
);
}
}
I am able to make bottomSheet to full height by using showModalBottomSheet(...) and set the property isScrollControlled:true.
However, the bottom sheet is over the status bar, and that is not what I expect.
Is it possible to make it below the status bar?
as an option you can modify bottom sheet
1. create new file custom_bottom_sheet.dart
2. copy-paste all code from bottom_sheet class into your custom class
3. modify buildPage() method like this
#override
Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
final BottomSheetThemeData sheetTheme = theme?.bottomSheetTheme ?? Theme.of(context).bottomSheetTheme;
Widget bottomSheet = SafeArea(
child: _ModalBottomSheet<T>(
route: this,
backgroundColor: backgroundColor ?? sheetTheme?.modalBackgroundColor ?? sheetTheme?.backgroundColor,
elevation: elevation ?? sheetTheme?.modalElevation ?? sheetTheme?.elevation,
shape: shape,
clipBehavior: clipBehavior,
isScrollControlled: isScrollControlled,
enableDrag: enableDrag,
),
);
if (theme != null) bottomSheet = Theme(data: theme, child: bottomSheet);
return bottomSheet;
}
use your class
import 'package:flutter/material.dart';
import 'custom_bottom_sheet.dart' as bs;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(child: Page()),
),
);
}
}
class Page extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text('show'),
onPressed: () {
bs.showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => Container(color: Colors.red),
);
},
),
);
}
}
here is modified bottom_sheet class https://pastebin.com/5U7fsqCw
also I think you need to add property
barrierColor: Colors.white.withOpacity(0),
to prevent status bar dimming
No need to create a new class. Simply assign a variable to the padding on the build of your view and use it when opening the dialog.
class MyHomeScreenState extends State<MyHomeScreen> {
double? topPadding;
#override
Widget build(BuildContext context) {
topPadding = MediaQuery.of(context).padding.top;
//...
}
showModalBottomSheet(
context: context,
useRootNavigator: true,
isScrollControlled: true,
builder: (context) {
return Container(
height: MediaQuery.of(context).size.height -
topPadding!,
color: Colors.green,
);
});
If you look at SafeArea source code this is exactly what is happening. Make sure that your build is at a 'root' level on the widget tree since descendant widgets might not have a top padding because they are not underneath the edges.
Since 20 Jul 2022, you can set useSafeArea parameter to true to show modal under status bar, you can find details here.
showModalBottomSheet<void>(
useSafeArea: true, // <<<<< use this
// ....
);
Note: Now it available on master channel only, and later will be
available on stable channel.
Another easy workaround with rounded borders
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => Container(
height: MediaQuery.of(context).size.height * 0.965,
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(16.0),
topRight: const Radius.circular(16.0),
),
),
child: Center(
child: Text("Your content goes here"),
),
),
);
I think this solution is the most suitable for now: (use FractionallySizedBox)
showModalBottomSheet(
context: context,
enableDrag: true,
isScrollControlled: true,
builder: (context) => FractionallySizedBox(
heightFactor: 0.9,
child: Container()
)
);
You can use the constraints parameter and set the height you want
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(35),
topRight: Radius.circular(35),
),
),
constraints: BoxConstraints(maxHeight:
MediaQuery.of(context).size.height - 50),
builder: (context) {
//continue coding..
}
The best and the easiest way is to subtract the total height of device from status bar height. You can get statusBarHeight by MediaQuery.of(context).viewPadding.top and whole device height by MediaQuery.of(context).size.height
Just like #Roddy R's answer but a little simpler
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
builder: (ctx) => Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
child: YourCustomBottomSheet(),
),
),
Thanks Roddy!
i want to be able to draw partial round-rect borders around a child widget: only the left, top, and right sides; only the left, bottom and right sides; and so on. this function comes tantalizingly close to doing what i want:
Widget roundRectBorderTop(Widget child, Color color, double cornerRadius,
[double borderWidth = 1.0])
{
final side = BorderSide(color:color, width:borderWidth);
final bord = Border(left:side, top:side, right:side);
final radi = BorderRadius.circular(cornerRadius);
final data = BoxDecoration(border:bord, borderRadius:radi);
return DecoratedBox(child:child, decoration:data);
}
alas, when i run this, it dies with the following assertion:
flutter: The following assertion was thrown during paint():
flutter: A borderRadius can only be given for uniform borders.
flutter: 'package:flutter/src/painting/box_border.dart':
flutter: Failed assertion: line 510 pos 12: 'borderRadius == null'
if i remove the borderRadius: parameter from the BoxDecoration object, then the code "works," but it draws right-angle corners, rather than round-rect corners, which is what i want.
does anybody know how to fix this?
You could simplify by having BorderRadius.only for decoration of a Container;
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0),
)
),
child: ...
)
Full working code for the screenshot below;
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,
),
home: Scaffold(
appBar: AppBar(
title: Text('Rounded Corners Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0),
),
),
padding: EdgeInsets.all(20.0),
child: Text('Rounded Corners'),
),
],
),
),
),
);
}
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("..."),
),
body: Container(
color: Colors.green,
child: OutlineButton(
onPressed: () { },
color: Colors.orange,
highlightColor: Colors.pink,
child: Container(
color: Colors.yellow,
child: Text("A"),
),
shape: CircleBorder(),
),
),
);
}
}
The above code gives a transparent button. How can I get an orange OutlineButton?
OutlineButton has been deprecated and has been replaced with OutlinedButton. (Notice the "d".)
OutlinedButton's documentation helped me understand how to use it. Here's an example with these states: Disabled (Grey) --> Normal (Blue) --> Pressed (Red)
return Container(
width: double.infinity,
height: 48,
child: OutlinedButton(
child: Text(
"This is an Outline\"d\"Button. (Not OutlineButton)",
style: TextStyle(color: Colors.white),
),
onPressed: () => print("Tapped"),
//onPressed: null, //Uncomment this statement to check disabled state.
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith<Color>((states) {
if (states.contains(MaterialState.disabled)) {
return Colors.grey[100];
}
return Colors.blue;
}),
overlayColor: MaterialStateProperty.resolveWith<Color>((states) {
if (states.contains(MaterialState.pressed)) {
return Colors.red;
}
return Colors.transparent;
}),
side: MaterialStateProperty.resolveWith((states) {
Color _borderColor;
if (states.contains(MaterialState.disabled)) {
_borderColor = Colors.greenAccent;
} else if (states.contains(MaterialState.pressed)) {
_borderColor = Colors.yellow;
} else {
_borderColor = Colors.pinkAccent;
}
return BorderSide(color: _borderColor, width: 5);
}),
shape: MaterialStateProperty.resolveWith<OutlinedBorder>((_) {
return RoundedRectangleBorder(borderRadius: BorderRadius.circular(16));
}),
),
),
);
Flutter replaced the former 3 button types (FlatButton, RaisedButton and OutlineButton) with new buttons types (TextButton, ElevatedButton and OutlinedButton) to remain in sync with Material design and also because using MaterialStateProperty provides the ultimate flexibility to achieve whatever state-specific-UI one needs. You can read more about it here.
To modify the backgroundColor of a OutlineButton you can use a DecoratedBox and a Theme widget. At the end of this answer you'll find a quick example.
Anyway I'd still recommend simply using the FlatButton with its color attribute instead.
Wrap your OutlinedButton inside a DecoratedBox. Set the shape of your DecoratedBox to the same shape your OutlinedButton. Now you can use the color attribute of your DecoratedBox to change the color. The result will still have a small padding around the OutlinedButton. To remove this you can wrap the DecoratedBox inside a Theme in which you adjust the ButtonTheme. Inside the ButtonTheme you want to set materialTapTargetSize: MaterialTapTargetSize.shrinkWrap.
The padding is added inside Flutter, to increase the tap area around the button to a minimum size of 48x48 (source). Setting materialTapTargetSize to MaterialTapTargetSize.shrinkWrap removes this minimum size.
FlatButton example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: FlatButton(
color: Colors.pinkAccent,
shape: CircleBorder(),
onPressed: () => {},
child: Text('A'),
),
),
),
);
}
}
OutlinedButton example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: MyButton(),
),
),
);
}
}
class MyButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DecoratedBox(
decoration:
ShapeDecoration(shape: CircleBorder(), color: Colors.pinkAccent),
child: Theme(
data: Theme.of(context).copyWith(
buttonTheme: ButtonTheme.of(context).copyWith(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap)),
child: OutlineButton(
shape: CircleBorder(),
child: Text('A'),
onPressed: () => {},
),
),
);
}
}
It is pretty much easy, just use
backgroundColor: MaterialStateProperty.all(Colors.colorname),
If you need to Change Colour of Outline Border
OutlineButton(
borderSide: BorderSide(color: kPrimaryColor, width: 1),)
Here's way that you can check the background color of the button.
Remove hightlightColor, and try give some value to highlightElevation property of OutlineButton, then press it, you could see initially it loads orange color.