Flutter несколько текстовых полей электронной почты
Для обучения я пытаюсь сделать клон Gmail, я борюсь с вводом нескольких электронных писем, который у вас есть, когда вы редактируете новое письмо. Мне нужно уметь слушать либо "ENTER", либо "SPACE", чтобы я мог изменять ввод (после проверки) с помощью "блока" электронной почты, например:
Я думаю, что могу работать с тегом onFieldSubmitted для клавиши "ENTER", но как я могу изменить вводимый текст? Я попытался:
controller.text = Container();
но "текст" принимает только String (что кажется логичным).
Цель состоит в том, чтобы создать точно такой же тип inputField, куда вы помещаете "Теги" в Stackru.
Я также нашел EmailInputElement, но не могу понять, как его правильно использовать, похоже, это не виджет.
Если у кого-то есть идея, это будет очень признательно, спасибо.
2 ответа
Хитрость здесь в том, чтобы показать желаемый пользовательский интерфейс следующим образом: InputDecoration.collapsed()
помогает нам сделать TextField
минимальный, вы можете добавить Divider()
как последний виджет основного столбца, чтобы изобразить, что все это происходит внутри TextField
Я попытался смоделировать поведение, которое вы пытаетесь реализовать. Я проверяю наличие пробелов в конце строки ввода вTextField
с помощью onChange
метод и для 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);
}