In my Flutter App, I have a Screen with a PageView that shows Cards. Some of the Cards have TextFields, so I put the Screen into a SingleChildScrollView, so that the Keyboard pushes the screen to the top and does not overlap. Here is an example of the code:
class DummyScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: PageView(
physics: NeverScrollableScrollPhysics(),
children: [
DummyCard(),
],
),
),
);
}
}
Now, some of the cards have a lot of TextFields and on smaller screens it would be too large. So I also put them into a ScrollView. This means I have a widget that contains a ScrollView, inside a PageView, inside a ScrollView:
(This is not the exact code, the styling parts are missing, obviously)
class DummyCard extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 14, vertical: 14),
child: Scrollbar(
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
decoration: InputDecoration(hintText: "TextField 1"),
),
SizedBox(
height: 10,
),
TextFormField(
decoration: InputDecoration(hintText: "TextField 2"),
),
SizedBox(
height: 10,
),
TextFormField(
decoration: InputDecoration(hintText: "TextField 3"),
),
SizedBox(
height: 10,
),
TextFormField(
decoration: InputDecoration(hintText: "TextField 4"),
),
SizedBox(
height: 10,
),
TextFormField(
decoration: InputDecoration(hintText: "TextField 5"),
),
],
),
),
),
),
);
}
}
The PageView should not be scrollable, but should change the page on a button tap. So that is no problem. When The Keyboard is not opened, it is not a problem either, because the outer ScrollView is not scrollable, when the keyboard is not opened. The Problem occurs when I tap on a TextField and the Keyboard pops up. Now I have a scrollable ScrollView on the outside (the whole screen), and a scrollable ScrollView inside the Card, which leads to the following two problems:
The scrolling behavior is not good in general. When I tap the screen inside the Card, I can only scroll the inner ScrollView, even if I have already scrolled "to the end". So when I want to scroll the outer ScrollView, I have to tap the screen outside of the Card, which is not a very nice user experience.
When I tap a TextField, I want the screen to be pushed to the top. But as the TextFields are inside the inner ScrollView, it only pushes the inner ScrollView to the top, which often leads to the keyboard still overlapping the TextField.
I know that it is not a good practice to have 2 ScrollViews with the same scroll direction nested in each other. But it is the only solution that came to my mind. I definitely need the ScrollView inside the card. And I also want to push the screen to the top when the keyboard pops up, I don't want to shrink the screen (not possible with my layout). So, any ideas on how I can fix the above 2 problems?
Scaffold has a property which will prevent resizing the page on keyboard open, which is true by default. Try to set it to false
bool? resizeToAvoidBottomInset
Related
I have a Flutter Web application where I need to show a widget on the right side of a ListView when I click an item and this widget should always be visible on screen. I can achieve my objective puting both on a Row and using a scrollable only for the ListView, but that requires the ListView to be wrapped by a widget with defined height.
Defining a container with height to wrap the ListView breaks the responsiveness when I resize the browser, as the container doesn't fit the height of the screen.
I thought of using the shrinkWrap property of the ListView so I don't have to wrap it in a widget with predefined height, but that makes the whole Row scrollable vertically, eventually causing the widget to leave the viewport.
I would appreciate if somebody knows how could I keep this right side widget fixed on screen so I can achieve my objective without losing responsiveness.
Here's something similitar to what I've got so far:
class PageLayout extends StatefulWidget {
const PageLayout({Key? key, required this.items}) : super(key: key);
final List<String> items;
#override
State<PageLayout> createState() => _PageLayoutState();
}
class _PageLayoutState extends State<PageLayout> {
final rightSideWidget = Container(
decoration: BoxDecoration(
color: Colors.red,
border: Border.all(color: Colors.white, width: 2),
),
height: 200);
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.49,
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (context, index) => Container(
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(color: Colors.white, width: 2),
),
height: 200,
child: Center(
child: Text(
widget.items[index],
style: const TextStyle(color: Colors.white),
),
),
),
itemCount: widget.items.length,
),
),
Expanded(child: rightSideWidget),
],
),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
I want rightSideWidget to be always centered on screen or follow the scroll.
You can divide your screen into two sections, right section and left section; thereby being able to control behaviour of widgets in both sections.
Divide the overall screen into 2 proportional sections using a Row
widget
Put this Row widget inside a Container with height equal to screen height for preserving responsiveness | Use MediaQuery to get current height of page
Now left hand section can individually scroll, and on click of any option from this section you can define behaviour for right section; while keeping the left section constant throughout page lifecycle
I am trying to align a button at bottom of a scrollable screen. For scrolling I used SingleChildScrollView and inside which I added all the widgets. Now the only issue I face is the button is not sticking at the bottom on scroll. Help would be appreciated.
As you can see in the image, the buy now button and add to cart button aligned at bottom. If we scroll the entire page, the button will not be scrollable, instead it will be sticking at bottom. I want to implement in this way.
You can use an stack and put All the widgets except button as its First child and then the button as another child of stack. Below it the example code -
class MyNewApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
alignment: Alignment.center,
children:[
Container(
height:MediaQuery.of(context).size.height,
width:MediaQuery.of(context).size.width,
child:SingleChildScrollView(
child: Column(
// Provide Your Widget here
),
),
),
Positioned(
bottom:0,
child:Align(
alignment:Alignment.center,
child: RaisedButton(
child:Text("Button"),
onPressed:(){},
),
),
),
],
),
);
}
}
I have a dropdown box and a listview below it on a screen.
On selection of a item in dropdownbox, the list view gets populated with multiple cards. As such the logic is working, but somehow having issues with the scroll configuration.
I have the main parent widget like this:
Widget build(BuildContext context) {
return Column(
children: <Widget>[locationDropdown(), showPackages(plansLoading)],
);
}
The locationDropDown() function returns a card widget with dropdown in it.
showPackages(plansLoading) returns a ListView widget whose code is like
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
return Card(
color: Colors.transparent,
child: Column(
children: <Widget>[
///...
],
),
);
)
Requirement is to keep the card in the loationDropdown() widget fixed at its place and the cards generated in showPackages's listview be scrolled.
This is the output I am getting, I am unable to scroll.
This functionality is not working. Where am I going wrong?
you can used SafeArea Widget or use Expanded widget before Listview.
ex.
Column (
children: <Widget>[
new TextField(
decoration: new InputDecoration(
labelText: "Search something"
),
Expanded(
ListView(),
),
]
)
I have a design which contains multiple columns with multiple widgets inside.
When the user clicks inside one column the rest of the screen should grey out - except the widget inside the column which is active.
For this "grey out" effect I use modal ModalBarrier class.
How can I tell a widget to appear ABOVE the modal barrier?
(my problem is, that everything appers behind the modal barrier...)
How about this?
Stack(
children: [
new Opacity(
opacity: 0.3,
child: const ModalBarrier(dismissible: false, color: Colors.grey),
),
Center(
child: Container(
Text('Hello'),
),
),
],
)
I've got a Column indide a Scaffold, and one of the Column's children is a TextField. I'd like the Textfield to expand and take all the available space, but if there is not enough space (basically when the keyboard shows up), I want the Textfield to be 3 lines height min and the Column to be scrollable so the focus on the Textfield works as intended.
I've tried a lot of different widgets, like Expand, IntrinsicHeight, LayoutBuilder, ConstrainedBox, SingleChildScrollView...
But none of the tested combination did work as expected...
The SingleChildScrollView page did help me a lot but none of the example match my situation :
https://docs.flutter.io/flutter/widgets/SingleChildScrollView-class.html
Scaffold(
body: Column(
children: <Widget>[
Text("Some\nvery\nvery\nvery\nvery\nvery\nvery\nvery\nvery\nvery\nvery\nvery\nlong\ntext"),
RaisedButton(child: Text("Some button")),
Text("Second line"),
Expanded(
child: Padding(
//Use the expand parameter instead of padding + maxLines, once released : https://github.com/flutter/flutter/pull/27205
padding: const EdgeInsets.only(top: 10, bottom: 30),
child: TextField(
decoration: InputDecoration(
hintText: "Message...",
),
maxLength: 500,
maxLengthEnforced: true,
maxLines: 50,
),
),
),
Center(
child: RaisedButton(
child: Text('OK'),
onPressed: _send,
),
)
],
),
)
With that code the first part works fine : when not keyboard is shown, the Textfield is expanded as expected.
But when the keyboard shows up, the layout overflow (because there is no SingleChildScrollView).
EDIT : the new TextField.extends property helped a bit but didn't solved my issue...