Flutter: aligning buttons edge to edge to other components - button

I found it difficult to layout buttons in a way that their visual representation is harmonic with other widgets, yet their touchable area has additional extra space for users' fingers.
Please have a look at the attached picture.
Code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Material(child: HomePage()),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: <Widget>[
Container(
color: Colors.yellow,
padding: EdgeInsets.all(16),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Text('Top Left'),
Spacer(),
Text('Top right')
],
),
Row(
children: <Widget>[
Text('Middle Left'),
Spacer(),
Text('Middle right')
],
),
Row(
children: <Widget>[
Text('Bottom Left'),
Spacer(),
Text('Bottom right')
],
),
],
),
),
Container(
color: Colors.orange,
padding: EdgeInsets.all(16),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Text('Top Left'),
Spacer(),
FlatButton(
child: Text('Top right'),
onPressed: () {},
)
],
),
Row(
children: <Widget>[
Text('Middle Left'),
Spacer(),
Text('Middle right')
],
),
Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.print),
onPressed: () {},
alignment: Alignment.bottomLeft,
),
Spacer(),
Text('Bottom right')
],
),
],
),
),
],
),
);
}
}
The "all texts" widgets are beautifully aligned. But when I add interactive widgets, it starts getting ugly.
The Flat button has the same font as Text. But it's touchable area is expanding it. I am aware of materialTapTargetSize but it shrinks the clickable area which has its inflated size to make it easy to tap it. Is there an easy way to render it exactly as the "Top right" text widget and still have the same touchable area? In that case, the touchable area should expand further out of 16px padding, because I would link the Ink to be distributed evently around the button.
IconButton is higher than text, but the problem is that it doesn't start where the texts starts (left edge). Even if I add the alignment: Alignment.centerLeft, it is slightly moved to the left but still not edge to edge with other texts. On top of that after changing this alignment it's no longer centred within its InkWell.
For now, I solve all these issues using Stack and Positioned widgets. With this approach, I can control exactly where the items are, and the InkWell keeps the content exactly in the centre. But I believe it's not a correct solution.
Appreciate your feedback.

Try this code,
For the flat button:
Container(
alignment: Alignment.centerRight,
width: 60,
child: FlatButton(
padding: EdgeInsets.all(0),
child: Text('Top right'),
onPressed: () {},
),
)
For the icon button:
IconButton(
padding: EdgeInsets.all(0),
icon: Icon(Icons.print),
onPressed: () {},
alignment: Alignment.bottomLeft,
),

Related

How to navigate to a specific route in a list full of the same widget?

I have a an app which the user is able to add in books and it upload to the firestore database, and then I use a streamBuilder() to recieve all the data and display it in a list of GestureDetector() with the card() as a child.
Here is the code for the
final bookWidget = GestureDetector(
key: ValueKey(loggedInUser.email),
onTap: () {
print(title);
},
child: Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Stack(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: 100,
height: 140,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(bookCoverURL)),
),
),
Container(
width: 230,
//color: Colors.red,
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
Text(
category.toString(),
style: TextStyle(
color: Colors.grey,
fontStyle: FontStyle.italic,
),
),
Container(
margin: EdgeInsets.only(top: 10),
child: Text(
title.toString(),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17,
),
),
),
Container(
margin: EdgeInsets.only(top: 10),
child: Text(
(author),
style:
TextStyle(color: Colors.grey),
),
),
],
),
),
Icon(
Icons.arrow_forward_ios,
color: Colors.black,
size: 15,
)
],
),
],
),
//color: Colors.yellowAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
elevation: 5,
margin: EdgeInsets.all(10),
),
);
bookWidgets.add(bookWidget);
And then I display all my code in the screen by :
Column(
children: bookWidgets,
)
But how can I make the app navigate to a new screen when the user pressed on a specific card()? Since all of them are the same widget and have no unique identifier?
Any help is greatly appreciated! Thank you!
GestureDetector(
key: ValueKey(loggedInUser.email),
onTap: () {
Navigator.pushNamed(
context,
'/routeNameHere',
arguments: id,
);
},
)
In parameter arguments: ID - you can change the id by the id of the card that you want to navigate to.
You must create a page for the display detail of the card, which just displays a card with same id in the argument. So on your new page, create a variable for receiving the argument, e.g.:
final cardId = ModalRoute.of(context).settings.arguments as String;
So the page can display the card depending on the cardId.
You will need to create an identifier to pass (in this case “book”). In Flutter, these are necessary when calling or passing an object.
However, in regards to using Named Routes , this can allow you to send the data without the identifier but is considered bad practice in coding as you would not have a reference to the call or be able to back trace through the data.
Keeping the above in mind, it is possible to use the OnTap() inside each child card container so that on handle clicks or tap gestures, the user is taken to a different screen using routes as it is part of the GestureDetector class.

Align buttons below appBar without any space in flutter [duplicate]

This question already has answers here:
Unwanted space appearing between RaisedButton widgets in a column
(1 answer)
Flutter: Remove padding in buttons - FlatButton, ElevatedButton, OutlinedButton
(11 answers)
Closed 3 years ago.
I want to align the buttons just below the appBar but I am getting this:
I want to remove the space between buttons and the appBar. How can I achieve this?
This is the code:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Home',
),
),
body: Column(
children: <Widget>[
Row(
children: <Widget>[
Column(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width*0.5,
child: RaisedButton(
onPressed: (){},
child: Text('SORT'),
),
)
],
),
Column(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width*0.5,
child: RaisedButton(
onPressed: (){},
child: Text('FILTER'),
),
)
],
)
],
),
],
),
);
}
}
Inside the RaisedButton, add this argument:
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap
Use a widget Align parent :
appBar: AppBar(
title: Align(
alignment: Alignment.center
Text(
'Home',
),
),
),

How to have a Flutter button be as wide as possible up to a maximum width?

In the following layout, I'd like the button to be able to grow to be as wide as possible on the screen, up to a maximum width. I've tried the following, which doesn't work (the button is always as wide as possible):
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextFormField(),
TextFormField(),
ConstrainedBox(
constraints: BoxConstraints(maxWidth: 200),
child: RaisedButton(child: Text("button"), onPressed: () {}),
),
],
),
),
),
),
);
}
}
To expand on the layout I'm looking for: the button must be the same width as the smaller of the following two quantities: 1) the width of the screen, 2) a given fixed maximum width.
Example scenarios:
A) the screen is 1000 pixels wide, and the given fixed maximum width is 600 pixels, then the button will be 600 pixels wide.
B) the screen is 400 pixels wide, and the given fixed maximum width is 600 pixels, then the button will be 400 pixels wide.
The solution is pretty simple, simply wrap your ConstrainedBox in Align and instead of using maxWidth use minWidth.
Align(
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: 200),
child: RaisedButton(child: Text("button"), onPressed: () {}),
),
)
Output:
If screen width > 200, button takes up 200 width
If screen width < 200, button takes up screen width
You can remove crossAxisAlignment: CrossAxisAlignment.stretch, because TextFormField is stretching anyway. This code will make the button width to be of a max width size, but no bigger than screen width:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) => MaterialApp(
home: Scaffold(
body: SafeArea(
child: Center(
child: Column(
children: [
TextFormField(),
TextFormField(),
Container(
width: 200,
child: RaisedButton(
onPressed: () {},
child: Text('button'),
),
),
Container(
width: 200,
child: RaisedButton(
onPressed: () {},
child: Text(
'Button with long text asf sadf sdf as df s df as '
'dfas dfsd f asfauhiusg isdugfisudgfi asudgk usgkdu'
'ksdfhk sudfhk sudhfk',
),
),
),
],
),
),
),
),
);
}
You can put the RaisedButton in a Row:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextFormField(),
TextFormField(),
ConstrainedBox(
constraints: BoxConstraints(maxWidth: 250),
child: Row(
children: <Widget>[
Expanded(
child: RaisedButton(child: Text('test')),
),
],
),
),
],
),
),
),
),
);
}
}
The Row will create a horizontal row, and the RaisedButton widget will only take as much room as its own size.
first wrap with container and add width to infinity to be responsive:
Container(
width: double.infinity,
child: ElevatedButton(
child: Text("button"),
onPressed: (){}
),
),
you can add padding too..
known that RaisedButton is deprecated and migrated to ElevatedButton..

How do I generate a list of widgets from firebase firestore database?

I'm trying to figure out how to iterate through documents in firestore and create a text widget for each one.
I've figured out how to access those elements. I used debugPrint() and got the results I'm expecting, but I can't get it to display below my other widgets. I get a red screen with a ton of errors (on phone).Below is my code for what I've tried so far.
QuerySnapshot querySnapshot = await
Firestore.instance.collection("users").document(user.uid).collection("trails").getDocuments();
var list = querySnapshot.documents;
list.forEach((doc) => debugPrint(doc.data["Trail Name"].toString()));//works
final topContentText = Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
//these widgets are generating correctly
Text(
"First Name: $firstName",
style: TextStyle(color: Colors.white, ),
),
Text(
"Last Name: $lastName",
textAlign: TextAlign.left,
style: TextStyle(color: Colors.white,),
),
Text(
"Email: $email",
style: TextStyle(color: Colors.white, ),
),
//these ones are causing my errors.
list.forEach((doc) => Text(
"Trail Name: ${doc.data["Trail Name"].toString()}",
style: TextStyle(color: Colors.white, ),
),)
],
);
final topContent = Stack(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height * 0.40,
padding: EdgeInsets.all(40.0),
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(color: Color.fromRGBO(58, 66, 86, .9)),
child: Center(
child: topContentText,
),
),
Positioned(
left: 8.0,
top: 60.0,
child: InkWell(
onTap: () {
Navigator.pop(context);
},
child: Icon(Icons.arrow_back, color: Colors.white),
),
)
],
);
return Scaffold(
body: Column(
children: <Widget>[topContent, bottomContent],
),
);
The screen on my device lights up red with errors on creating child widgets, when I'm expecting it to display the Trail Name(s) below the other info. I only included necessary code, but could include more (such as the widget's build method) if needed. Thanks for any assistance. I'm new to flutter.
Try using the map function:
List<Widget> _widgets = list.map((doc) => Text(
"Trail Name: ${doc.data["Trail Name"].toString()}",
style: TextStyle(color: Colors.white, ),
),).toList();
And then for the children of the column, just add that list to the list you specify, like:
children: [ ... ] + _widgets;

overflowing RenderFlex flutter

I am trying to make a seemingly easy page with flutter.
It contains of totally five rows where row 1 & 2, 3 & 4 belongs together, and the last row is its own.
Row 1: Centered text
Row 2: 8 icon buttons
Row 3: Centered text
Row 4: 5 checkboxes
Row 5: Text with a following icon button
The problem I get is the size:
I/flutter (22610):◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢
A RenderFlex overflowed by 248 pixels on the right.
I have tried to make the code in different classes, according to their belonging, but then I get this error.
When I tried to put the code in containers, the iconButtons and checkBoxes quit working. I have read a lot of questions about similar problems here on Stackoverflow and googled around about it, but I'm still stuck.
class MyIcon extends StatefulWidget {
#override
_MyIconState createState() => _MyIconState();
}
class _MyIconState extends State<MyIcon> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.red,
// body: Container(
// height: 180.0,
// color: Colors.lightBlueAccent,
body: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 50.0),
),
Text(
'FIRST TEXT',
textDirection: TextDirection.ltr,
style: TextStyle(
fontSize: 25.0,
color: Colors.white,
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
// Padding(padding: EdgeInsets.only(left: 3.0, right: 10.0)),
_IconButtons(
headImageAssetPath: 'assets/ico.png',
onPressed: () {},
),
_IconButtons(
headImageAssetPath: 'assets/ico.png',
onPressed: () {},
),
_IconButtons(
headImageAssetPath: 'assets/ico.png',
onPressed: () {},
),
_IconButtons(
headImageAssetPath: 'assets/ico.png',
onPressed: () {},
),
_IconButtons(
headImageAssetPath: 'assets/ico.png',
onPressed: () {},
),
_IconButtons(
headImageAssetPath: 'assets/ico.png',
onPressed: () {},
),
_IconButtons(
headImageAssetPath: 'assets/ico.png',
onPressed: () {},
),
_IconButtons(
headImageAssetPath: 'assets/ico.png',
onPressed: () {},
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"SECOND TEXT'",
style: TextStyle(fontSize: 25.0, color: Colors.white),
),
],
),
MyCheckBox(),
],
),
// ),
);
}
}
class _IconButtons extends StatelessWidget {
final iconSize = 60.0;
final String headImageAssetPath;
final onPressed;
_IconButtons({this.headImageAssetPath, this.onPressed});
#override
Widget build(BuildContext context) {
return IconButton(
iconSize: iconSize,
icon: Image.asset(headImageAssetPath),
onPressed: () {});
}
}
class MyCheckBox extends StatefulWidget {
#override
_MyCheckBoxState createState() => _MyCheckBoxState();
}
class _MyCheckBoxState extends State<MyCheckBox> {
#override
Widget build(BuildContext context) {
return Expanded(
child: Column(
children: <Widget>[
Expanded(
child: Row(
children: <Widget>[
Text(
"ANOTHER TEXT",
textDirection: TextDirection.ltr,
style: TextStyle(
fontSize: 25.0,
color: Colors.blueGrey,
),
),
],
),
),
],
),
);
}
}
This is one of many tries. If you want I can send the code for the checkboxes too. (I'm new to flutter so I'm sorry if my code is bad).
Any help is appreciated. Thank you.
In the row 2, every button icon width valued 60, then the Row container will width valued 60 * 8 = 480 at least, so the error occurs.
I have two solutions for you:
If you want to keep the button size width valued 60, you can replace Row with Wrap, the button will start a new row when it meet the edge of the screen.
If you want the 8 buttons placed in one single row, you can wrap the IconButton with Expanded
return Expanded(child:IconButton(
iconSize: iconSize,
icon: Image.asset(headImageAssetPath),
onPressed: () {}
));

Resources