Pin a view to bottom of device - dart

I have a view in a stack and I would like it to stay pinned to the bottom of the phone when it is turned to landscape. Currently i have the following:
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: detectOrientationForAppBar(),
backgroundColor: Colors.black,
key: _scaffoldKey,
body: new Column(
children: <Widget>[
new Expanded(
child: new Stack(
children: <Widget>[
new Container(
height: double.infinity,
width: double.infinity,
child: new Padding(
padding: const EdgeInsets.all(1.0),
child: new Center(
child: _cameraPreviewWidget(),
),
),
),
_captureControlRowWidget()
],
),
),
],
),
);
}
within this i would like the _captureControlRowWidget to stay fixed to the bottom of the device even when it is rotated. Here is my _captureControlRowWidget:
Widget _captureControlRowWidget() {
return new Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
new Container(
color: Colors.black,
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new IconButton(
icon: const Icon(
Icons.camera_alt,
color: Colors.white,
),
color: Colors.white,
onPressed: controller != null &&
controller.value.isInitialized &&
!controller.value.isRecordingVideo
? onTakePictureButtonPressed
: null,
),
//showSwapCameraButton(),
new IconButton(
icon: new Icon(
Icons.videocam,
color: isRecording ? Colors.red : Colors.white,
),
color: isRecording ? Colors.red : Colors.white,
onPressed: controller != null &&
controller.value.isInitialized &&
!controller.value.isRecordingVideo
? onVideoRecordButtonPressed
: onStopButtonPressed,
)
],
),
),
],
);
}
Ive tried a few things like using the CrossAxis and MainAxis Alignments but it always sticks to the edge of the phone that is lowest. e.g if im holding the phone in porttrait the widget is at the bottom, if i rotate the phone 90 degrees in either direction the widget should be on the right side of the phone.

At the moment, your main body is a Column, so, as expected your control widget is below your camera preview. In landscape mode you want your control widget to be to the right of the camera preview, which means you want your main body to be a Row.
You can achieve this by changing the top Column to a Flex (which is a switch-selectable Row/Column).
#override
Widget build(BuildContext context) {
bool landscape = MediaQuery.of(context).orientation == Orientation.landscape;
return new Scaffold(
appBar: detectOrientationForAppBar(),
backgroundColor: Colors.black,
key: _scaffoldKey,
body: new Flex(
direction: landscape ? Axis.horizontal : Axis.vertical,
You will have to make the same change in your _captureControlRowWidget, making its Columns and Rows into orientation sensitive Flexes.

Related

how to create a custom container with three individual buttons inside

I am trying to create a custom widget that is a rounded rectangle containing 3 iconButtons, used for navigation. but, as far as I have seen, iconButtons cannot be used outside of material widgets, and i don't know how to wrap them in a widget that doesn't mess up my UI.
just a container with iconButtons throws "no material widget found, iconButton widgets require material widget"
trying to wrap with a material widget, i get positional arguments, messing up my UI
i have tried wrapping my container in other widgets to no avail.
here is a piece of my code, just one of the icons in the container. the code repeats twice with different icon and onPressed before closing the widget.
i really would like my UI to look how i planned, and for these buttons to work.
#override
Widget build(BuildContext context) {
return Container(
height: 50.0,
width: 200.0,
// color: Colors.grey[800],
decoration: new BoxDecoration(
color: Colors.grey[800],
borderRadius: new BorderRadius.all( Radius.circular(50.0)),
),
child: Stack(
children: <Widget>[
Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new Container(
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
IconButton(
icon: Icon(Icons.menu),
color: Colors.white,
onPressed: () {
print('test');
},
) // IconButton
], // <Widget>[]
) //Column
), // Container
Wrap your code in Scaffold and it will work.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
height: 50.0,
width: 200.0,
decoration: BoxDecoration(
color: Colors.grey[800],
borderRadius: new BorderRadius.all(Radius.circular(50.0)),
),
child: Stack(
children: [
Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
IconButton(
icon: Icon(Icons.menu),
color: Colors.white,
onPressed: () {
print('test');
},
)
],
),
)
],
),
)
],
),
),
); // IconButton ], // [] ) //Column ), // Container
}

Expanded widget inside Column not expanded, instead it's got invisible

I am trying to expand widget inside of Column widget but not able to make it expended.
When giving constant height to parent widget, the layout will be rendered as expected. But as I remove the constant height layout is not as expected as I want to make Listview with it and I should not give a constant height to the widget which will be used as listview item.
Below is my layout code.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'layout test',
home: Layout_test_class(),
));
}
class Layout_test_class extends StatelessWidget {
Widget cell() {
return Container(
color: Colors.yellow,
// height: 200, after un commenting this will work. but i want to make it without this
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Container(
color: Colors.green,
child: Text('apple z'),
),
),
Container(
color: Colors.red,
child:Text('apple 2'),
)
],
),
),
Column(
children: <Widget>[
Container(
color: Colors.black,
width: 200,
height: 200,
),
],
),
],
),
);
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('title'),
),
body: Center(
child: ListView(
children: <Widget>[
cell(),
],
)
),
);
}
}
Below is my expected output screenshot.
Try to wrap your Container with IntrinsicHeight
return IntrinsicHeight(
Container(
color: Colors.yellow
child: ...
)
)
Your ListView needs to be inside Flexible. Flexible inside Column will set maximum height available to ListView. ListView needs a finite height from parent, Flexible will provide that based on max. space available.
Column(
children: <Widget>[
Flexible(
child: ListView.builder(...)
),
Container(
color: Colors.red,
child:Text('apple 2'),
),
],
)
A nice way of doing this, it's to play with the MediaQuery, heigth and width.
Let me explain, if you want the widget to have the maximum heigth of a decide screen, you can set it like this:
Container(
height: MediaQuery.of(context).size.height // Full screen size
)
You can manipulate it by dividing by 2, 3, 400, the value you want.
The same things works for the width
Container(
width: MediaQuery.of(context).size.width // Can divide by any value you want here
)
Actually quite the opposite, if you're planning to use this as an item in a listViewyou can't let infinite size on the same axis your listView is scrolling.
Let me explain:
Currently you're not defining any height on your cell() widget, which is fine if you're using it alone. like this :
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'layout test',
home: Layout_test_class(),
));
}
class Layout_test_class extends StatelessWidget {
Widget cell() {
return Container(
color: Colors.yellow,
//height: 250, after un commenting this will work. but i want to make it without this
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Container(
color: Colors.green,
child: Text('apple z'),
),
),
Container(
color: Colors.red,
child: Text('apple 2'),
)
],
),
),
Column(
children: <Widget>[
Container(
color: Colors.black,
width: 200,
height: 200,
),
],
),
],
),
);
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('title'),
),
body: cell(),
);
}
}
But using it with a listView you have to define a height. A listView scrolls as long as it have some content to scroll. Right now it just like you're giving it infinite content so it would scroll indefinitely. Instead Flutter is not constructing it.
It's actually quite ok to define a global size for your container (as an item). You can even define a specific size for each using a parameter like this :
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'layout test',
home: Layout_test_class(),
));
}
class Layout_test_class extends StatelessWidget {
Widget cell(double height) {
return Container(
color: Colors.yellow,
height: height, //after un commenting this will work. but i want to make it without this
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Container(
color: Colors.green,
child: Text('apple z'),
),
),
Container(
color: Colors.red,
child: Text('apple 2'),
)
],
),
),
Column(
children: <Widget>[
Container(
color: Colors.black,
width: 200,
height: 200,
),
],
),
],
),
);
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('title'),
),
body: ListView(
children: <Widget>[
cell(250.0),
cell(230.0),
cell(300.0),
],
)
);
}
}

Unwanted space appearing between RaisedButton widgets in a column

tl;dr Why is space appearing between my two buttons when I have not explicitly set any?
I am trying to make a layout like the one below:
However, what appears to be about 16px of space appears between the two buttons and I cannot figure out where it is coming from.
I at first thought maybe the Column was adding space but I am using MainAxisAlignment.center which shouldn't add any. I now think that there is perhaps some Material theming going on that automatically applies padding to the RaisedButton, however I have looked through both button_theme.dart and raised_button.dart and it seemed like only the inner padding (between text and button edges) was being set. I'm sure I overlooked something and would appreciate any help in finding out why this space exists.
auth.dart (screen shown in the image)
...
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(flex: 2, child: Container()),
Expanded(
flex: 8,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.fromLTRB(0, 0, 0, 32),
child: Image(
fit: BoxFit.contain,
image: AssetImage('lib/res/drawable/logo.webp'))),
PrimaryButton(
onPressed: //...,
child: Text('Log In')),
PrimaryButton(
onPressed: //...,
child: Text('Sign Up')),
])),
Expanded(flex: 2, child: Container()),
]));
}
primary_button.dart (custom rounded button widget that extends RaisedButton):
...
Widget build(BuildContext context) {
return Theme(
data: Theme.of(context).copyWith(
textTheme: Theme.of(context).textTheme,
buttonTheme: Theme.of(context).buttonTheme.copyWith(
padding: EdgeInsets.all(0),
minWidth: double.infinity,
buttonColor: Theme.of(context).accentColor,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24))),
),
child: Builder(builder: super.build));
}
add the property materialTapTargetSize and set it to MaterialTapTargetSize.shrinkWrap.
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
if you check the source code of RawMaterialButton it adds a padding based on that value:
Size minSize;
switch (widget.materialTapTargetSize) {
case MaterialTapTargetSize.padded:
minSize = const Size(48.0, 48.0);
break;
case MaterialTapTargetSize.shrinkWrap:
minSize = Size.zero;
break;
}
return Semantics(
container: true,
button: true,
enabled: widget.enabled,
child: _InputPadding(
minSize: minSize,
child: result,
),
);

Flutter Overlapped InkWells Gesture Detection

While working on an application, we have an instance where we want a card to have an inkwell as well as a button on the card (also with an inkwell). However, I have been unable to determine a way to separate the gestures such that only the inkwell directly under the user's tap is invoked. As it is today, it appears that the tap 'bleeds through' to the next inkwell such that both splash effects are invoked. This is undesirable behavior, the application appears to be selecting the card not the invokable item on the card (note: actual application is much different but the same issue is present). I have reproduced this in a simple application to demonstrate the bleed through when the user pressed the button in the bottom right of the card. Is there something I am missing which can prevent this behavior? Thanks
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
body: Card(
color: Colors.blue,
child: InkWell(
onTap: () { },
child: Container(
height: 150.0,
child: Align(
alignment: Alignment.bottomRight,
child: RaisedButton(
color: Colors.red,
onPressed: () { },
),
),
),
),
),
);
}
}
This is the normal expected InkWell behavior as most of the time you want to use it's tap feature for every widget in it's tree. So what you can do is to define a Stack and set the button in the z-axis absolute over the InkWell:
Widget build(BuildContext context) {
return new Scaffold(
body: Center(
child: Card(
color: Colors.blue,
child: Stack(
children: <Widget>[
InkWell(
onTap: () {
print("inkwell");
},
child: Container(
height: 150.0,
),
),
RaisedButton(
color: Colors.red,
onPressed: () {
print("button");
},
),
],
),
),
),
);
}
If you would want to set the button in the bottom right corner again you can set a Row and Colum around it and assign it's alignment:
#override
Widget build(BuildContext context) {
return new Scaffold(
body: Center(
child: Container(
height: 150.0,
child: Card(
color: Colors.blue,
child: Stack(
children: <Widget>[
InkWell(
onTap: () {
print("inkwell");
},
child: Container(
height: 150.0,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
RaisedButton(
color: Colors.red,
onPressed: () {
print("button");
},
),
],
),
],
),
],
),
),
),
),
);
}
Upper code would result in seperated widgets:

Flutter align button to bottom of Drawer

I'm trying to have a Widget align to the bottom of my NavDrawer while still keeping a DrawerHeader and a list at the top of the Drawer. Here's what I'm trying:
drawer: new Drawer(
child: new Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new Text('Top'),
new Align(
alignment: FractionalOffset.bottomCenter,
child: new Text('Bottom'),
),
],
),
),
The bottom text should be aligned to the bottom of the drawer, but It isn't!
You need to wrap your Align widget in Expanded.
drawer: Drawer(
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Text('Top'),
Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: Text('Bottom'),
),
),
],
),
),
Edit:
Years on and there's a much easier solution:
return Drawer(
child: Column(
children: [
ListView(), // <-- Whatever actual content you want goes here
Spacer(), // <-- This will fill up any free-space
// Everything from here down is bottom aligned in the drawer
Divider(),
ListTile(
title: Text('Settings'),
leading: Icon(Icons.settings),
),
ListTile(
title: Text('Help and Feedback'),
leading: Icon(Icons.help),
),
]
);
A little late to the party, but here's my solution to this problem:
#override
Widget build(BuildContext context) {
return Drawer(
// column holds all the widgets in the drawer
child: Column(
children: <Widget>[
Expanded(
// ListView contains a group of widgets that scroll inside the drawer
child: ListView(
children: <Widget>[
UserAccountsDrawerHeader(),
Text('In list view'),
Text('In list view too'),
],
),
),
// This container holds the align
Container(
// This align moves the children to the bottom
child: Align(
alignment: FractionalOffset.bottomCenter,
// This container holds all the children that will be aligned
// on the bottom and should not scroll with the above ListView
child: Container(
child: Column(
children: <Widget>[
Divider(),
ListTile(
leading: Icon(Icons.settings),
title: Text('Settings')),
ListTile(
leading: Icon(Icons.help),
title: Text('Help and Feedback'))
],
)
)
)
)
],
),
);
}
This produces the below output where the UserAccountDrawerHeader and the text items can be scrolled around inside the drawer but the Divider and the two ListTiles stay static on the bottom of the drawer.
look whats the problem with your code you have added a Column as a Child to the Drawer so whatever you add in it are vertically placed and The height of Column is by default shrunk to its children's height and it gets larger as the child gets, so there's no point in adding an Align inside a Column
The Simpler Solution Would be to use an Expanded Widget that takes the remaining Space Look I have used a Column and added A widget above and below the Expanded Widget.
Drawer(
elevation: 1.5,
child: Column(children: <Widget>[
DrawerHeader(
decoration: BoxDecoration(
color: Colors.redAccent,
)),
Expanded(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
ListTile(
title: Text('My Cart'),
leading: Icon(Icons.shopping_cart),
onTap: () {},
),
ListTile(
title: Text('My Orders'),
leading: Icon(Icons.add_shopping_cart),
onTap: () {},
),
ListTile(
title: Text('Logout'),
leading: Icon(Icons.exit_to_app),
onTap: () {})
],
)),
Container(
color: Colors.black,
width: double.infinity,
height: 0.1,
),
Container(
padding: EdgeInsets.all(10),
height: 100,
child: Text("V1.0.0",style: TextStyle(fontWeight: FontWeight.bold),)),
])),
A simple approach would be to use Spacer() like:
Scaffold(
drawer: Drawer(
child: Column(
children: <Widget>[
Text('Top'),
Spacer(), // use this
Text('Bottom'),
],
),
)
)
here is my solution of a vertical Row with icons in the end of the drawer.
#override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: <Widget>[
Expanded(
child: ListView(
children: <Widget>[
DrawerHeader(
padding: const EdgeInsets.all(7),
decoration: BoxDecoration(
color: AppColors.menuHeaderColor,
),
child: buildHeader(),
),
AccountDrawerRow(),
ListTile(
leading: Icon(Icons.directions_car),
title: Text(translations.button.vehicles),
),
ListTile(
leading: Icon(Icons.calendar_today),
title: Text(translations.button.appointments,),
),
],
),
),
Container(
child: Align(
alignment: FractionalOffset.bottomCenter,
child: Container(
padding: EdgeInsets.all(15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
InkWell(
onTap: () => Navigator.of(context).push(MaterialPageRoute(
builder: (context) => SettingsPage())),
child: Icon(Icons.settings)),
Icon(Icons.help),
Icon(Icons.info),
],
),
),
),
),
],
),
);
}
I'd put it in a row and align all the stuff to bottom using crossAxisAlignment: CrossAxisAlignment.baseline
Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.baseline,
children: <Widget>[
Text(
'12.00',
style: Theme.of(context).textTheme.headline2,
textAlign: TextAlign.start,
),
Text(
'USD',
style: Theme.of(context).textTheme.bodyText2,
textAlign: TextAlign.start,
),
]),
Using Expanded widget to align widget to bottom of column parent widget
Column(
children: [
..other children
Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: Text(
'Button',
style: TextStyle(
decoration: TextDecoration.underline,
fontSize: 18,
color: Colors.black),
),
),
),
],
),

Resources