How to avoid bottom overflow when keyboard open (in alert dialog) - flutter

In this screen i'm facing overflow every time I open keyboard in the alert text field. I already tried to put SingleChildScrollVIew in all the places and it didn't resolve. This is the component which have the alertdialog:
Future<void> _showAlertDialog() async {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Type the Name', textAlign: TextAlign.center,
style: TextStyle(color: Colors.white)),
content: TextField(
controller: _nameController,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.sentences,
)
);
},
);
}
This is the build function:
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomPadding: false,
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TableCalendar(//default code),
Divider(),
_showList(),
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add, color: Colors.white,),
onPressed: () => _showCity(context)
),
);
}
The _showAlertDialog is called inside _showCity (see build > floating action button > onpressed)
_showCity:
_showCity(BuildContext context){
_showAlertDialog();
}
But after all attempts, every time I hit the textField in _showAlertDialog and it opens the keyboard, it throws an error:
A RenderFlex overflowed by 33 pixels on the bottom.
How can I handle that?
Update
_showList:
Widget _showList(){
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: _sEv.length,
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: () {
},
child: Dismissible(
child: ListTile(
leading: //loremipsum,
title: //blablabla,
subtitle: //dolorsit,
),
),
key: UniqueKey(),
direction: DismissDirection.startToEnd,
background: Container(
alignment: AlignmentDirectional.centerEnd,
color: Colors.redAccent,
child: Icon(Icons.delete_rounded),
),
onDismissed: (DismissDirection direction) {
setState(() {
//my irrelevant code
});
}
),);
},
);
}
UPDATE 2
ScreenShot:

Just wrap the body of this page in SingleChildScrollView like this.
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Scaffold(
resizeToAvoidBottomPadding: false,
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ TableCalendar(
//default code),
Divider(), _showList(),
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add, color: Colors.white,),
onPressed: () => _showCity(context)
),
),
);
}

Related

Using Form in Alertdialog

I want to create an object in body and use it in alertdialog, how should I do it? For example:
var form = Form(
);
********
return AlertDialog(
content: form,
),
The reason I want to do this is because when I try to use the buildForm structure outside the body it doesn't work. But I can't define it in the body because I want to use floating action button how can I solve it or is there a different way to solve it?
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
tooltip: "New ToDo",
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("New ToDo"),
content: Container(
child: Column(children: [
Form(
key: _formKey,
child: Column(children: [
buildForm(_controllerTitle, "Title")
],),
),
],),
),
actions: [
],
);
},
);
},
),
You can create a method buildForm that will take a TextEditingController and String
Widget buildForm(TextEditingController controller, String title) {
return Container(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(title), // or just use on input decoration
TextFormField(
controller: controller,
decoration: InputDecoration(),
),
],
),
);
}
Customize the way you want.
And can be use like
buildForm(_controllerTitle, "Title"),
class _EXTTEstState extends State<EXTTEst> {
Widget buildForm(TextEditingController controller, String title) {
return Container(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(title), // or just use on input decoration
TextFormField(
controller: controller,
decoration: InputDecoration(),
),
],
),
);
}
final _controllerTitle = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
tooltip: "New ToDo",
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("New ToDo"),
content: Container(
child: Column(
children: [
Form(
// key: _formKey,
child: Column(
children: [
buildForm(_controllerTitle, "Title"),
],
),
),
],
),
),
actions: [],
);
},
);
},
),
);
}
}

ListView doesn't separate objects from doc

I'm trying to create scrollable list of posts, but instead i got static non-scrollable bloc of strings, which is overflowing.
Example:
Oveflowing:
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: _writePost,
tooltip: 'Increment',
child: Icon(Icons.create, color: Colors.grey[300]),
),
body: SizedBox(
child: Container(
child: Column(children: [
StreamBuilder<List<Post>>(
initialData: const [],
stream: _socketStream.stream,
builder: (context, snapshot) {
if (_isLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
ListView(
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: [
...snapshot.data!.map<Widget>(
(post) => Padding(
key: ValueKey(post.id),
padding: const EdgeInsets.symmetric(vertical: 10),
child: ListTile(
title: Text(
post.content,
style: const TextStyle(fontSize: 20),
),
trailing: MaterialButton(
onPressed: () {
_deletePost(post.id);
},
child: const Icon(
Icons.delete,
size: 30,
),
),
),
),
)
],
);
},
),
]))));
}
Moreover, they all go like a single card, without separating.
Edited code, which is scrolling but doesn't separate posts
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: _writePost,
tooltip: 'Increment',
child: Icon(Icons.create, color: Colors.grey[300]),
),
body: SizedBox(
height: 500,
child:
StreamBuilder<List<Post>>(
initialData: const [],
stream: _socketStream.stream,
builder: (context, snapshot) {
if (_isLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
return Card(child: ListView(
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: [
...snapshot.data!.map<Widget>(
(post) => Padding(
key: ValueKey(post.id),
padding: const EdgeInsets.symmetric(vertical: 10),
child: ListTile(
title: Text(
post.content,
style: const TextStyle(fontSize: 20),
),
trailing: MaterialButton(
onPressed: () {
_deletePost(post.id);
},
child: const Icon(
Icons.delete,
size: 30,
),
),
),
),
)
],
) );
},
),
));
}
I'm tried to find error with documentation, but...
Column and Listview take the maximum available height. therefore, the height of Listview which here is a child of Column should be constrained. You can do so by wrapping your ListView inside Expanded:
child: Column(
children: [
Expanded(
child: ListView(
Also, if your list is long, it is not recommended to set shrinkwrap to true. Because it makes the ListView to load all its items when the layout gets built. So it can slow down performance.
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: _writePost,
tooltip: 'Increment',
child: Icon(Icons.create, color: Colors.grey[300]),
),
body: SizedBox(
height: MediaQuery.of(context).height*0.8, // add this line
child:
// Container( // do not need this
// child: // and this do not need
// Column(children: [ // and this do not need
StreamBuilder<List<Post>>(
initialData: const [],
stream: _socketStream.stream,
builder: (context, snapshot) {
if (_isLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
ListView( // change this to ListView.builder for more performance
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: [
...snapshot.data!.map<Widget>(
(post) => Padding(
key: ValueKey(post.id),
padding: const EdgeInsets.symmetric(vertical: 10),
child: ListTile(
title: Text(
post.content,
style: const TextStyle(fontSize: 20),
),
trailing: MaterialButton(
onPressed: () {
_deletePost(post.id);
},
child: const Icon(
Icons.delete,
size: 30,
),
),
),
),
)
],
);## Heading ##
},
),
// ]) // comment this
// ). // and comment this
)
);
}

Flutter: Make list scrollable

this is a typical question that might be considered as low quality but I have been on this for about two hours, and I am just trying to understand this piece of code better, so instead of just telling me how to fix, could you please also explain a bit what is happening. I am sure that for someone more experienced that me, should be very easy to spot.
I am trying to make a scrollable list, and draw each row of the list, and be able to click in each row item. But my app draws all the items but I am only able to see some of the items, as much as the screen allows, which means it is not scrollable.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Some App Page'),
),
body: ListView(
children: <Widget>[
Stack(
alignment: const Alignment(1.0, 1.0),
children: <Widget>[
TextField(
controller: cityController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(hintText: 'Enter city...'),
),
TextButton(
onPressed: () {
cityController.clear();
},
child: const Icon(Icons.clear),
),
],
),
ElevatedButton(
onPressed: () {
_futureTime = fetchTimes(int.parse(cityController.text));
if (cityController.text.isNotEmpty) {
setState(() {
cityController.clear(); // Clear value
}); // clear the textField
FocusScope.of(context)
.requestFocus(FocusNode()); // hide the keyboard
}
},
child: const Text('Get City', style: TextStyle(fontSize: 20)),
),
Column(
children: <Widget>[
Center(
child: FutureBuilder<Times>(
future: _futureTime,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const CircularProgressIndicator();
}
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return myTimeCard( date, index);
},
itemCount: data == null ? 0 : data.length,
);
},
),
),
],
),
],
),
);
}
Widget myTimeCard(String date, int index) {
return InkWell(
onTap: () {
// Navigate to the next page & pass data.
print("tapped, -> " + index.toString()); // for debugging purpose!
},
child: Stack(
children: <Widget>[
Opacity(
opacity: 1,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Text(
index.toString(),
style: const TextStyle(
color: Colors.black,
fontSize: 22.0,
fontWeight: FontWeight.bold),
),
),
],
)
],
)
],
),
);
}
You are using two ListView s nested inside each other. In such cases you may need to let the Flutter know which ListView is the primary one. So, there is a property called primary. Try to set primary to false for the inner Listview.
return ListView.builder(
primary: false,
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return myTimeCard( date, index);
},
itemCount: data == null ? 0 : data.length,
);
The code you shared does not compile because I do not have additional context, so I had to spend some time to be able to make it compile, please make sure to provide a compilable code in the future.
the problem you're facing is because the main ListView is taking control of the scroll, to see the effect try scrolling by holding the screen from the button Get City.
There are many ways to solve this problem, depending on your goal, do you want to make the whole screen scrollable, or just the data list
Way 1. Make the whole screen scrollable:
by keeping the control of the scroll in the main ListView, and making all the descending widgets non-scrollable, which in your case, by making the widget that wraps the data a Column instead of ListView:
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final TextEditingController cityController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Some App Page'),
),
body: ListView(
children: <Widget>[
Stack(
alignment: const Alignment(1.0, 1.0),
children: <Widget>[
TextField(
controller: cityController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(hintText: 'Enter city...'),
),
TextButton(
onPressed: () {
cityController.clear();
},
child: const Icon(Icons.clear),
),
],
),
ElevatedButton(
onPressed: () {
_futureTime = fetchTimes(int.parse(cityController.text));
if (cityController.text.isNotEmpty) {
setState(() {
cityController.clear(); // Clear value
}); // clear the textField
FocusScope.of(context)
.requestFocus(FocusNode()); // hide the keyboard
}
},
child: const Text('Get City', style: TextStyle(fontSize: 20)),
),
Column(
children: <Widget>[
Center(
child: FutureBuilder<Times>(
future: _futureTime,
builder: (context, snapshot) {
// if (!snapshot.hasData) {
// return const CircularProgressIndicator();
// }
final data =
// snapshot.data;
List.generate(50, (index) => index.toString());
return Column(
children: [
for (int i = 0; i < data.length; i++)
myTimeCard(data[i], i)
],
);
},
),
),
],
),
],
),
);
}
Widget myTimeCard(String date, int index) {
return InkWell(
onTap: () {
// Navigate to the next page & pass data.
print("tapped, -> " + index.toString()); // for debugging purpose!
},
child: Stack(
children: <Widget>[
Opacity(
opacity: 1,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Text(
index.toString(),
style: const TextStyle(
color: Colors.black,
fontSize: 22.0,
fontWeight: FontWeight.bold),
),
),
],
)
],
)
],
),
);
}
}
Way 2. make the non-data widgets non-scrollable, and keep the scroll control in the data widget:
can be done by converting the main ListView to a non-scrollable Widget (in your case Column), and wrapping the data list in Expanded widget, so it takes all the space it can have (for more info about Expanded):
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final TextEditingController cityController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Some App Page'),
),
body: Column(
children: <Widget>[
Stack(
alignment: const Alignment(1.0, 1.0),
children: <Widget>[
TextField(
controller: cityController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(hintText: 'Enter city...'),
),
TextButton(
onPressed: () {
cityController.clear();
},
child: const Icon(Icons.clear),
),
],
),
ElevatedButton(
onPressed: () {
_futureTime = fetchTimes(int.parse(cityController.text));
if (cityController.text.isNotEmpty) {
setState(() {
cityController.clear(); // Clear value
}); // clear the textField
FocusScope.of(context)
.requestFocus(FocusNode()); // hide the keyboard
}
},
child: const Text('Get City', style: TextStyle(fontSize: 20)),
),
FutureBuilder<Times>(
future: _futureTime,
builder: (context, snapshot) {
// if (!snapshot.hasData) {
// return const CircularProgressIndicator();
// }
final data =
// snapshot.data;
List.generate(50, (index) => index.toString());
return Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return myTimeCard(date, index);
},
itemCount: data == null ? 0 : data.length,
),
);
},
),
],
),
);
}
Widget myTimeCard(String date, int index) {
return InkWell(
onTap: () {
// Navigate to the next page & pass data.
print("tapped, -> " + index.toString()); // for debugging purpose!
},
child: Stack(
children: <Widget>[
Opacity(
opacity: 1,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Text(
index.toString(),
style: const TextStyle(
color: Colors.black,
fontSize: 22.0,
fontWeight: FontWeight.bold),
),
),
],
)
],
)
],
),
);
}
}
The issue is coming because we have two scrollable ListView. While both of them are scrollable, while scrolling when the inner ListView it gets focused and parent become unfocus and scroll event only effect on inner ListView and you can't rollback to parent ListView, A simple solution will be using NeverScrollableScrollPhysics on inner
ListView.builder.
child: ListView.builder(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
singleChildScrollView(
child: ListView.builder(
sinkwrap:true,
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,)
)
Simple and Easy

There are multiple heroes that share the same tag within a subtree without using FAB

I am trying to navigate to a route and this exception happen. Have look at my code and I don't think I have a FAB and Hero inside this route. Is it because when you tap on each List item, it will show a dialog with Gridview on it? Someone care to explain to me how can this exception happened? It doesn't produce any error on user thought, just throw an exception.
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Daftar Dokter")),
body: ListView.separated(
separatorBuilder: (BuildContext context, int i) => Divider(color: Colors.grey[400]),
itemCount: widget.data.length,
itemBuilder: (context, index) {
Doctor doctor = widget.data[index];
return InkWell(
onTap: (){
_buildDialog(context, scheduleService, doctor.doctorId);
},
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
width: 50,
height: 50,
child: Placeholder(),
),
),
Flexible(
child: SizedBox(
child: ListTile(
title: Text(doctor.name),
subtitle: Text(doctor.specializationName),
),
)
)
],
),
);
}
)
);
}
}
Hope it will solve your issue:
onItem Builder:
itemBuilder: (context, index) {
return Hero(
tag: "itemTag $index",
child: Material(
child: InkWell(
onTap: () {
// _buildDialog(context, scheduleService, doctor.doctorId);
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => Wisgets(
tag: "itemTag $index",
)));
},
child: Row(
children: <Widget>[
Text("Item $index"),
],
),
),
),
);
},
Child Widget for row
class Wisgets extends StatelessWidget {
final String tag;
const Wisgets({Key? key, required this.tag}) : super(key: key);
#override
Widget build(BuildContext context) {
return Hero(
tag: tag,
child: Scaffold(
appBar: AppBar(
title: Text("Child"),
),
body: Column(
children: [
Text("got $tag"),
],
),
),
);
}
}

How can I have my trailing widget alight to the right?

I have a ListView and I want to have a switch on the right side of every ListTile. But the switch appears to be aligned to the left. How can I make it align to the right?
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).backgroundColor,
body: Padding(
padding: EdgeInsets.all(30.0),
child: Column
(
mainAxisAlignment: MainAxisAlignment.center,
children:
[
Expanded(
flex: 1,
child: Stack
(
children: [
Align(
alignment: Alignment.topLeft,
child: NavigatePopButton(),
),
Align
(
alignment: Alignment.topCenter,
child: Text
(
'Settings',
style: Theme.of(context).textTheme.title,
overflow: TextOverflow.visible,
),
),
],
)
),
Expanded
(
flex: 4,
child: MediaQuery.removePadding
(
context: context,
removeTop: true,
child: ListView.separated
(
itemCount: 3,
separatorBuilder: (BuildContext context, int index) => Divider(height: 1.0),
itemBuilder: (BuildContext context, int index)
{
return ListTile
(
title: Text(settings()[index].value),
trailing: settings()[index].controller,
);
}
),
),
),
]
),
),
);
}
}
ListTile has a Padding of it's own. Try tweaking that value,
For example
return ListTile
(
contentPadding: EdgeInsets.only(right: 0.0),
title: Text(arguments.groups[index]),
onTap: () => onGroupTap(arguments.groups[index]),
trailing: Switch(
value: true,
onChanged: (isSwitched) => print('is switched'),
),
);
I can't replicate the issue you are facing. I see Switch aligned to the right properly. Sample working code below:
return Scaffold(
appBar: AppBar(
title: Text('Demo'),
),
body: Center(
child: ListView.separated
(
itemCount: 5,
separatorBuilder: (BuildContext context, int index) => Divider(height: 0.0),
itemBuilder: (BuildContext context, int index){
return ListTile
(
title: Text('Test'),
onTap: () {},
trailing: Switch(
value: true,
onChanged: (isSwitched) => print('is switched'),
),
);
},
),
),
);