Flutter 多个电子邮件 TextField
Flutter multiple email TextField
出于学习目的,我正在尝试做一个 gmail 克隆,我在编辑新邮件时遇到了多电子邮件输入问题。我需要能够收听“ENTER”或“SPACE”,以便我可以使用这样的电子邮件“块”修改输入(验证后):
我想我可以使用“ENTER”键的 onFieldSubmitted 标签,但我如何更改输入文本?我试过了:
controller.text = Container();
但是“文本”只接受字符串(这似乎符合逻辑)。
目标是执行与您在 Whosebug 上放置“标签”的完全相同类型的 inputField。
我也找到了 EmailInputElement,但我不知道如何正确使用它,它似乎不是一个小部件。
如果有人有任何想法,将不胜感激。
这里的技巧是如下显示令人满意的 UI,InputDecoration.collapsed()
帮助我们使 TextField
最小化,您可以添加一个 Divider()
作为主栏的最后一个小部件,用于描述所有这些都在 TextField
中进行
我已经尝试模拟您想要实现的行为。
我使用 onChange
方法检查 TextField
中字符串输入末尾的空格,对于 enter
按钮我使用 onEditingComplete
函数。
我对电子邮件行进行了限制,使其只能增长到视口的特定宽度,您也可以使用不同的布局。
class Test extends StatefulWidget {
@override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
List<String> emails = [];
TextEditingController _emailController;
@override
void initState() {
super.initState();
_emailController = TextEditingController();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
child: Center(
child: Row(
children: <Widget>[
Container(
constraints: BoxConstraints(maxWidth: 200, minWidth: 0),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
...emails
.map((email) => Chip(label: Text(email)))
.toList(),
],
),
),
),
Expanded(
child: TextField(
decoration: InputDecoration.collapsed(hintText: 'EMail'),
controller: _emailController,
onChanged: (String val) {
if (val.endsWith(' '))
setState(() {
emails.add(_emailController.text);
_emailController.text = '';
});
},
onEditingComplete: () {
setState(() {
emails.add(_emailController.text);
_emailController.text = '';
});
},
),
)
],
),
)),
),
);
}
}
如果有人来这里是为了获得我要求的相同功能,这里有我的模板,您可以将其粘贴到一个新文件中,然后在需要的地方调用 EmailInput()
。您只想将 setList
属性与更新 List<String>
的功能一起使用,以将数据返回到您的父组件中:) 它还会检查输入的字符串是否是有效的电子邮件。
看起来像这样:
import 'package:flutter/material.dart';
class EmailInput extends StatefulWidget {
final Function setList;
final String hint;
final List<String> parentEmails;
const EmailInput({Key key, this.setList, this.hint, this.parentEmails}) : super(key: key);
@override
_EmailInputState createState() => _EmailInputState();
}
class _EmailInputState extends State<EmailInput> {
TextEditingController _emailController;
String lastValue = '';
List<String> emails = [];
FocusNode focus = FocusNode();
@override
void initState() {
super.initState();
_emailController = TextEditingController();
focus.addListener(() {
if (!focus.hasFocus) {
updateEmails();
}
});
}
@override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Column(
children: <Widget>[
Container(
constraints: BoxConstraints(
minWidth: 0,
),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Column(
children: <Widget>[
...emails
.map(
(email) => Chip(
avatar: CircleAvatar(
backgroundColor: Colors.black,
child: Text(
email.substring(0, 1),
style: TextStyle(color: Colors.white),
),
),
labelPadding: EdgeInsets.all(4),
backgroundColor: Color.fromARGB(255, 39, 182, 192),
label: Text(
email,
style: TextStyle(fontSize: 16, color: Colors.white),
),
onDeleted: () => {
setState(() {
emails.removeWhere((element) => email == element);
})
},
),
)
.toList(),
],
),
),
),
TextField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration.collapsed(hintText: widget.hint),
controller: _emailController,
focusNode: focus,
onChanged: (String val) {
setState(() {
if (val != lastValue) {
lastValue = val;
if (val.endsWith(' ') && validateEmail(val.trim())) {
if (!emails.contains(val.trim())) {
emails.add(val.trim());
widget.setList(emails);
}
_emailController.clear();
} else if (val.endsWith(' ') && !validateEmail(val.trim())) {
_emailController.clear();
}
}
});
},
onEditingComplete: () {
updateEmails();
},
)
],
),
));
}
updateEmails() {
setState(() {
if (validateEmail(_emailController.text)) {
if (!emails.contains(_emailController.text)) {
emails.add(_emailController.text.trim());
widget.setList(emails);
}
_emailController.clear();
} else if (!validateEmail(_emailController.text)) {
_emailController.clear();
}
});
}
setEmails(List<String> emails) {
this.emails = emails;
}
}
bool validateEmail(String value) {
Pattern pattern =
r'^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regex = new RegExp(pattern);
return regex.hasMatch(value);
}
出于学习目的,我正在尝试做一个 gmail 克隆,我在编辑新邮件时遇到了多电子邮件输入问题。我需要能够收听“ENTER”或“SPACE”,以便我可以使用这样的电子邮件“块”修改输入(验证后):
我想我可以使用“ENTER”键的 onFieldSubmitted 标签,但我如何更改输入文本?我试过了:
controller.text = Container();
但是“文本”只接受字符串(这似乎符合逻辑)。
目标是执行与您在 Whosebug 上放置“标签”的完全相同类型的 inputField。
我也找到了 EmailInputElement,但我不知道如何正确使用它,它似乎不是一个小部件。
如果有人有任何想法,将不胜感激。
这里的技巧是如下显示令人满意的 UI,InputDecoration.collapsed()
帮助我们使 TextField
最小化,您可以添加一个 Divider()
作为主栏的最后一个小部件,用于描述所有这些都在 TextField
我已经尝试模拟您想要实现的行为。
我使用 onChange
方法检查 TextField
中字符串输入末尾的空格,对于 enter
按钮我使用 onEditingComplete
函数。
我对电子邮件行进行了限制,使其只能增长到视口的特定宽度,您也可以使用不同的布局。
class Test extends StatefulWidget {
@override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
List<String> emails = [];
TextEditingController _emailController;
@override
void initState() {
super.initState();
_emailController = TextEditingController();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
child: Center(
child: Row(
children: <Widget>[
Container(
constraints: BoxConstraints(maxWidth: 200, minWidth: 0),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
...emails
.map((email) => Chip(label: Text(email)))
.toList(),
],
),
),
),
Expanded(
child: TextField(
decoration: InputDecoration.collapsed(hintText: 'EMail'),
controller: _emailController,
onChanged: (String val) {
if (val.endsWith(' '))
setState(() {
emails.add(_emailController.text);
_emailController.text = '';
});
},
onEditingComplete: () {
setState(() {
emails.add(_emailController.text);
_emailController.text = '';
});
},
),
)
],
),
)),
),
);
}
}
如果有人来这里是为了获得我要求的相同功能,这里有我的模板,您可以将其粘贴到一个新文件中,然后在需要的地方调用 EmailInput()
。您只想将 setList
属性与更新 List<String>
的功能一起使用,以将数据返回到您的父组件中:) 它还会检查输入的字符串是否是有效的电子邮件。
看起来像这样:
import 'package:flutter/material.dart';
class EmailInput extends StatefulWidget {
final Function setList;
final String hint;
final List<String> parentEmails;
const EmailInput({Key key, this.setList, this.hint, this.parentEmails}) : super(key: key);
@override
_EmailInputState createState() => _EmailInputState();
}
class _EmailInputState extends State<EmailInput> {
TextEditingController _emailController;
String lastValue = '';
List<String> emails = [];
FocusNode focus = FocusNode();
@override
void initState() {
super.initState();
_emailController = TextEditingController();
focus.addListener(() {
if (!focus.hasFocus) {
updateEmails();
}
});
}
@override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Column(
children: <Widget>[
Container(
constraints: BoxConstraints(
minWidth: 0,
),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Column(
children: <Widget>[
...emails
.map(
(email) => Chip(
avatar: CircleAvatar(
backgroundColor: Colors.black,
child: Text(
email.substring(0, 1),
style: TextStyle(color: Colors.white),
),
),
labelPadding: EdgeInsets.all(4),
backgroundColor: Color.fromARGB(255, 39, 182, 192),
label: Text(
email,
style: TextStyle(fontSize: 16, color: Colors.white),
),
onDeleted: () => {
setState(() {
emails.removeWhere((element) => email == element);
})
},
),
)
.toList(),
],
),
),
),
TextField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration.collapsed(hintText: widget.hint),
controller: _emailController,
focusNode: focus,
onChanged: (String val) {
setState(() {
if (val != lastValue) {
lastValue = val;
if (val.endsWith(' ') && validateEmail(val.trim())) {
if (!emails.contains(val.trim())) {
emails.add(val.trim());
widget.setList(emails);
}
_emailController.clear();
} else if (val.endsWith(' ') && !validateEmail(val.trim())) {
_emailController.clear();
}
}
});
},
onEditingComplete: () {
updateEmails();
},
)
],
),
));
}
updateEmails() {
setState(() {
if (validateEmail(_emailController.text)) {
if (!emails.contains(_emailController.text)) {
emails.add(_emailController.text.trim());
widget.setList(emails);
}
_emailController.clear();
} else if (!validateEmail(_emailController.text)) {
_emailController.clear();
}
});
}
setEmails(List<String> emails) {
this.emails = emails;
}
}
bool validateEmail(String value) {
Pattern pattern =
r'^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regex = new RegExp(pattern);
return regex.hasMatch(value);
}