I've been dealing with a strange situation in Flutter that I just can't figure out.
The problem is that when I attempt to use any kind of TextField in any kind of Scrollable Widget, when I tap the TextField for the keyboard to come up, the keyboard pushes the TextField off of the screen. Everything in the Scaffold is just blank.
I can't exactly say for sure what is happening but sometimes it seems as if the keyboard pushes the content of the scrollable view up past the view but other times it seems that there just seems to be a giant white box attached to the top of the keyboard that covers the context. I've done several experiments but I can't pin the exact behavior down.
To be clear, I've tried using SingleChildScrollView and a ListView. The behavior is the same.
I've read through the whole of this thread and tried the workarounds with no success:
https://github.com/flutter/flutter/issues/10826
I've also tried using this workaround:
https://gist.github.com/collinjackson/50172e3547e959cba77e2938f2fe5ff5
However, I am just not sure that I am having the exact same issue as those threads.
Here is a code snippet that demonstrates the problem and some screenshots. Am I just doing something blatantly wrong?
class MakeEntryView extends StatefulWidget {
#override
State<StatefulWidget> createState() => new MakeEntryState();
}
class MakeEntryState extends State<MakeEntryView> {
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new SingleChildScrollView(
child: new Container(
child: new Column(
children: <Widget>[
new TextField(),
new TextField(),
new TextField(),
new TextField(),
new TextField(),
new TextField(),
new TextField(),
],
),
),
)
);
}
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
}
Before pressing Text Field
After Pressing Text Field
The Scaffold has a properly to deal with this:
Scaffold(
resizeToAvoidBottomPadding: false,
There are rare cases where it doesn't work but that should take care of it.
Related
The application first displays the With text: Entered text screen. And there is a button when clicked on which the user gets to another screen where he needs to enter text. It is necessary for me that when the user has entered the text, when returning back to the first screen, this text is displayed. How can this be done?
My code:
import 'package:flutter/material.dart';
class TextScreen extends StatefulWidget {
const TextScreen({Key? key}) : super(key: key);
#override
State<TextScreen> createState() => _TextScreenState();
}
class _TextScreenState extends State<TextScreen> {
final textController = TextEditingController();
#override
void dispose() {
textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Enter data'),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: textController,
decoration: InputDecoration(labelText: 'Message'),
),
const SizedBox(
height: 20,
),
],
)),
);
}
}
You can add a result when navigating back
Navigator.of(context).pop("test");
And the you can use the result in the previos screen
Navigator.push(
context,
MaterialPageRoute(builder: (context) => TextScreen()),
).then((result) => { ... });
There are several ways to do this. The main question is if you have a specific submit action, such as a button, to confirm using the new text or if the newly entered text should always be used, also when the user uses the back button or back swipe gesture to go back to the previous screen.
Only through submit action
If you want to consider the back button as a 'cancel' operation, and only want to update the value with a specific button, you can return the new value with the Navigator: Navigator.of(context).pop(updatedValue). Where the page was pushed, you can await this result: final updatedValue = await Navigator.of(context).push(editPageRoute);
If you always want the value to update
In this case, you'll need to update this new text in a component that lives above the Navigator which is likely provided by your MaterialApp (or other WidgetApp).
To do so, you can wrap the Navigator in another widget by using the builder function of the MaterialApp. This widget can be obtained through the widget tree and is available in both pages. You can use InheritedWidget or provider to obtain this widget.
You could also keep a component outside of your widget tree that holds this text. Using get_it might be a solution for that, but riverpod would probably allow you to do so as well.
I am having problem with Flutter List View scrolls speed. Need to get the scroll speed whenever user scrolls list faster or slower. I Want to check the List view scroll speed(Velocity), If user scroll a list faster or slower using scroll controller then it will notify user that scroll speed.
You can try ScrollVelocityListener from this lib
class ExampleApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ScrollVelocityListener(
onVelocity: (velocity) {
// Velocity is in pixels per millisecond
},
child: ListView(
children: [
Container(),
Container(),
Container(),
Container(),
],
),
);
}
}
Here is my problem.
I would like that by dragging progressively (with my finger for example) up a container (Container) that this one moves following the movement but that a new page (NewPage) towards which I want to navigate is supperposed on the container while following the movement until a certain level when I release, that the page takes all the screen. (Let's say I want to do a Navigator.push but in this sense).
Thank you.
class NewPage extends StatelessWidget {
...
#override
Widget build(BuildContext context) {
return Scaffold(...);
}
}
...
class _MainPageState extends State<MainPage> {
...
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
...
GestureDetector(
child: Positioned(
...
child: Container()
)
)
]
)
);
}
}
Your question is a bit unclear. But try these different navigation techniques.
Hero Animations
PageView - Try this with scrollDirection: Axis.vertical
I want to use MediaQuery to build widgets based on screen height and width. The problem, also referenced in #26004, is that I only want to query the size data once, for example in initState. MediaQuery documentation states
Querying the current media using MediaQuery.of will cause your widget to rebuild automatically whenever the MediaQueryData changes (e.g., if the user rotates their device).
, but that causes unnecessary rebuilds in my application. Specifically, it causes rebuild of widgets if there are changes to insets or padding (such as when keyboard is displayed).
Is there an alternative to MediaQuery which wouldn't cause rebuilds when MediaQueryData changes?
I had this issue as well and initially thought that the MediaQuery is causing unnecessary rebuilds, but if you think about it you do want the widgets to rebuild (in cases of device rotation, keyboard popup) for the app to have a responsive design.
You could do something like this:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Builder(builder: (context) {
ResponsiveApp.setMq(context);
return MyHomePage(title: 'Flutter Demo Home Page');
}),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Flex(
direction:
ResponsiveApp().mq.size.width > ResponsiveApp().mq.size.height
? Axis.horizontal
: Axis.vertical,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class ResponsiveApp {
static MediaQueryData _mediaQueryData;
MediaQueryData get mq => _mediaQueryData;
static void setMq(BuildContext context) {
_mediaQueryData = MediaQuery.of(context);
}
}
I set the mediaQueryData at the beginning with ResponsiveApp.setMq(context) and I used the Builder because you can only use the MediaQuery one context below the MaterialApp widget. After the _mediaQueryData is set you can get it whenever you want to build widgets based on the screen size.
In this code I just change the Axis direction when the device is rotated and the widget needs to rebuild to show the changed direction.
You could also have something like :
if (_mediaQueryData.size.shortestSide < 400)
//phone layout
else if(_mediaQueryData.size.shortestSide >= 400 && _mediaQueryData.size.shortestSide < 600)
//tablet layout
else
//web layout
and resizing the window in web will cause the widgets to rebuild multiple times and display the desired layout.
But if you don't want to use MediaQuery at all, you can check the Window class from dart:ui.
LayoutBuilder seems preferable over every use of MediaQuery for sizing a viewport (either the whole screen, or the space left in a column or other layout).
LayoutBuilder also works hard to avoid rebuilding its child if the size doesn't change and the parents haven't had to re-layout.
The builder function is called in the following situations:
The first time the widget is laid out.
When the parent widget passes different layout constraints.
When the parent widget updates this widget.
When the dependencies that the builder function subscribes to change.
The builder function is not called during layout if the parent passes
the same constraints repeatedly.
And you don't have to think about "the height of the appbar" ever again, because you're getting the space left, not the total space on the screen.
Check it out: https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html
In my case, the problem was happening because I was controlling the focus manually using:
onEditingComplete: () {
FocusScope.of(context).nextFocus();
}
The context used was the Parent's context, and it was causing the rebuilding. Not sure why it happened, but it stopped once I've wrap the TextFormField with a Builder and started using its context instead.
Note: I'm also using MediaQuery.of(context).size.height normally (without the rebuild side effect) to set the Widget's Parent height 🤔
I'm still trying to understand how to structure widgets. I have placed a container in the body already so how can I now add another row. I've removed some code to simplify my situation but hopefully this gives an idea of how my project is structured at the moment.
class AddButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.bottomCenter,
//Stats Button
child: Row(
), //container
//How can I enter a new row here <------- WHERE I WANT TO ENTER A ROW
);
}
}
The short answer is, you cannot. You can take advantage of the children property of a Column, the most common layout widget in all of Flutter. Flutter works on a system of nested widgets, you cannot have many parents as it all starts with one widget.
class AddButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.bottomCenter,
//Stats Button
child: Column(
children: <Widget>[
Row(
children: <Widget>[
// nested widgets
],
),
],
),
),
);
}
}
Judging by your class name, you just want a button. Not every widget starts with a Scaffold, that's only if you want an entire layout with an app bar or a bottom navigation bar. For simple widgets like a button, you can get rid of Scaffold entirely and just use MaterialButton like this.
class AddButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialButton(
onPressed: () {}, //empty function
);
}
}
More reading:
https://flutter.dev/docs/development/ui/layout
https://pusher.com/tutorials/flutter-building-layouts
https://medium.com/flutter-community/flutter-layout-cheat-sheet-5363348d037e