I am trying to create a simple To Do App in in flutter with a Floating Action Button in the bottom which when clicked show an Alert Dialog to add items to the list.
Every time I click on the button, the Keyboard pushes the Action Button upward causing overflowing error.
Is there any way to avoid pushing the action button upward when Keyboard is opened?
Here is the snapshot I took:
Snapshot
Below the source code:
import 'package:flutter/material.dart';
import '../model/todo_item.dart';
class ToDoScreen extends StatefulWidget {
#override
_ToDoScreenState createState() => _ToDoScreenState();
}
class _ToDoScreenState extends State<ToDoScreen> {
TextEditingController _textEditingController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blueAccent,
body: Column(
children: <Widget>[ToDoItem("Going for a Walk", "12 January, 2019")],
),
floatingActionButton: FloatingActionButton(
tooltip: 'Add Item',
child: Icon(Icons.add),
backgroundColor: Colors.red,
onPressed: _showFormDialog,
),
);
}
void _showFormDialog() {
var alert = AlertDialog(
content: Row(
children: <Widget>[
Expanded(
child: TextField(
controller: _textEditingController,
autofocus: true,
decoration: InputDecoration(
labelText: "Item",
hintText: "eg. Buy Vegetables",
icon: Icon(Icons.note_add)),
),
)
],
),
actions: <Widget>[
FlatButton(
onPressed: () {
// _handleSubmit(_textEditingController.text);
_textEditingController.clear();
},
child: Text("Save ToDo"),
),
FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Cancel"),
)
],
);
showDialog(context: context, builder: (BuildContext context) => alert);
}
}
I had the same issue, where my Floating Action Button would get pushed up.
I solved this using the property:
resizeToAvoidBottomPadding: false, // fluter 1.x
resizeToAvoidBottomInset: false // fluter 2.x
On the parent Scaffold.
I tested it with your code, it solves the issue as well.
You can check if the keyboard is show up or not, and based on that create the floating button or not.
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: keyboardIsOpened ?
null ://or an empty container
FloatingActionButton(
tooltip: 'Add Item',
child: Icon(Icons.add),
backgroundColor: Colors.red,
onPressed: _showFormDialog,
),
);
}
Inside the build method you can know if the keyboard show up by using MediaQuery.of(context).viewInsets.bottom and save its value on a bool variable keyboardIsOpened like the following code.
#override
Widget build(BuildContext context) {
bool keyboardIsOpened = MediaQuery.of(context).viewInsets.bottom != 0.0;
Used MediaQuery and Visibility
Visibility(
visible: MediaQuery.of(context).viewInsets.bottom == 0.0,
child: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.chat),
),
),
When the keyboard is opened, the bottom will not be zero, which will cause fab to get invisible.
Wrap your complete code in this
new Container(
child: Column(
children: <Widget>[
Expanded(
child: SingleChildScrollView(
child: Stack(
children: <Widget>[
// Your body code
] // Widget
), // Stack
), // SingleChildScrollView
), // Expanded
Container(
alignment: Alignment.bottomCenter,
padding: EdgeInsets.all(10.0),
child :
// button code here
// to make button full width use minWidth: double.infinity,
,
), //Container
], // Widget
), // Column
), // Container
Wrapping the Floating Action Button inside a column together with my bottom navigation, then passing this as the child to my bottom navigation bar solved most of the stated concerns: Also ensure you add mainAxisSize: MainAxisSize.min,to your column.
i try this so good
Visibility(
visible: MediaQuery.of(context).viewInsets.bottom == 0.0,
child: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.chat),
),
),
Related
I'm working on a flutter app. For now, I don't have much, but my question is, how I can add a card to a list (ListView) by clicking on a plus icon in the appBar? Code:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Todo'),
centerTitle: true,
actions: <Widget>[
IconButton(
icon: Icon(Icons.add),
tooltip: 'Add a todo card',
onPressed: () {`
},
),
],
),
That is not the whole class, but the things you need to see.
I have my appBar and in it my IconButton.
That is how one of my card looks, it's a placeholder:
children: <Widget>[
Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
leading: Icon(Icons.album),
title: Text('The Enchanted Nightingale'),
subtitle:
Text('Music by Julie Gable. Lyrics by Sidney Stein.'),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
TextButton(
child: const Text('BUY TICKETS'),
onPressed: () {},
),
const SizedBox(width: 8),
TextButton(
child: const Text('LISTEN'),
onPressed: () {},
),
const SizedBox(width: 8),
],
),
],
),
),
I want to add this kinda card (custom) to ListView (body).
Steps to achieve that:
Create a Stateless widget to extract your TodoCard as a separate widget.
Create a list of widget to hold your TodoCard widgets in the TodoScreen.
Add a single TodoCard in the initState so one will be rendered once the TodoScreen is built
When the add todo card is pressed, add a TodoCard to the list of widgets created and call setState to trigger a rebuild.
Render the list of TodoCard widgets in the ListView using the spread operator.
I added a demo using your code as an example:
THE TODOCARD WIDGET
// create a stateless widget to extract your TodoCard
class TodoCard extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
leading: Icon(Icons.album),
title: Text('The Enchanted Nightingale'),
subtitle: Text('Music by Julie Gable. Lyrics by Sidney Stein.'),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
TextButton(
child: const Text('BUY TICKETS'),
onPressed: () {},
),
const SizedBox(width: 8),
TextButton(
child: const Text('LISTEN'),
onPressed: () {},
),
const SizedBox(width: 8),
],
),
],
),
);
}
}
THE TODOSCREEN
class TodoScreen extends StatefulWidget {
#override
_TodoScreenState createState() => _TodoScreenState();
}
class _TodoScreenState extends State<TodoScreen> {
// create a list of widget to hold your TodoCard widgets
List<Widget> _allCards = [];
#override
void initState() {
super.initState();
// add a single TodoCard so one will be rendered once the page is built
_allCards.add(TodoCard());
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Todo'),
centerTitle: true,
actions: <Widget>[
IconButton(
icon: Icon(Icons.add),
tooltip: 'Add a todo card',
onPressed: () {
// when the add todo card is pressed, add a TodoCard to the list of widgets created and call setstate to trigger a rebuild
setState(() {
_allCards.add(TodoCard());
});
},
),
],
),
body: ListView(
children: [
// reder the list of TodoCard widgets using the spread operator
..._allCards,
],
),
);
}
}
I am facing a strange bug when I try to add a dynamic widget to my app. When press add button screen turns completely white I can not find why it happens.
I use https://www.youtube.com/watch?v=xPW1vtDDlt4 as resource I am really new at Flutter maybe I forget something to add bu I check many times.
Here is my code,
class DynamicWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: new TextField(
decoration: new InputDecoration(hintText: 'Press + to Add Field'),
),
);
}
}
Initialization of the list.
List<DynamicWidget> listDynamic = [];
My function to add widgets to the list.
addDynamic() {
listDynamic.add(new DynamicWidget());
print("addDynamic");
setState(() {});
}
I am not sure but problem might be here,
final testText = Visibility(
child: new Column(
children: <Widget>[
new Flexible(
child: new ListView.builder(
itemCount: listDynamic.length,
itemBuilder: (_, index) => listDynamic[index],
),
),
],
),
);
Here I call my widget which I declare it to variable here.
final body = Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
testText,
strPhoto
],
),
),
);
And finally my button.
return Scaffold(
appBar: new AppBar(title: new Text(device_type), centerTitle: true),
drawer: Menu(),
body: body,
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
heroTag: null,
child: Icon(
Icons.add,
color: Colors.white,
),
onPressed: () {
addDynamic();
},
),
],
));
Thanks for helping me.
you must create variable Widget and add to Build Context on Scaffold
In the first step, i created a Widget called CustomTextField
import 'package:flutter/material.dart';
class CustomTextField extends StatelessWidget {
final String hint;
final TextEditingController controllers;
CustomTextField(this.hint, this.controllers);
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: 2.0, left: 6.0, right: 6.0, bottom: 2.0),
child: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(hintText: hint),
controller: controllers),
SizedBox(height: 4.0),
],
),
);
}
}
this widget give me one text for display on hint and one controller for control textfield
And in the next step, I change the homepage class this way
i have list of Custom TextField (my widget) and display on listview using mapping list of build method
import 'package:flutter/material.dart';
import 'CustomTextField.dart';
class PageTutorial extends StatefulWidget {
#override
_PageTutorialState createState() => _PageTutorialState();
}
class _PageTutorialState extends State<PageTutorial> {
List<CustomTextField> widgets = [
CustomTextField("UserName", TextEditingController())
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: widgets.map<Widget>((widget) => widget).toList(),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
widgets.add(CustomTextField("Password", TextEditingController()));
});
},
child: Icon(Icons.add)),
);
}
}
have fun
I am trying to popup dialog while making http request, to accomplish this I have created function which show dialog on screen.
Here is my code.
class AppLoader {
static Future<void> showLoadingDialog(BuildContext context, GlobalKey key) async {
return showDialog(
context: context,
barrierDismissible: false,
builder: (ctx) {
return new WillPopScope(
onWillPop: () async => false,
child: SimpleDialog(
key: key,
backgroundColor: Colors.white70,
children: <Widget>[
Center(
child: Column(children: <Widget>[
CircularProgressIndicator(),
SizedBox(height: 10),
Text('Please wait...', style: TextStyle(color: Colors.black))
],),
)
],
)
);
}
);
}
}
and calling it like below:
AppLoader.showLoadingDialog(context, _keyLoader);
And output is screen as below.
As you can see, at the center there is rectangular white background shaped box is showing. I want to remove that white background.
I also tried to set backgroundColor: Colors.transparent, but it not working. It is looking more wired UI.
Just want to show loader and text of dialog.
If you want to change the background color of SimpleDialog to transparent then add a background color with elevation like below,
new SimpleDialog(
backgroundColor: Colors.transparent,
elevation: 0.1,
title: new Text('Do you like Flutter?'),
children: <Widget>[
Center(
child: Column(
children: <Widget>[
CircularProgressIndicator(),
SizedBox(height: 10),
Text('Please wait...', style: TextStyle(color: Colors.black))
],
),
)
],
);
So here's my simple use case: I want to have two buttons next to each other horizontally. In native android (which is where I come from) I would have placed them in a LinearLayout and given them weight 1 each and setting their height to wrap_content.
Now, I have placed two RaisedButton in a ButtonBar widget but where I run the app I can see the second one is getting clipped. I want them to be equally spaced and have dynamic height as per their text. How can I achieve the same in flutter? Below is what I have tried so far:
import 'package:flutter/material.dart';
class NewScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: true,
title: Text("Building layouts"),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context, false),
)),
body: myLayoutWidget(),
),
);
}
}
// replace this method with code in the examples below
Widget myLayoutWidget() {
return Container(
child: Column(
children: <Widget>[
ButtonBar(
children: <Widget>[
RaisedButton(
onPressed: () {},
child: Text("Very long text button",),
),
RaisedButton(
child: Text("Very very very very long text button"),
color: Colors.red,
onPressed: () {},
)
],
),
],
),
);
}
This is how it looks right now:
Try using Row instead of Button Bar and add buttons to Expanded parent
Widget myLayoutWidget() {
return Container(
child: Row(
children: <Widget>[
Expanded(
child: RaisedButton(
onPressed: () {},
child: Text("Very long text button",),
),
),
Expanded(
child: RaisedButton(
child: Text("Very very very very long text button"),
color: Colors.red,
onPressed: () {},
),
)
],
),
);
}
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))),
],
),
),
);