isValidUrlAsync<T> function
Validates URLs with format checking and domain restrictions. Returns String/bool/UrlValidationResult based on generic type T.
Implementation
Future<T> isValidUrlAsync<T>(
String url, {
bool allowRelative = false,
List<String>? allowedSchemes,
List<String>? allowedDomains,
bool normalize = true,
bool checkDomainExists = false,
}) async {
UrlValidationResult result;
if (url.trim().isEmpty) {
result = UrlValidationResult(isValid: false, message: "URL is required");
return _castResult<T>(result);
}
String trimmedUrl = url.trim();
try {
Uri uri = Uri.parse(trimmedUrl);
// Allow relative paths
if (allowRelative && !uri.hasScheme && uri.path.isNotEmpty) {
result = UrlValidationResult(
isValid: true,
message: "Relative URL is valid",
);
return _castResult<T>(result);
}
// Scheme check
if (uri.scheme.isEmpty) {
result = UrlValidationResult(
isValid: false,
message: "URL must start with http:// or https://",
);
return _castResult<T>(result);
}
if (allowedSchemes != null &&
!allowedSchemes.contains(uri.scheme.toLowerCase())) {
result = UrlValidationResult(
isValid: false,
message: "Scheme '${uri.scheme}' is not allowed",
);
return _castResult<T>(result);
}
if (!uri.hasAuthority || uri.host.isEmpty) {
result = UrlValidationResult(
isValid: false,
message: "URL must have a valid domain",
);
return _castResult<T>(result);
}
// Domain whitelist check
if (allowedDomains != null &&
!allowedDomains.contains(uri.host.toLowerCase())) {
result = UrlValidationResult(
isValid: false,
message: "Domain '${uri.host}' is not allowed",
);
return _castResult<T>(result);
}
// Optional DNS check (not supported on web platforms)
if (checkDomainExists && !kIsWeb) {
try {
final lookup = await InternetAddress.lookup(uri.host);
if (lookup.isEmpty || lookup.first.address.isEmpty) {
result = UrlValidationResult(
isValid: false,
message: "Domain does not exist",
);
return _castResult<T>(result);
}
} catch (e) {
result = UrlValidationResult(
isValid: false,
message: "Domain lookup failed",
);
return _castResult<T>(result);
}
}
UrlData urlData = UrlData(
protocol: uri.scheme.toLowerCase(),
host: uri.host.toLowerCase(),
domain: _extractDomain(uri.host),
port: uri.hasPort ? uri.port : null,
path: uri.path.isEmpty ? "/" : uri.path,
query: uri.query.isEmpty ? null : uri.query,
fragment: uri.fragment.isEmpty ? null : uri.fragment,
fullUrl: normalize ? _normalizeUrl(uri) : trimmedUrl,
);
result = UrlValidationResult(
isValid: true,
message: "URL is valid",
data: urlData,
);
return _castResult<T>(result);
} catch (e) {
result = UrlValidationResult(
isValid: false,
message: "URL format is invalid",
);
return _castResult<T>(result);
}
}