I'm using https://pub.dev/packages/flutter_tags for a tag input. A tag is added to the array of tags by pressing submit on the keyboard and the TextInput Field cleared.
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
Use the enabled: property of the TextField widget by setting it to false : TextField( enabled: false, ... ) This field won't respond to onTap events - it is similar to a disabled field in HTML.
Next, we need to check to see if the current FocusNode has the “primary focus.” If it doesn't, we call unfocus() on the current node to remove focus and trigger the keyboard to dismiss. If you attempt to unfocus() a node that currently has the primary focus, Flutter will throw an exception.
You can copy paste run full code below
The following code use official example code and modify 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. Assuming you've a boolean condition called completed
, you can do something as follow:
if (completed) {
focus.unfocus();
} else {
focus.requestFocus(myFocusNode);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With