Flutter multiple email TextField
Solution 1
The trick here would be to show the deirable UI as follows, InputDecoration.collapsed()
helps us to make the TextField
minimal, u may add a Divider()
as the last widget of the main column to portray that all of this goes on inside the TextField
I have tried to simulate the behaviour you seek to implement.
I check for spaces at the end of string input in TextField
using onChange
method and for enter
button i use onEditingComplete
function.
I have laid constrains on the row of emails to grow only upto a certain width of the viewport you can use a different layout too.
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 = '';
});
},
),
)
],
),
)),
),
);
}
}
Solution 2
If someone come here for the same features that i asked for there is my template, you can paste this in a new file and just call EmailInput()
where you need it. You just want to use the setList
attribute with the function to update your List<String>
to get the data back into your parent component :) It also check if the string entered is a valid email or not.
It look something like this :
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);
}
Comments
-
Anthony D over 1 year
For learning purpose i'm trying to do a gmail clone, i'm struggling with the multi email input you have when you are editing a new mail. I need to be abble to listen either for "ENTER" or "SPACE" so that i can modify the input (after validation) with a email "block" like this :
I think i can work with the onFieldSubmitted tag for the "ENTER" key but how can i change the input text ? I tried :
controller.text = Container();
but the "text" only accepts String (which seems logic).
The goal is to do the exact same kind of inputField where you put the "Tags" on StackOverflow.
I also found the EmailInputElement but i can't figure out how to use it properly, it seems like it's not a widget.
If someone have any idea it would be much appreciated, thanks.
-
Anthony D almost 4 yearsThanks a lot @Aadiyaara your example make a lot of sense after fideling a bit with it,It's really a good starting point, i'll continue based on that Thanks dude !