š§ Project: String Tools Package
š Lesson 4: Packages & Libraries
ā± 25-30 min
š Intermediate
šÆ Objective
Create a reusable string_tools package with extension methods and utility functions for string manipulation. Learn to organize code with extensions and proper exports.
Requirements
- Create package with
dart create -t package string_tools - Implement extension methods on String class
- Create utility functions for validation, formatting, and transformation
- Organize code in
lib/src/with separate files - Export only the public API from main library file
- Include: capitalize, titleCase, truncate, isValidEmail, isValidPassword, countWords, reverse, removeWhitespace
// ============================================
// FILE: string_tools/lib/src/extensions.dart
// ============================================
/// Extension methods on the String class.
library;
extension StringExtensions on String {
/// Capitalizes the first letter of the string.
String get capitalize {
if (isEmpty) return this;
return '${this[0].toUpperCase()}${substring(1)}';
}
/// Converts to Title Case (capitalizes first letter of each word).
String get titleCase {
if (isEmpty) return this;
return split(' ')
.map((word) => word.isEmpty ? word : word.capitalize)
.join(' ');
}
/// Reverses the string.
String get reverse {
return split('').reversed.join('');
}
/// Truncates the string to [maxLength] and adds '...' if truncated.
String truncate(int maxLength) {
if (length <= maxLength) return this;
return '${substring(0, maxLength)}...';
}
/// Removes all whitespace from the string.
String get removeWhitespace {
return replaceAll(RegExp(r'\s+'), '');
}
/// Counts the number of words in the string.
int get wordCount {
if (isEmpty) return 0;
return trim().split(RegExp(r'\s+')).length;
}
/// Checks if the string is a palindrome.
bool get isPalindrome {
final cleaned = toLowerCase().replaceAll(RegExp(r'[^a-z0-9]'), '');
return cleaned == cleaned.reverse;
}
/// Converts to camelCase.
String get camelCase {
final words = split(RegExp(r'[\s_]+'));
if (words.isEmpty) return '';
return words.first.toLowerCase() +
words.skip(1).map((w) => w.capitalize).join('');
}
/// Converts to snake_case.
String get snakeCase {
return replaceAll(RegExp(r'([A-Z])'), r'_\1')
.replaceAll(RegExp(r'\s+'), '_')
.toLowerCase()
.replaceAll(RegExp(r'^_+'), '');
}
}
// ============================================
// FILE: string_tools/lib/src/validators.dart
// ============================================
/// String validation utilities.
library;
/// Validates an email address format.
bool isValidEmail(String email) {
if (email.isEmpty) return false;
final emailRegex = RegExp(
r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
);
return emailRegex.hasMatch(email);
}
/// Validates password strength.
/// Must be at least 8 characters with uppercase, lowercase, number, and special char.
bool isValidPassword(String password) {
if (password.length < 8) return false;
bool hasUppercase = password.contains(RegExp(r'[A-Z]'));
bool hasLowercase = password.contains(RegExp(r'[a-z]'));
bool hasDigit = password.contains(RegExp(r'[0-9]'));
bool hasSpecial = password.contains(RegExp(r'[!@#$%^&*(),.?":{}|<>]'));
return hasUppercase && hasLowercase && hasDigit && hasSpecial;
}
/// Checks password strength and returns a score (0-4).
int passwordStrength(String password) {
int score = 0;
if (password.length >= 8) score++;
if (password.length >= 12) score++;
if (password.contains(RegExp(r'[A-Z]'))) score++;
if (password.contains(RegExp(r'[0-9]'))) score++;
if (password.contains(RegExp(r'[!@#$%^&*(),.?":{}|<>]'))) score++;
return score.clamp(0, 4);
}
/// Validates a URL format.
bool isValidUrl(String url) {
final urlRegex = RegExp(
r'^(https?:\/\/)?([\w\-]+\.)+[\w\-]+(\/[\w\-\.~:/?#\[\]@!$&\'()*+,;=]*)?$'
);
return urlRegex.hasMatch(url);
}
// ============================================
// FILE: string_tools/lib/src/formatters.dart
// ============================================
/// String formatting utilities.
library;
/// Formats a phone number to (XXX) XXX-XXXX format.
String formatPhoneNumber(String phone) {
final digits = phone.replaceAll(RegExp(r'\D'), '');
if (digits.length == 10) {
return '(${digits.substring(0, 3)}) ${digits.substring(3, 6)}-${digits.substring(6)}';
}
return phone; // Return original if invalid
}
/// Formats a number as currency string.
String formatCurrency(double amount, {String symbol = '\$'}) {
final parts = amount.toStringAsFixed(2).split('.');
final intPart = parts[0].replaceAllMapped(
RegExp(r'(\d)(?=(\d{3})+(?!\d))'),
(match) => '${match[1]},',
);
return '$symbol$intPart.${parts[1]}';
}
/// Generates a slug from a string (URL-friendly version).
String slugify(String text) {
return text
.toLowerCase()
.replaceAll(RegExp(r'[^a-z0-9\s-]'), '')
.replaceAll(RegExp(r'\s+'), '-')
.replaceAll(RegExp(r'-+'), '-')
.replaceAll(RegExp(r'^-|-$'), '');
}
/// Wraps text at a specified width.
String wordWrap(String text, {int width = 80}) {
if (text.length <= width) return text;
final buffer = StringBuffer();
int currentLineLength = 0;
for (var word in text.split(' ')) {
if (currentLineLength + word.length > width) {
buffer.writeln();
currentLineLength = 0;
}
if (currentLineLength > 0) {
buffer.write(' ');
currentLineLength++;
}
buffer.write(word);
currentLineLength += word.length;
}
return buffer.toString();
}
// ============================================
// FILE: string_tools/lib/string_tools.dart
// ============================================
/// A comprehensive string manipulation package.
library string_tools;
export 'src/extensions.dart';
export 'src/validators.dart';
export 'src/formatters.dart';
// ============================================
// FILE: test_app/bin/main.dart
// ============================================
import 'package:string_tools/string_tools.dart';
void main() {
print('āāāāāāāāāāāāāāāāāāāāāāāāāāāā');
print('ā String Tools Demo ā');
print('āāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
// Test extensions
print('š Extensions:');
print(' "hello world".capitalize = "${"hello world".capitalize}"');
print(' "dart programming".titleCase = "${"dart programming".titleCase}"');
print(' "hello".reverse = "${"hello".reverse}"');
print(' Word count: ${"The quick brown fox".wordCount}');
print(' Palindrome check: "racecar" = ${"racecar".isPalindrome}');
print(' Truncate: ${"This is a very long text".truncate(10)}');
// Test case conversions
print('\nš Case Conversions:');
print(' camelCase: ${"hello world example".camelCase}');
print(' snake_case: ${"HelloWorldExample".snakeCase}');
// Test validators
print('\nā
Validation:');
final emails = ['user@example.com', 'invalid-email', 'test@domain.co.uk'];
for (var email in emails) {
print(' $email ā ${isValidEmail(email) ? "ā
Valid" : "ā Invalid"}');
}
final passwords = ['weak', 'StrongP@ss1', 'Abc123!@'];
for (var pwd in passwords) {
print(' Password "$pwd": Strength ${passwordStrength(pwd)}/4');
}
// Test formatters
print('\nš Formatting:');
print(' Phone: ${formatPhoneNumber("1234567890")}');
print(' Currency: ${formatCurrency(1234567.89)}');
print(' Slug: ${slugify("Hello World! This is Dart.")}');
// Test word wrap
print('\nš Word Wrap:');
final longText = 'Dart is a client-optimized language for fast apps on any platform. It is developed by Google and is used to build mobile, desktop, server, and web applications.';
print(wordWrap(longText, width: 60));
}