Flutter/Dart - Username Validation - Checking for bad words - flutter

In the code below, we have a method which is called whenever the user clicks on the 'Sign Up' button (on our Authentication screen). Currently this method has two different conditions, if it doesn't pass these conditions, the user won't be able to sign up.
We'd also like to add one more condition here. We have an array of strings that shouldn't be accepted as usernames (bad words). But if the bad word is used in combination with other letters or numbers, it should also not be accepted.
Example of a "bad word": Charlie
That means Charlie shouldn't be accepted as a username but there's also potential for millions of other combinations, examples: Charlieee, Charlie124342, Charlie6648, 213charlie, ch1chaRliE32, etc. etc.
So would there be any simple method to check whether the username entered contains any of the bad words listed in an array, regardless of whether its used in combination with other characters or not?
Future<String?> usernameValidator({required String? username}) async {
// Validates username complexity
bool isUsernameComplex(String? text) {
final String _text = (text ?? "");
String? p = r"^(?=(.*[ #$!%*?&=_+/#^.~`]))";
RegExp regExp = RegExp(p);
return regExp.hasMatch(_text);
}
final String _text = (username ?? "");
// Complexity check
if (isUsernameComplex(_text)) {
return "Username can only contain letters and numbers.";
}
// Length check
else if (_text.length < 3 || _text.length > 16) {
return "Username must be between 3-16 characters long.";
}
return null;
}

see if this helps:
List<String> badwords = [
'charlie',
'another_badword',
];
List<String> input = [
'Charlieee',
'Charlie124342',
'Charlie6648',
'213charlie',
'ch1chaRliE32',
'spaghetti',
];
input.forEach((text) {
final bwIndex = badwords.indexWhere(
(badword) => text.toLowerCase().contains(badword),
);
if (bwIndex != -1) {
print('$text is bad word, similar to ${badwords[bwIndex]}');
} else {
print('$text is not a bad word');
}
});

Related

username input must be in English only in flutter form

In my form validation I already added stuff like the username already exists or it should be longer than n letters etc. but now I want to add more restrictions and display a message that says 'username' is invalid when it's not in English or if it has stuff like underscores slashes etc. is there a way for me to do that?
For just the English Alphabets you need to set a regex for the alphabetical pattern checking. Create a static final field for the RegExp to avoid creating a new instance every time a value is checked.
static final RegExp alphaExp = RegExp('[a-zA-Z]');
And then to use it :
validator: (value) => value.isEmpty
? 'Enter Your Name'
: (alphaExp.hasMatch(value)
? null
: 'Only Alphabets are allowed in a username');
you can set textfields validations like this it will only except characters a to z. and add validation in textfield through this
class FormValidator {
static String validateEmail(String? name) {
if (name!.isEmpty) {
return 'Name is must not e empty';
}
String pattern =
'([a-zA-Z])';
RegExp regExp = RegExp(pattern);
if (!regExp.hasMatch(name)) {
return 'invalid name';
}
return '';
}
}

how can I check if any word of a string matches a word in a given list?

for example if I have a string = "I Like To Play Football" and a list = [Car,Ball,Door,Sky] it should give true.
Use any of list
var list = ["Car","Ball","Door","Sky"];
String text = "i like to play football";
if (list.any((item) => text.toLowerCase().contains(item))) {
//Text has a value from list
}
Here You Go:
void main() {
final String tempString = "I Like To Play Football";
final List<String> tempList = ["Car","Ball","Door","Sky"];
for(var i=0; i < tempList.length; i ++) {
if(tempString.contains(tempList.elementAt(i).toLowerCase())){
print("Found and its ${tempList[i]}");
}
}
}
Regex is your friend here. You can make a simple regex that uses each string in the array as an option (and make it case insensitive) then run the match. I've made an example here in javascript, but it's easy to do in dart
https://api.dart.dev/stable/2.16.1/dart-core/RegExp-class.html
const source = "I Like To Play Football";
const toMatch = ["Car","Ball","Door","Sky"];
let regexString = '';
for (const option of toMatch) {
//adding | modifier after string. Last one is redundant of course
//also I'm not checking for special regex characters in toMatch, but that might be necessary.
regexString += option + '|';
}
// using slice to remove last |
console.log(regexString.slice(0, -1));
const regexp = new RegExp(regexString.slice(0, -1), 'i');
console.log(source.match(regexp));
Here's a short version:
var src = 'I Like To Play Football'.split(' ');
var list = ['Car','Ball','Door','Sky'];
var result = list.any((x) => src.any((y) => y.toLowerCase().contains(x.toLowerCase())));
print(result);

How can I add Wildcard to the string in flutter dart?

The following code will give the current user whose role is Meet.
_currentUser["role"] == "Meet"
But in reality, I'd like to get the current user whose role is prefixed with Meet.
eg.MeetAdmin, MeetPec, like that.
Please help me how do I do that.
You can create an extension on String:
extension StringExtension on String {
bool hasPrefix(String prefix) {
return substring(0, prefix.length) == prefix;
}
}
void main() {
final role = 'MeetPec';
final invalidRole = 'PecMeet';
print(role.hasPrefix('Meet')); // returns true
print(invalidRole.hasPrefix('Meet')); // returns false
}
It assumes case-sensitive check, but you can tweak it to also support case-insensitive by adding .toLowerCase() to both prefix and the string itself.
EDIT:
As pointed out in the comments, we already have a startsWith method, this is definitely a way to go here:
void main() {
final role = 'MeetPec';
final invalidRole = 'PecMeet';
print(role.startsWith('Meet')); // returns true
print(invalidRole.startsWith('Meet')); // returns false
}
Okay, if I understand your question property, you want to check if the current user role has the word "meet" in it.
If that's the case your can use contains to check if the role contains role
Example
if(_currentUser["role"].contains("meet") ) {
//It contains meet
}else{
//It does not contain meet
}
check this method it might help you. whenever you want to check or search you should convert string to lower case to compare then check
bool getCurrentUserWithMeet() {
Map<String, String> _currentUser = {
'role': 'MeetAdmin',
};
final isCurrentUserContainsMeet =
_currentUser['role']?.toLowerCase().contains('meet');
return isCurrentUserContainsMeet ?? false;
}

Multiple word search using trie in dart

I'm trying to implement trie search in flutter. And here's the entire trie.dart file.
The idea is something like this, say we have I have a list of recipe names:
Burger
French Fries
Ice Cream
Garlic Parmesan Butter
Now I need to search using prefix so if the user searches for bur it'll show Burger. But if someone write Garlic Butter I need to return Garlic Parmesan Butter. So, basically if the search query has multiple words I need to show the correct name.
Here's the part where I get all words with prefix:
List<String> getAllWordsWithPrefix(String prefix) {
StringBuffer fullPrefix = new StringBuffer();
return _getAllWordsWithPrefixHelper(prefix, _head, fullPrefix);
}
List<String> _getAllWordsWithPrefixHelper(
String prefix, _TrieNode node, StringBuffer fullPrefix) {
if (prefix.length == 0) {
String pre = fullPrefix.toString();
return _collect(
new StringBuffer(pre.substring(0, max(pre.length - 1, 0))), node, []);
}
for (_TrieNode child in node.children) {
if ((child.char == prefix.substring(0, 1)) ||
(!_isCaseSensitive &&
child.char.substring(0, child.char.length).toLowerCase() ==
prefix.substring(0, 1).toLowerCase())) {
fullPrefix.write(child.char);
return _getAllWordsWithPrefixHelper(
prefix.substring(1), child, fullPrefix);
}
}
return [];
}
And finally I'm using the trie search in the following way(thought this might help someway):
class Search {
final Map<String, RecipeModel> _map = Map.fromIterable(
Store.instance.getAllRecipes(),
// ignore: non_constant_identifier_names
// key: (recipe) => RecipeModel().recipeName!,
key: (recipe) => recipe.recipeName!,
);
late final Trie trie;
Search() {
// This will be O[n]
trie = Trie.list(_map.keys.toList());
}
late RecipeModel recipe;
RecipeModel returnRecipe(String? suggestion) {
if (suggestion == null) return recipe;
// This will be O(1) instead of O(n) [better]
final RecipeModel? found = _map[suggestion];
return found ?? recipe;
}
List<String> returnSuggestions(String prefix) {
//will return O[W*L] ad-hoc search was O[n^2]
return trie.getAllWordsWithPrefix(prefix);
}
}
First, your TrieNode is using a List, which means you have to do a for-loop O(N) search for each char. It would be faster to use a Map.
(You could also use a 26D array, instead of a map, if you know that you'll only have English letters: [0] = 'A', [1] = 'B', ... [25] = 'Z')
Part I: garlic butter
Now I need to search using prefix so if the user searches for bur it'll show Burger. But if someone write Garlic Butter I need to Garlic Parmesan Butter. So, basically if the search query has multiple words I need to show the correct name.
bur is easy to do, as that's what a Trie data structure is for, but the garlic butter one is harder. You probably want to look into how to implement a fuzzy search or "did you mean...?" type algorithm:
Fuzzy search algorithm (approximate string matching algorithm)
However, maybe this is more complex than you want.
Here are some alternatives I thought of:
Option #1
Change your TrieNode to store a String variable stating the "standard" word. So the final TrieNode of garlic parmesan butter and garlic butter would both have an instance variable that stores garlic butter (the main standard/common word you want to use).
Your TrieNode would be like this:
class _TrieNode {
//...other vars...
String _standardWord;
}
Then your add method would look like this:
String standardWord = 'garlic butter';
trie.addWord('garlic butter', standardWord);
trie.addWord('garlic parmesan butter', standardWord);
Internally, your add method would set the standardWord of the last char's TrieNode to the 2nd param of addWord.
Internally, your find method would return the standardWord, which was set in the last char's TrieNode.
Then you only have to test for garlic butter and don't have to worry about parmesan.
Does that make sense?
Of course, you can also flip this around so that it always returns garlic parmesan butter instead:
String standardWord = 'garlic parmesan butter';
trie.addWord('garlic butter', standardWord);
trie.addWord('garlic parmesan butter', standardWord);
This requires that you know all phrases that it could be in advance, and then you add them to your Trie, with all common phrases pointing to the same standard word.
Option #2
Split your phrase/sentence into words, based on the spaces between words.
So your Trie would look like this:
trie.addWord('garlic');
trie.addWord('parmesan');
trie.addWord('butter');
When you have all of the matching words, you then need to write an algorithm (logic) to piece them together in a meaningful way.
Here's an example:
Set<String> wordsFromTrie = {};
List<String> words = "garlic parmesan butter".split(RegExp(r'\s+'));
words.forEach((word) => wordsFromTrie.add(trie.findWord(word)));
if(wordsFromTrie.contains("garlic") && wordsFromTrie.contains("butter")) {
print("Garlic parmesan butter!");
}
Option #3
Use some type of Regex matching instead of a Trie, so for example, your Regex would be RegExp(r"garlic.*butter",caseSensitive: false) and this would match all garlic butter regardless of what's in the middle.
This will be a bit slower, as you'll have a bunch of if-statements with Regexes. You could make it faster by first testing someString.startsWith('garlic') (after stripping spaces and lower-casing).
Option #4
Use a combination of #1 and #3.
This would mean you'd have a special RegexTrieNode that you would add.
trie.addWord('garlic',RegExp(r'[^b]+'),'butter');
When you hit RegexTrieNode it would continue to match each char until it stops matching. When it stops matching, you would need to go to the next child, butter, for the rest of the matching.
It's quite complicated, and it won't work for all Regexes, but it's doable.
Part II: bur => burger
Basically, once you reach the end of bur, you need to keep going down (or up?) the Trie for TrieNodes that only have 1 child.
Why only 1 child? Well, if you have burrito and burger, which does bur match? It's ambiguous.
Here's some example code, roughly based on your code. It uses null-safety. If you don't have that enabled, then remove all ? from the code.
import 'dart:math';
void main() {
var trie = Trie();
trie.addWord("burger");
// All results will be "burger".
print(trie.findWord("b"));
print(trie.findWord("bur"));
print(trie.findWord("burger"));
// This will be null, but fixable
// if want to allow longer strings.
print(trie.findWord("burgerme"));
}
class Trie {
TrieNode head = TrieNode();
void addWord(String? word) {
if(word == null || word.isEmpty) {
return;
}
var currentNode = head;
// Rune is a unicode codepoint (unicode char).
word.runes.forEach((rune) {
var childNode = currentNode.children[rune];
if(childNode == null) {
childNode = TrieNode();
currentNode.children[rune] = childNode; // Add to parent.
}
currentNode = childNode; // Next node.
});
// Last node is the last char.
currentNode.endOfWord = true;
}
String? findWord(String partial) {
var word = StringBuffer();
var currentNode = head;
partial.runes.forEach((rune) {
var childNode = currentNode.children[rune];
if(childNode == null) {
return null; // Not found.
}
word.writeCharCode(rune);
currentNode = childNode; // Next node.
});
// Prevent "burgerme" from matching to "burger".
// Uncomment this if-block if want to allow it.
if(currentNode.endOfWord && partial.length > word.length) {
return null;
}
// This logic allows "bur" to match to "burger".
while(!currentNode.endOfWord) {
// Ambiguous: "bur" could match either "burger" or "burrito".
if(currentNode.children.length != 1) {
return null; // Don't know.
}
var onlyChild = currentNode.children.entries.first;
word.writeCharCode(onlyChild.key);
currentNode = onlyChild.value; // Next node.
}
return word.toString();
}
}
class TrieNode {
Map<int,TrieNode> children = {};
bool endOfWord = false;
}

How to Check username already exists in the database or not and validate the username without spaces

I am making a register form that can check whether there is or not in the database and how to validate the username must use alphanumeric and no spaces,
this is my code for now
String validateUsername(String value) {
String 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 regExp = new RegExp(pattern);
if (value.length == 0) {
return "Username is Required";
} else if(!regExp.hasMatch(value)){
return "Invalid username";
}else {
return null;
}
}
I think the validation is almost the same as email, but I'm still confused
This is how I do it.
while using flutter.
// import needed for BlackListingTextInputFormatter function
import 'package:flutter/services.dart';
TextField(
inputFormatters: [
BlacklistingTextInputFormatter(
new RegExp("[^a-z^A-Z^0-9]+")) //Regex for accepting only alphanumeric characters
],
)
using this it will ensure no character other than alphanumeric can pass through the textfield.
so now you just need to check the string you get from that field with the database.
You can make an api and make an query like
SELECT * FROM user WHERE user_name = "userName";
if you get an empty response then that username is available.