I have this code for a chat app that renders a series of messages. The thing is page is showing ok, but when trying to send a message rendering overflow appears. It's like my input is going all the way up instead of setting itself over my displayed keyboard.
This is my code for rendering the page
return Scaffold(
body: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Flexible(
child: ListView.builder(
itemBuilder: (_, index) => _messages[index],
itemCount: _messages.length,
//El reverse hace que vaya de abajo hacia arriba
reverse: true,
padding: EdgeInsets.all(6.0),
),
),
Divider(
height: 1.0,
),
Container(
child: _buildComposer(),
decoration: BoxDecoration(color: Theme.of(context).cardColor),
)
],
),
);
And here I have my code for the input where think the mistake is
Widget _buildComposer() {
return IconTheme(
data: IconThemeData(color: Theme.of(context).accentColor),
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 9.0),
child: Row(
// mainAxisSize: MainAxisSize.min,
children: <Widget>[
Flexible(
child: TextField(
controller: _textController,
onChanged: (String texto) {
setState(() {
_isWriting = texto.length > 0;
});
},
onSubmitted: _enviarMensaje,
decoration:
InputDecoration.collapsed(hintText: "Envie un mensaje!"),
),
),
Container(
margin: EdgeInsets.symmetric(horizontal: 3.0),
child: IconButton(
icon: Icon(Icons.message),
onPressed: _isWriting
? () => _enviarMensaje(_textController.text)
: null,
),
)
],
),
),
);
}
Here are my print captures for initial rendering, after clicking inside the input to write some message
Here's my log, and it's supposed to be easy to understand the approach I should take but I'm not having any luck with it.
FYI: I have already tried this approach but did not seem to work
StackOverflow answer on rendering
I have already gone through flutter.io docs but I'm not understanding the whole listview theory. If you have some insights I would appreciate them as well so I can deeply understand how it behaves.
resizeToAvoidBottomPadding: false might help.. have a look here
As #diegoveloper's comment, it will not resize which means it will not show content behind keyboard. Based on your use case you can choose option 1 or 2 in the above link
Related
I am trying to create a Draft Page Screen for a blog app ,which has a Textfield and a close icon for closing it and a Publish Text for publishing it .I have given the Publish a Close icon in a row so even if scroll down after typing the row should remain fixed at the top .But how can I make the row widget fixed at the top of the body ?
This is my code:
ListView(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
InkWell(
child: Icon(Icons.close_sharp),
onTap: () {
Navigator.pop(context);
},
),
Text("Publish"),
],
),
TextField(
autofocus: true,
decoration: new InputDecoration(
border: InputBorder.none),
keyboardType: TextInputType.multiline,
maxLines: null,
cursorColor: Color(0xff057009),
cursorHeight: 30,
),
],
),
Try this code this will do the trick.
Scaffold(
appBar: AppBar(
title: const Text('Blog View'),
),
body: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
InkWell(
child: const Icon(Icons.close_sharp),
onTap: () {
Navigator.pop(context);
},
),
const Text("Publish"),
],
),
const Expanded(
child: TextField(
autofocus: true,
decoration: InputDecoration(border: InputBorder.none),
keyboardType: TextInputType.multiline,
maxLines: null,
cursorColor: Color(0xff057009),
cursorHeight: 30,
),
),
],
),
);
Your row must be outside the list. You can make the list scrollable as well, but just make it outside the main list.
Here is an example:
Column with a preferred height, for example media query . size . height - > [
SizedBox height: the height you want to show of the row
-> SingleChildScrollView -> your Row with the possibly overflowing text
...Your other children in a scrollable view
]
I am trying to create one Grid View page with Cards as list elements and the last of the cards are getting cut from the bottom. The following are relevant code snippets:
createListBody.dart
List<String> services = [
'Demo1',
'Demo2',
'Demo3',
'Demo4',
'Demo5',
'Demo6',
'Demo7',
'Demo8',
'Demo9',
'Demo10',
];
#override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height,
child: GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 2.0,
mainAxisSpacing: 2.0,
children: services.map((String serviceName){
return Container(
child:Card(
color: Colors.grey[500],
elevation: 15,
semanticContainer: true,
shadowColor: palletFuchsia,
shape: CircleBorder(),
child: InkWell(
onTap: (){
print("Tapped "+serviceName);
},
child: Center(
child: Text(
serviceName,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25
),
),
),
),
)
);
}).toList(),
),
);
}
listScreen.dart
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: palletWhiteDrawer,
drawer: Theme(
data: Theme.of(context).copyWith(
canvasColor: palletYellowDrawer,
),
child: CreatDrawerWidget(),
),
appBar: CreateAppBar(),
body:GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ServiceListBody()
],
)
),
),
);
}
Screenshot
I am completely new to flutter so, sorry if there is some silly mistake. But any help would be useful.
Thank you for your time!
Just for exploring, you can do height: MediaQuery.of(context).size.height *2, It shouldn't get cut anymore right?
Solution 1
Your telling your container a given height which is fix for the moment, no matter how many items you got inside your ListView.
You can for example use ConstrainedBox with shrinkWrap:true and manage your max height
ConstrainedBox(
constraints: BoxConstraints(maxHeight: 200, minHeight: 56.0),
child: ListView.builder(
shrinkWrap: true,
...
more info: https://api.flutter.dev/flutter/widgets/ConstrainedBox-class.html
Solution 2
use Slivers, they are dynamic.
For this you need to customize your structure a little bit. You wrap everything with a CustomScrollView(), SliverToBoxAdapter() for fixed elements and SliverList() for dynamic ones, makes it work like a charm.
Example using CustomScrollView and SliverToBoxAdapter:
return SafeArea(
child: CustomScrollView(
//scrollDirection: Axis.vertical,
physics: BouncingScrollPhysics(),
slivers: <Widget>[
// Place sliver widgets here
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.fromLTRB(25, 30, 25, 20),
child: SizedBox(
height: 299,
child: Column(children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
...
You can define the height with the SizedBox height. Also you can apply nice effects to your whole View on the CustomScrollView
Example Sliverlist:
SliverList(
delegate: SliverChildBuilderDelegate(
(ctx, index) => ChangeNotifierProvider.value(
value: list[index],
child: GestureDetector(
onTap: () {}),
childCount: list.length,
)),
Infos about slivers: https://medium.com/flutterdevs/explore-slivers-in-flutter-d44073bffdf6
The GridView Widget itself is Scrollable , So you don't have to wrap it with a Column and SingleChildScrollView Widget...
You can now simply scroll down if the Circles goes out of the screen
If you wish all the circles to fit in the screen.. You'll have to use
var mobileHeight = MediaQuery.of(context).size.height
And then the height of each circles will be mobileHeight divided by the no. of circles vertically.
I've very much a beginner in Dart and Flutter, and my project is to learn while building an app. I've learned the basics with an online class and I have basis in Java.
Now, in my homepage, I'm trying to have 4 buttons take the whole screen. My current layout is to have them all take an equal amount of space and be stacked vertically.
Problem is, I can't find a way to make the RaisedButton fill automatically. I've tried retrieving the height of the screen and dividing that between the buttons but it's not working. Here's how I do it :
First, the homescreen layout:
class Homescreen extends StatelessWidget {
Widget build(context) {
double height = MediaQuery.of(context).size.height - 60;
return Scaffold(
body: SafeArea(
child: ListView(
children: [
PlanningButton(height),
Container(margin: EdgeInsets.only(top: 20.0)),
BookingButton(height),
Container(margin: EdgeInsets.only(top: 20.0)),
ModifyButton(height),
Container(margin: EdgeInsets.only(top: 20.0)),
CancelButton(height),
],
),
),
);
}
As you can see, I pass the height argument to my buttons all built around this model:
Widget PlanningButton(double pixel_y) {
return RaisedButton(
padding: EdgeInsets.all(pixel_y / 8.0),
onPressed: () {}, //goes to the planning screen
color: Colors.blue[200],
child: Text("Planning"),
);
}
Yet, it's never quite right. I don't think it's taking into account the notification bar or the navigation bar.
I suspect there is a workaround that involves wrapping the buttons into a container or using a different kind of widget that have button-like properties but I can't find what suits my needs.
Thanks for any help !
edit: I'm basically trying to do this
Try removing the variable height logic, and simply wrap your buttons in Expanded widgets. This is pretty much what it was built for.
Check it out here: https://api.flutter.dev/flutter/widgets/Expanded-class.html
You could use a Column and Expanded to do the job. If you want to make your Column scrollable, you can wrap it with SingleChildScrollView. I used SizedBox instead of Container to make the spacing because it is way more efficient and is designed for it.
class Homescreen extends StatelessWidget {
Widget build(context) {
double height = MediaQuery.of(context).size.height - 60;
return Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: PlanningButton(height)
),
SizedBox(height:20),
Expanded(
child: BookingButton(height),
),
SizedBox(height:20),
Expanded(
child: ModifyButton(height),
),
SizedBox(height:20),
Expanded(
child: CancelButton(height),
),
],
),
),
);
}
If 4 buttons are the only widgets, you dont need to use ListView just use a Column with mainAxisAlignment: MainAxisAlignment.spaceEvenly
class Sample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: Container(
color: Colors.red,
child: Center(
child: RaisedButton(
onPressed: () {},
child: Text("button"),
),
),
),
),
Expanded(
child: Container(
color: Colors.blue,
child: Center(
child: RaisedButton(
onPressed: () {},
child: Text("button"),
),
),
),
),
Expanded(
child: Container(
color: Colors.green,
child: Center(
child: RaisedButton(
onPressed: () {},
child: Text("button"),
),
),
),
),
Expanded(
child: Container(
color: Colors.yellow,
child: Center(
child: RaisedButton(
onPressed: () {},
child: Text("button"),
),
),
),
),
],
),
),
);
}
}
Before you read this, it's not the best way to do things but it's one I'm using at least for prototyping. You may find using a container with a list and the argument MainAxisAlignment to work better for you.
A lot of comments here might give a solution that works for you but here's mine. I got it to work like I wanted by using SizedBox.
First, I recovered the width of the screen, then I created the buttons and put them into SizedBox fitting the screen's width, I put them in a Column and I added a few Container for the style points.
Result:
Code :
class Homescreen extends StatelessWidget {
Widget build(context) {
double width = MediaQuery.of(context).size.width; //getting screen width
return Scaffold(
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(margin: EdgeInsets.only(bottom: 20.0)),
PlanningButton(width),
Container(margin: EdgeInsets.only(bottom: 20.0)),
BookingButton(width),
Container(margin: EdgeInsets.only(bottom: 20.0)),
ModifyButton(width),
Container(margin: EdgeInsets.only(bottom: 20.0)),
CancelButton(width),
Container(margin: EdgeInsets.only(bottom: 20.0)),
],
),
),
);
}
Widget PlanningButton(double width) {
return Expanded(
child: SizedBox(
width: width - 20,
child: RaisedButton(
onPressed: () {},
color: Colors.blue,
child: Text(
"planning",
textScaleFactor: 4.0,
),
),
//fit: BoxFit.cover,
),
);
}
The user can tap anywhere on the colored buttons and it will register. Now I'll add images and transparency to make it look good!
appBar: new AppBar(
leading: Stack(
children: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_forward),
onPressed: () {
_nextPage(1);
},
tooltip: 'Next',
padding: EdgeInsets.only(left: 360.0),
),
IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
_nextPage(-1);
},
tooltip: 'Previous',
),
],
),
),
Blockquote
The two IconButtons , the first one doesn't work but the second does , when you remove the padding it works fine , How should i do this ? , and using Containers wouldn't help as much because they take space too , so what to do ?
You have many errors there .
First
You are using a Stack, stack will put your widgets over the other, so you have to specify the position using Positioned or Align.
Second
If you check the source code , you'll find there is a width limit for the leading Widget.
if (leading != null) {
leading = new ConstrainedBox(
constraints: const BoxConstraints.tightFor(width: _kLeadingWidth),
child: leading,
);
}
where _kLeadingWidth = 56
1st Solution
Replace your Stack widget by Row widget, if you do this, you'll get an overflow exception because the size of your two IconButtons exceed the width > 56.
Final Solution (you can find more)
Remove your IconButton and use an Icon wrapped by InkWell (in order to receive the tap)
return Scaffold(
appBar: new AppBar(
leading: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
InkWell(
onTap: () => print("1"),
child: Padding(
padding: EdgeInsets.all(2.0),
child: const Icon(Icons.arrow_forward))),
InkWell(
onTap: () => print("2"),
child: Padding(
padding: EdgeInsets.all(2.0),
child: const Icon(Icons.arrow_back))),
],
),
),
);
I'm writing my first app with flutter and came onto the problem that an InkWell does not detect when it's tapped. It's a more complicated structure than I could find examples for. I wrote the following function:
class ChatOverviewElements {
static Widget buildChatEntry(BuildContext context, String username,
String lastMessageText,
String lastMessageDate, bool group,
int unread, int chatID) {
return new Material(
child: new Ink(
padding: const EdgeInsets.only(right: 32.0),
child: new InkWell(
onTap: ChatOverviewNavigation.openChat(context, chatID),
child: new Row(
children: <Widget>[
new Container(
padding: const EdgeInsets.all(10.0),
child: new Icon(
Icons.account_circle,
size: 60.0,
),
),
new Expanded (
child: new Container(
padding: const EdgeInsets.only(right: 5.0),
height: 60.0,
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Row(
children: <Widget>[
group ? new Icon(Icons.group) : new Container(),
new Container(width: group ? 10.0 : 0.0,),
TextStyles.username(username),
],
),
TextStyles.chatSnippet(lastMessageText),
],
),
),
),
new Column(
children: <Widget>[
new Row(
children: <Widget>[
new Icon(
Icons.done_all,
color: Colors.green,
),
new Container(width: 8.0,),
TextStyles.chatDate(lastMessageDate),
]),
new Icon(
Icons.thumb_up,
color: Colors.grey[500],
)
],
),
],
),
),
),
);
}
}
It creates a box like you'd see in WhatsApp etc. with username, message preview, unread badge (the thumb up icon is a placeholder for that) and so on. What I want it for that outer Container to be tappable, so I changed it so and Ink (to see the ripple) and added and InkWell and everything else as a child of that well. That does not work. It doesn't detect the tap a all and I don't understand why.
The build function is called to create the children of a ListView, which itself is nested inside a Scaffold and a Material App. That shouldn't matter though, I tried simple examples from StackOverflow within the ListView and those work just fine.
So the question is: what am I doing wrong here? Where would that InkWell have to be so the whole container is tapable and a ripple is shown on the container background ?
onTap: ChatOverviewNavigation.openChat(context, chatID),
should be
onTap: () => ChatOverviewNavigation.openChat(context, chatID),
otherwise the result of ChatOverviewNavigation.openChat(context, chatID) will be used as onTap handler.