[英]Flutter: How to Keep TextField Focus on Submit?
I'm using https://pub.dev/packages/flutter_tags for a tag input.我正在使用https://pub.dev/packages/flutter_tags作为标签输入。 A tag is added to the array of tags by pressing submit on the keyboard and the TextInput Field cleared.
通过按键盘上的提交并清除 TextInput 字段,将标签添加到标签数组中。
Now I have the problem that with a submit the keyboard also closes.现在我遇到的问题是,提交后键盘也会关闭。 So you have to press into the field again for every tag, which is not the sense of the thing.
所以你必须为每个标签再次按下该字段,这不是事情的意义。 There are so many instructions on how to close the keyboard, but none on how to leave it open.
有很多关于如何关闭键盘的说明,但没有关于如何保持打开状态的说明。
When Submit is pressed, the field loses focus and thus the keyboard closes.按下提交时,该字段失去焦点,因此键盘关闭。 My idea was, unfortunately without success, to set a new focus on onSubmitted.
我的想法是,不幸的是没有成功,将新的焦点放在提交上。 Unfortunately nothing happens here:
不幸的是,这里什么也没发生:
TextField(
controller: myController,
focusNode: myFocusNode,
// more code
onSubmitted: (String str) {
myController.clear();
myFocusNode.requestFocus();
}
)
Kind regards,亲切的问候,
Jakob雅各布
You can copy paste run full code below您可以在下面复制粘贴运行完整代码
The following code use official example code and modify Demo2以下代码使用官方示例代码并修改Demo2
code snippet代码片段
TextField(
controller: myController,
decoration: InputDecoration(
icon: Icon(Icons.person),
labelText: "add tag with focus",
),
focusNode: myFocusNode,
onSubmitted: (val) {
setState(() {
_items.add(val);
});
myController.clear();
myFocusNode.requestFocus();
},
),
working demo工作演示
full code完整代码
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_tags/flutter_tags.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Tags Demo',
theme: ThemeData(
primarySwatch: Colors.blueGrey,
),
home: MyHomePage(title: 'Flutter Tags'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
FocusNode myFocusNode = FocusNode();
TextEditingController myController = TextEditingController();
TabController _tabController;
ScrollController _scrollViewController;
final List<String> _list = [
'0',
'SDK',
'plugin updates',
'Facebook',
'哔了狗了QP又不够了',
'Kirchhoff',
'Italy',
'France',
'Spain',
'美',
'Dart',
'SDK',
'Foo',
'Select',
'lorem ip',
'9',
'Star',
'Flutter Selectable Tags',
'1',
'Hubble',
'2',
'Input flutter tags',
'A B C',
'8',
'Android Studio developer',
'welcome to the jungle',
'Gauss',
'美术',
'互联网',
'炫舞时代',
'篝火营地',
];
bool _symmetry = false;
bool _removeButton = true;
bool _singleItem = false;
bool _startDirection = false;
bool _horizontalScroll = false;
bool _withSuggesttions = false;
int _count = 0;
int _column = 0;
double _fontSize = 14;
String _itemCombine = 'withTextBefore';
String _onPressed = '';
List _icon = [Icons.home, Icons.language, Icons.headset];
@override
void initState() {
super.initState();
_tabController = TabController(length: 2, vsync: this);
_scrollViewController = ScrollController();
_items = _list.toList();
}
List _items;
final GlobalKey<TagsState> _tagStateKey = GlobalKey<TagsState>();
@override
Widget build(BuildContext context) {
//List<Item> lst = _tagStateKey.currentState?.getAllItem; lst.forEach((f) => print(f.title));
return Scaffold(
body: NestedScrollView(
controller: _scrollViewController,
headerSliverBuilder: (BuildContext context, bool boxIsScrolled) {
return <Widget>[
SliverAppBar(
title: Text("flutter tags"),
centerTitle: true,
pinned: true,
expandedHeight: 0,
floating: true,
forceElevated: boxIsScrolled,
bottom: TabBar(
isScrollable: false,
indicatorSize: TabBarIndicatorSize.label,
labelStyle: TextStyle(fontSize: 18.0),
tabs: [
Tab(text: "Demo 1"),
Tab(text: "Demo 2"),
],
controller: _tabController,
),
)
];
},
body: TabBarView(
controller: _tabController,
children: [
CustomScrollView(
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate([
Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.grey[300], width: 0.5))),
margin:
EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: ExpansionTile(
title: Text("Settings"),
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GestureDetector(
child: Row(
children: <Widget>[
Checkbox(
value: _removeButton,
onChanged: (a) {
setState(() {
_removeButton = !_removeButton;
});
}),
Text('Remove Button')
],
),
onTap: () {
setState(() {
_removeButton = !_removeButton;
});
},
),
Padding(
padding: EdgeInsets.all(5),
),
GestureDetector(
child: Row(
children: <Widget>[
Checkbox(
value: _symmetry,
onChanged: (a) {
setState(() {
_symmetry = !_symmetry;
});
}),
Text('Symmetry')
],
),
onTap: () {
setState(() {
_symmetry = !_symmetry;
});
},
),
Padding(
padding: EdgeInsets.all(5),
),
DropdownButton(
hint: _column == 0
? Text("Not set")
: Text(_column.toString()),
items: _buildItems(),
onChanged: (a) {
setState(() {
_column = a;
});
},
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GestureDetector(
child: Row(
children: <Widget>[
Checkbox(
value: _horizontalScroll,
onChanged: (a) {
setState(() {
_horizontalScroll =
!_horizontalScroll;
});
}),
Text('Horizontal scroll')
],
),
onTap: () {
setState(() {
_horizontalScroll = !_horizontalScroll;
});
},
),
GestureDetector(
child: Row(
children: <Widget>[
Checkbox(
value: _singleItem,
onChanged: (a) {
setState(() {
_singleItem = !_singleItem;
});
}),
Text('Single Item')
],
),
onTap: () {
setState(() {
_singleItem = !_singleItem;
});
},
),
],
),
Column(
children: <Widget>[
Text('Font Size'),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Slider(
value: _fontSize,
min: 6,
max: 30,
onChanged: (a) {
setState(() {
_fontSize = (a.round()).toDouble();
});
},
),
Text(_fontSize.toString()),
Padding(
padding:
EdgeInsets.symmetric(horizontal: 20),
),
Container(
height: 30,
width: 30,
//color: Colors.blueGrey,
child: IconButton(
padding: EdgeInsets.all(0),
//color: Colors.white,
icon: Icon(Icons.add),
onPressed: () {
setState(() {
_count++;
_items.add(_count.toString());
//_items.removeAt(3); _items.removeAt(10);
});
},
),
),
Padding(
padding:
EdgeInsets.symmetric(horizontal: 5),
),
Container(
height: 30,
width: 30,
//color: Colors.grey,
child: IconButton(
padding: EdgeInsets.all(0),
//color: Colors.white,
icon: Icon(Icons.refresh),
onPressed: () {
setState(() {
_items = _list.toList();
});
},
),
),
],
),
],
),
],
),
),
Padding(
padding: EdgeInsets.all(20),
),
_tags1,
Container(
padding: EdgeInsets.all(20),
child: Column(
children: <Widget>[
Divider(
color: Colors.blueGrey,
),
Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child: Text(_onPressed),
),
],
)),
])),
],
),
CustomScrollView(
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate([
Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Colors.grey[300], width: 0.5))),
margin:
EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: ExpansionTile(
title: Text("Settings"),
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GestureDetector(
child: Row(
children: <Widget>[
Checkbox(
value: _withSuggesttions,
onChanged: (a) {
setState(() {
_withSuggesttions =
!_withSuggesttions;
});
}),
Text('Suggestions')
],
),
onTap: () {
setState(() {
_withSuggesttions = !_withSuggesttions;
});
},
),
Padding(
padding: EdgeInsets.all(20),
),
DropdownButton(
hint: Text(_itemCombine),
items: _buildItems2(),
onChanged: (val) {
setState(() {
_itemCombine = val;
});
},
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GestureDetector(
child: Row(
children: <Widget>[
Checkbox(
value: _horizontalScroll,
onChanged: (a) {
setState(() {
_horizontalScroll =
!_horizontalScroll;
});
}),
Text('Horizontal scroll')
],
),
onTap: () {
setState(() {
_horizontalScroll = !_horizontalScroll;
});
},
),
GestureDetector(
child: Row(
children: <Widget>[
Checkbox(
value: _startDirection,
onChanged: (a) {
setState(() {
_startDirection = !_startDirection;
});
}),
Text('Start Direction')
],
),
onTap: () {
setState(() {
_startDirection = !_startDirection;
});
},
),
],
),
Column(
children: <Widget>[
Text('Font Size'),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Slider(
value: _fontSize,
min: 6,
max: 30,
onChanged: (a) {
setState(() {
_fontSize = (a.round()).toDouble();
});
},
),
Text(_fontSize.toString()),
],
),
],
),
],
),
),
Padding(
padding: EdgeInsets.all(20),
),
_tags2,
Container(
padding: EdgeInsets.all(20),
child: Column(
children: <Widget>[
Divider(
color: Colors.blueGrey,
),
Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child: Text(_onPressed),
),
],
)),
TextField(
controller: myController,
decoration: InputDecoration(
icon: Icon(Icons.person),
labelText: "add tag with focus",
),
focusNode: myFocusNode,
onSubmitted: (val) {
setState(() {
_items.add(val);
});
myController.clear();
myFocusNode.requestFocus();
},
),
])),
],
),
],
)),
);
}
Widget get _tags1 {
return Tags(
key: _tagStateKey,
symmetry: _symmetry,
columns: _column,
horizontalScroll: _horizontalScroll,
//verticalDirection: VerticalDirection.up, textDirection: TextDirection.rtl,
heightHorizontalScroll: 60 * (_fontSize / 14),
itemCount: _items.length,
itemBuilder: (index) {
final item = _items[index];
return ItemTags(
key: Key(index.toString()),
index: index,
title: item,
pressEnabled: true,
activeColor: Colors.blueGrey[600],
singleItem: _singleItem,
splashColor: Colors.green,
combine: ItemTagsCombine.withTextBefore,
image: index > 0 && index < 5
? ItemTagsImage(
//image: AssetImage("img/p$index.jpg"),
child: Image.network(
"http://www.clipartpanda.com/clipart_images/user-66327738/download",
width: 16 * _fontSize / 14,
height: 16 * _fontSize / 14,
))
: (1 == 1
? ItemTagsImage(
image: NetworkImage(
"https://d32ogoqmya1dw8.cloudfront.net/images/serc/empty_user_icon_256.v2.png"),
)
: null),
icon: (item == '0' || item == '1' || item == '2')
? ItemTagsIcon(
icon: _icon[int.parse(item)],
)
: null,
removeButton: _removeButton
? ItemTagsRemoveButton(
onRemoved: () {
setState(() {
_items.removeAt(index);
});
return true;
},
)
: null,
textScaleFactor:
utf8.encode(item.substring(0, 1)).length > 2 ? 0.8 : 1,
textStyle: TextStyle(
fontSize: _fontSize,
),
onPressed: (item) => print(item),
);
},
);
}
// Position for popup menu
Offset _tapPosition;
Widget get _tags2 {
//popup Menu
final RenderBox overlay = Overlay.of(context).context?.findRenderObject();
ItemTagsCombine combine = ItemTagsCombine.onlyText;
switch (_itemCombine) {
case 'onlyText':
combine = ItemTagsCombine.onlyText;
break;
case 'onlyIcon':
combine = ItemTagsCombine.onlyIcon;
break;
case 'onlyIcon':
combine = ItemTagsCombine.onlyIcon;
break;
case 'onlyImage':
combine = ItemTagsCombine.onlyImage;
break;
case 'imageOrIconOrText':
combine = ItemTagsCombine.imageOrIconOrText;
break;
case 'withTextAfter':
combine = ItemTagsCombine.withTextAfter;
break;
case 'withTextBefore':
combine = ItemTagsCombine.withTextBefore;
break;
}
return Tags(
key: Key("2"),
symmetry: _symmetry,
columns: _column,
horizontalScroll: _horizontalScroll,
verticalDirection:
_startDirection ? VerticalDirection.up : VerticalDirection.down,
textDirection: _startDirection ? TextDirection.rtl : TextDirection.ltr,
heightHorizontalScroll: 60 * (_fontSize / 14),
textField: TagsTextField(
autofocus: false,
textStyle: TextStyle(
fontSize: _fontSize,
//height: 1
),
enabled: true,
constraintSuggestion: true,
suggestions: _withSuggesttions
? [
"One",
"two",
"android",
"Dart",
"flutter",
"test",
"tests",
"androids",
"androidsaaa",
"Test",
"suggest",
"suggestions",
"互联网",
"last",
"lest",
"炫舞时代"
]
: null,
onSubmitted: (String str) {
print("onSubmitted $str");
setState(() {
_items.add(str);
});
},
),
itemCount: _items.length,
itemBuilder: (index) {
final item = _items[index];
return GestureDetector(
child: ItemTags(
key: Key(index.toString()),
index: index,
title: item,
pressEnabled: false,
activeColor: Colors.green[400],
combine: combine,
image: index > 0 && index < 5
? ItemTagsImage(image: AssetImage("img/p$index.jpg"))
: (1 == 1
? ItemTagsImage(
image: NetworkImage(
"https://image.flaticon.com/icons/png/512/44/44948.png"))
: null),
icon: (item == '0' || item == '1' || item == '2')
? ItemTagsIcon(
icon: _icon[int.parse(item)],
)
: null,
removeButton: ItemTagsRemoveButton(
backgroundColor: Colors.green[900],
onRemoved: () {
setState(() {
_items.removeAt(index);
});
return true;
},
),
textScaleFactor:
utf8.encode(item.substring(0, 1)).length > 2 ? 0.8 : 1,
textStyle: TextStyle(
fontSize: _fontSize,
),
),
onTapDown: (details) => _tapPosition = details.globalPosition,
onLongPress: () {
showMenu(
//semanticLabel: item,
items: <PopupMenuEntry>[
PopupMenuItem(
child: Text(item, style: TextStyle(color: Colors.blueGrey)),
enabled: false,
),
PopupMenuDivider(),
PopupMenuItem(
value: 1,
child: Row(
children: <Widget>[
Icon(Icons.content_copy),
Text("Copy text"),
],
),
),
],
context: context,
position: RelativeRect.fromRect(
_tapPosition & Size(40, 40),
Offset.zero &
overlay
.size) // & RelativeRect.fromLTRB(65.0, 40.0, 0.0, 0.0),
)
.then((value) {
if (value == 1) Clipboard.setData(ClipboardData(text: item));
});
},
);
},
);
}
List<DropdownMenuItem> _buildItems() {
List<DropdownMenuItem> list = [];
int count = 19;
list.add(
DropdownMenuItem(
child: Text("Not set"),
value: 0,
),
);
for (int i = 1; i < count; i++)
list.add(
DropdownMenuItem(
child: Text(i.toString()),
value: i,
),
);
return list;
}
List<DropdownMenuItem> _buildItems2() {
List<DropdownMenuItem> list = [];
list.add(DropdownMenuItem(
child: Text("onlyText"),
value: 'onlyText',
));
list.add(DropdownMenuItem(
child: Text("onlyIcon"),
value: 'onlyIcon',
));
list.add(DropdownMenuItem(
child: Text("onlyImage"),
value: 'onlyImage',
));
list.add(DropdownMenuItem(
child: Text("imageOrIconOrText"),
value: 'imageOrIconOrText',
));
list.add(DropdownMenuItem(
child: Text("withTextBefore"),
value: 'withTextBefore',
));
list.add(DropdownMenuItem(
child: Text("withTextAfter"),
value: 'withTextAfter',
));
return list;
}
}
You need to pass myFocusNode
to requestFocus()
as long as the user needs to fill that form.只要用户需要填写该表单,您就需要将
myFocusNode
传递给requestFocus()
。 Assuming you've a boolean condition called completed
, you can do something as follow:假设您有一个名为
completed
的 boolean 条件,您可以执行以下操作:
if (completed) {
focus.unfocus();
} else {
focus.requestFocus(myFocusNode);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.