I have a TextField widget inside a Container and want to be able to tap on the TextField without the tap propagating to the parent (the Container). I also want to unfocus the TextField when tapping outside it. To achieve that I have wrapped the Container with an AbsorbPointer widget inside a GestureDetector. The problem is that this makes all taps go to the Container and I lose the ability to interact with the TextField.
How to stop event propagation from the TextField to the Container?
Edit: Futter docs says that TextField widget has a function for tapping outside:
onTapOutside → TapRegionCallback?
Called for each tap that occurs outside of the TextFieldTapRegion group when the text field is focused.
This property does not show among the available properties on the TextField in my app; Flutter 3.3.10 • channel stable
Code looks something like this:
class Filters extends StatelessWidget {
const Filters({super.key});
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 12),
child: OutlinedButton.icon(
.
.
onPressed: () => {
showDialog(
context: context,
builder: (_) {
return Dialog(
child: SingleChildScrollView(
child: GestureDetector(
onTap: () => log("parent"),
child: AbsorbPointer(
child: Container(
.
.
child: Column(
children: [
.
.
TextField(
controller: textEditingController,
focusNode: focusNode,
onTap: () {
log("textField..............");
},
.
.
),
],
),
),
),
),
),
);
},
),
},
),
);
}
}
Related
Why my bottomsheet is disappearing upon opening keyboard while clicking textfeild? I tried many solutions but none of them is working properly.
I have nothing much to add just trying to complete the words criteria.
"It looks like your post is mostly code; please add some more details".
Container(
padding: EdgeInsets.symmetric(
horizontal: deviceSize.width * 0.1),
child: InkWell(
onTap: () {
showAddressBottomSheet(context);
},
child: ...// Button style
),
void showAddressBottomSheet(BuildContext _context) {
showModalBottomSheet(
backgroundColor: Colors.transparent,
context: _context,
isScrollControlled: true,
elevation: 20,
builder: (context) {
return AddAddressView(
callback: (val) => setState(() => _addrSelectedTitle = val),
);
},
);
}
Actual Buttom sheet
class AddAddressView extends StatefulWidget {
#override
_AddAddressViewState createState() => _AddAddressViewState();
}
class _AddAddressViewState extends State<AddAddressView> {
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
padding: EdgeInsets.all(30),
child: ListView(
shrinkWrap: true,
children: <Widget>[
Text(......),
TextField(
autofocus: true,
style: TextStyle(fontSize:15),
controller: widget.controller,
..........
),
),
),
),
},
}
I have seen this issue before and since you have posted a reproduction of your actual code, I'm assuming this padding value exists somewhere in your widget tree
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
In older versions of flutter, this padding was required for the bottom sheet to move up when the keyboard is in view. They have however been fixed and you don't need to apply the padding anymore and the framework will handle it. Otherwise there will be twice as much padding as seen in your case.
I am new to Flutter. The thing I want is to keep focus on TextField, but not display keyboard. Is it possible?
To give focus to a text field as soon as it’s visible, use the autofocus property.
content_copy
TextField(
autofocus: true,
);
_dismissKeyboard(BuildContext context) {
FocusScope.of(context).requestFocus(new FocusNode());
}
#override
Widget build(BuildContext context) {
return new GestureDetector(
onTap: () {
this._dismissKeyboard(context);
},
child: new Container(
color: Colors.white,
child: new Column(
children: <Widget>[/*...*/],
),
),
);
}
Both of these components should be used together to implement what you are trying to acheive.
I need to scroll page when textfield is focused.
So I used scroll_to_index plugin(https://pub.dev/packages/scroll_to_index).
But It operate well only when keyboard is already opened.
If I tap the textfield when keyboard is not opened, it doesn't scroll the page.
I think it is because page is scrolled before the keyboard is opened.
In my opinion, the problem seems to arise because the code works this way.
1.tap a textfield (linked Focusnode has Focus)
2. It try to scroll the page to the target. But because now keyboard is not opened yet, target is already in view. So It looks like nothing happened.
3. Keyboard is opened, and hide the content.
auto_scroll.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
class MyCustomScrollView extends StatefulWidget {
#override
_MyCustomScrollViewState createState() => _MyCustomScrollViewState();
}
class _MyCustomScrollViewState extends State<MyCustomScrollView> {
AutoScrollController _autoScrollController = new AutoScrollController();
List<FocusNode> nodes = List<FocusNode>.generate(3, (index) => FocusNode());
Future _scrollToIndex(int index) async {
await _autoScrollController.scrollToIndex(index,
preferPosition: AutoScrollPosition.end);
}
#override
void initState() {
super.initState();
for(var i =0;i<3;i++) {
nodes[i].addListener(() {
if(nodes[i].hasFocus) _scrollToIndex(i);
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
body: CustomScrollView(
controller: _autoScrollController,
slivers: [
SliverToBoxAdapter(
child: Container(
child: Column(
children: [
SizedBox(height: 100),
CupertinoTextField(
focusNode: nodes[0],
onEditingComplete: (){
FocusScope.of(context).requestFocus(nodes[1]);
},
),
AutoScrollTag(
key: ValueKey(0),
controller: _autoScrollController,
index: 0,
child: Container(height: 300, color : Colors.green)
),
CupertinoTextField(
focusNode: nodes[1],
onEditingComplete: (){
FocusScope.of(context).requestFocus(nodes[2]);
},
),
AutoScrollTag(
key: ValueKey(1),
controller: _autoScrollController,
index: 1,
child: Container(
height: 300,
color : Colors.green,
child: Center(
child: Text("Here should be visible!!!!"),
),
)
),
CupertinoTextField(
focusNode: nodes[2],
),
AutoScrollTag(
key: ValueKey(2),
controller: _autoScrollController,
index: 2,
child: Container(height: 300, color : Colors.green)
),
],
),
)
)
],
),
);
}
}
This is the code. When I tap on the second textfield, it should scroll to show all the red containers below the second textfield, but it doesn't.
But when the keyboard is up, clicking on the second text field works as desired.
What I expected when I tap 2nd Textfield
What actually happened
I know that just giving focus to textfield is enought to scroll to the textfield. But in my application I have a Positioned bar just above the keyboard, so I have a situation where the Positioned bar covers the text field. This is why I use scroll_to_index.
So what I want to do is wait for the keyboard to come up, and then when the keyboard comes up, run the _scrollToIndex function. How can I wait until keyboard is opened? or there are any good solution for this problem? Thank you for your reading.
You can use flutter_keyboard_visibility. There are 2 ways of implementation: with provider and with listener.
It's written here That
/// The scaffold will expand to fill the available space. That usually
/// means that it will occupy its entire window or device screen. When
/// the device's keyboard appears the Scaffold's ancestor [MediaQuery]
/// widget's [MediaQueryData.viewInsets] changes and the Scaffold will
/// be rebuilt. By default the scaffold's [body] is resized to make
/// room for the keyboard.
According to this if there is a TextField at the bottom, the Scaffold will resize itself and it does happen. But when I put a TextField inside a modalBottomSheet it doesn't get pushed up by the keyboard. The Keyboard overlaps the modalBottomSheet (with the TextField). If the Scaffold itself gets resized how modalBottomSheet stays at its place? And resizeToAvoidBottomInsethas no effect on modalBottomSheet.
Here is the sample code.
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
showModalBottomSheet(
context: context, builder: (context) => ShowSheet());
},
),
);
}
}
class ShowSheet extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 200,
child: TextField(
autofocus: true,
),
);
}
}
I apologize if this question is dumb but I didn't understand this.
I still don't know the reason may be because modalBottomSheet is using PopupRoute so it's a different route not sure. Anyway, here I found the solution I just needed to put some bottom viewInsets padding.
Widget build(BuildContext context) {
return Padding(
padding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Container(
color: Colors.blue,
height: 200,
child: TextField(
autofocus: true,
),
),
);
}
Also I needed to set isScrollControlled: true, of showModalBottomSheet()
The widgets in my ReorderableListView are essentially TextFields. When long pressing on a widget, after the time when the long press should cause the widget to "hover," instead the TextField receives focus. How can I make the drag & drop effect take precedence over the TextField? I would still like a normal tap to activate the TextField.
The code below demonstrates my issue.
I also tried to use this unofficial flutter_reorderable_list package. (To test this one, replace the Text widget on this line of the example code with a TextField.)
I'm willing to use any ugly hacks to get this working, including modifying the Flutter source code!
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final children = List<Widget>();
for (var i = 0; i < 5; i++) {
children.add(Container(
color: Colors.pink, // Only the pink area activates drag & drop
key: Key("$i"),
height: 50.0,
child: Container(
color: Colors.grey,
margin: EdgeInsets.only(left: 50),
child: TextField(),
),
));
}
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: ReorderableListView(
children: children,
onReorder: (oldIndex, newIndex) => null,
),
),
),
);
}
}
You need to do multiple things in there to fix this.
First disable the default handler in ReorderableListView by setting buildDefaultDragHandles: false in its properties.
Wrap you child widget inside ReorderableDragStartListener widget like this
ReorderableDragStartListener(
index: i,
child: Container(
color: Colors.grey,
margin: EdgeInsets.only(left: 50),
child: TextFormField(initialValue: "Child $i", ),
),
),
Then inside this ReorderableDragStartListener wrap your child in InkWell and AbsorbPointer. Then use FocusNode to focus inner TextField on single tap.
Like this
InkWell(
onTap: () => _focusNode.requestFocus(),
onLongPress: () {
print("long pressed");
},
child: AbsorbPointer(
child: TextFormField(initialValue: "Child $i", focusNode: _focusNode,),
),
),
You need to create multiple FocusNode for all the items in list. You can do this by using List or by simpling creating a new FocusNode inside the loop.
Complete code example here https://dartpad.dev/?id=e75b493dae1287757c5e1d77a0dc73f1