I would like to present a different view depending on MediaQuery output. Hiding/sliding drawer on smaller devices like phones and a permanent/docked drawer on larger devices like tablets.
I see that drawer is part of the Scaffold but the existing infrastructure does not allow for a permanent drawer, or at least I don't see it.
What you should do is create a different layout for tablets since the drawer will opaque de view when is it open. So create a new widget for the drawer (it will be used in both screens) then check the width of it and depending of the size, put it as the drawer parameter or inside the body, within a stack
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Scaffold(
appBar: AppBar(backgroundColor: Colors.orange),
drawer: size.width < 500 ? Drawer() : null,
body: Stack(
children: <Widget>[
if (size.width >= 500) Drawer(),
],
),
);
}
Related
I am trying to make a launcher in flutter, however I cannot figure out how to make a drawer that can be swiped up from the home screen, like in other launchers like nova or poco or many others. I understand that the app drawer closely matches a bottom sheet, but bottom sheets in flutter need to be first tapped on them and then dragged. How do I drag a widget up from anywhere on the scaffold?
To obtain the functionality you're looking for use this package: flutter slider
Example:
with code as simple as this you can obtain the slideup bottom sheet like functionality:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("SlidingUpPanelExample"),
),
body: SlidingUpPanel(
panel: Center(
child: Text("This is the sliding Widget"),
),
body: Center(
child: Text("This is the Widget behind the sliding panel"),
),
),
);
}
I am facing a problem with the listview when height is bounded, so when I change the phone font size an overflow occurs and I don't want to give extra height to the container.
Container(
height: fixed height goes here,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
some widgets goes here...
],
),
)
you can try to rely on textScaleFactor, by default it's 1.0
if you change font size on Settings page of your device this value will be changed to 1.15, 1.3 and so on (in 0.15 steps).
so you can multiply container height by this value:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
body: SafeArea(child: Home()),
),
);
}
}
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
double h = MediaQuery.of(context).textScaleFactor;
return Center(
child: Text('$h'), // with default settings it shows 1.0
);
}
}
In case your looking for the device font size (set inside the settings activity of the smartphone), you can use textScaleFactor property of the MediaQuery class.
double textScale = MediaQuery.of(context).textScaleFactor;
Alternatively, you can get the same value like this:
double textScale = MediaQuery.textScaleFactorOf(context);
As a heads up, all of the font sizes in Flutter are automatically scaled by this setting so if a user has their font scaled way up, you might hit some overflow errors. With that said, doing the same thing while you're debugging is an awesome way to find where your layout might overflow.
Check out the accessibility section of the Flutter Docs for some more info.
You need to detect screen height and give your Container() height according to tha, and keep it detecting whenever build() method being re-called.
That's how to get a responsive height for your Container()
MediaQuery() could do that, as follow :
Container(
height: MediaQuery.of(context).size.height, // screen's size.height
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
some widgets goes here...
],
),
)
Flutter says about size property :
The size of the media in logical pixels (e.g, the size of the screen).
Scrollbar widget in Flutter seems to have a fixed thickness of 6. I think the Scrollbar looks ugly, it is too thick. Also if I compare Flutter Scrollbar to scrollbars in other apps in my device, they all look different (they look better).
Can I change the Scrollbar thickness without creating a new Scrollbar widget?
I had an idea to force Scrollbar partially out of screen to make it look thinner, but I don't know how to do that.
I am using the Scrollbar with a ListView.
Just change the thickness property from scrollbar theme data:
class MyApp extends StatelessWidget {
final double _thickness = 2;
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.light().copyWith(
scrollbarTheme: ScrollbarThemeData().copyWith(
thickness: MaterialStateProperty.all(_thickness),
)
),
);
}
}
I found a relatively simple workaround which to wrap the Scrollbar in a Stack widget and add to Stack a Container which will hide part of the Scrollbar. There will be a small 'white' margin in the right edge of the screen but you will see similar margin e.g. in OS settings views (at least in Galaxy S7 settings app). Sample code (notice that this is for Android only, not tested in iOS):
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.topRight,
fit: StackFit.loose,
children: <Widget>[
Scrollbar(
child: buildListView(),
),
Container(
width: 3,
color: Theme.of(context).canvasColor,
)
],
);
}
How can I get the height of the BottomNavigationBar of a Scaffold in Flutter? I know there is MediaQuery.of(context).size to get a screen size. Is there a similar method for BottomNavigationBar?
Container(
width: MediaQuery.of(context).size.width,
height: kBottomNavigationBarHeight,
child: Scaffold(
backgroundColor: Colors.transparent,
body: null,
bottomNavigationBar: BottomNavigationBar()
)
)
This will create a Scaffold with enough room only for the BottomNavigationBar widget.
kBottomNavigationBarHeight is a constant, and can be found in the constants.dart file.
For getting size of widget you can use key field
final key = GlobalKey();
... set key field of widget
double height = key.currentContext.size.height;
If you want to know height of bottomNavigationBar in one of the children's screens of main Scaffold with bottomNavigationBar you can use MediaQuery:
final bottomPadding = MediaQuery.of(context).padding.bottom;
Bottom padding from MediaQuery, in addition of SafeArea, takes into account bottomNavigationBar's height.
More detailed:
#override
Widget build(BuildContext context) {
final bottomPadding = MediaQuery.of(context).padding.bottom; // From here you will get only SafeArea padding.
return Scaffold(
body: PageView(
children: const [
// But in build() method of each of these screens you will get
// SafeArea padding with bottomNavigationBar height
// just by calling MediaQuery.of(context).padding.bottom;
FirstScreen(),
SecondScreen(),
ThirdScreen(),
FourthScreen(),
],
),
bottomNavigationBar: MyBottomNavigationBar(),
);
}
I tried and for android i used kBottomNavigationBarHeight and for ios i think the height is 90 pixel.. so i declared the height in my constant file such as double btmNavigationBarHeight = Platform.isAndroid ? kBottomNavigationBarHeight : 90;
According to dosc: https://api.flutter.dev/flutter/material/NavigationBar/height.html
If null, NavigationBarThemeData.height is used. If that is also null, the default is 80
I am trying to understand the SafeArea widget in Flutter.
SafeArea code added to Flutter Gallery app here in github show top:false and bottom:false everywhere. Why do these need to be set false in these cases?
SafeArea is basically a glorified Padding widget. If you wrap another widget with SafeArea, it adds any necessary padding needed to keep your widget from being blocked by the system status bar, notches, holes, rounded corners, and other "creative" features by manufacturers.
If you are using a Scaffold with an AppBar, the appropriate spacing will be calculated at the top of the screen without needing to wrap the Scaffold in a SafeArea and the status bar background will be affected by the AppBar color (Red in this example).
If you wrap the Scaffold in a SafeArea, then the status bar area will have a black background rather than be influenced by the AppBar.
Here is an example without SafeArea set:
Align(
alignment: Alignment.topLeft, // and bottomLeft
child: Text('My Widget: ...'),
)
And again with the widget wrapped in a SafeArea widget:
Align(
alignment: Alignment.topLeft, // and bottomLeft
child: SafeArea(
child: Text('My Widget: ...'),
),
)
You can set a minimum padding for edges not affected by notches and such:
SafeArea(
minimum: const EdgeInsets.all(16.0),
child: Text('My Widget: ...'),
)
You can also turn off the safe area insets for any side:
SafeArea(
left: false,
top: false,
right: false,
bottom: false,
child: Text('My Widget: ...'),
)
Setting them all to false would be the same as not using SafeArea. The default for all sides is true. Most of the time you will not need to use these settings, but I can imagine a situation where you have a widget that fills the whole screen. You want the top to not be blocked by anything, but you don't care about the bottom. In that case, you would just set bottom: false but leave the other sides to their default true values.
SafeArea(
bottom: false,
child: myWidgetThatFillsTheScreen,
)
Supplemental code
In case you want to play around more with this, here is main.dart:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: BodyWidget(),
),
);
}
}
class BodyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.topLeft,
child: SafeArea(
left: true,
top: true,
right: true,
bottom: true,
minimum: const EdgeInsets.all(16.0),
child: Text(
'My Widget: This is my widget. It has some content that I don\'t want '
'blocked by certain manufacturers who add notches, holes, and round corners.'),
),
);
}
}
When you wrap a widget A in a safe area, you are asking to the framework "Please, keep my widget A away from the device's UI navigation and notches".
The arguments 'top, bottom, right and left' are used to tell to the framework if you want him to avoid the device's intrusions from that sides specifically.
For example: if you put your widget A inside a safe area in the top of the screen and sets the "top" argument to false, it will be cropped by the iPhone's X and Pixel 3's notches.
SafeArea is a widget that sets its child by enough padding to avoid intrusions by the operating system and improve the user interface.
import 'package:flutter/material.dart';
class SafeArea extends StatefulWidget {
#override
_SafeAreaState createState() => _SafeAreaState();
}
class _SafeAreaState extends State<SafeArea> {
#override
Widget build(BuildContext context) {
MediaQueryData mediaQueryData=MediaQuery.of(context);
double screenWidth = mediaQueryData.size.width;
var bottomPadding=mediaQueryData.padding.bottom;
return Padding(
padding: EdgeInsets.only(bottom: bottomPadding),
child: Scaffold(
body: new Container(
),
),
); }}
Without using SafeArea in iPhone 12 pro max
With using SafeArea
Code snippet using SafeArea
SafeArea(
child: Text('Your Widget'),
)