firebaseSetupCommand function
Orchestrates the full Firebase setup flow for a Flutter project.
If full is true, it performs a comprehensive setup including tool
checks, login, project creation, and dependency configuration.
The env parameter specifies the target environment (e.g., 'dev').
Implementation
Future<void> firebaseSetupCommand({
required bool full,
required String env,
}) async {
final firebase = FirebaseService();
final flutter = FlutterService();
final prompt = PromptService();
final envService = EnvService();
final feature = FeatureService();
print('š FirePilot: Elite Firebase Setup ($env)\n');
try {
/// š¹ Validate Flutter project
if (!File('pubspec.yaml').existsSync()) {
print('ā Not a Flutter project');
print('š Run this inside a Flutter project folder');
return;
}
/// š¹ Check & auto-fix tools
await _checkAndFixTools();
/// š¹ Ask Project ID
/// š¹ Ask Project ID with Validation
String projectId = '';
while (true) {
projectId = prompt.ask('Enter Firebase project ID');
if (projectId.isEmpty) {
print('ā Project ID cannot be empty');
continue;
}
final regExp = RegExp(r'^[a-z][a-z0-9-]{5,29}$');
if (!regExp.hasMatch(projectId)) {
print('ā Invalid Project ID format');
print('š Must be 6-30 chars, lowercase, numbers, and hyphens (no underscores)');
print('š Must start with a letter');
continue;
}
break;
}
/// š¹ Firebase Login & Account verification
print('š Checking Firebase login...');
final activeAccount = await firebase.login();
if (activeAccount != null) {
print('ā
Active Account: $activeAccount\n');
}
/// š¹ List & Check Projects
print('\nš Checking your Firebase projects...');
final existingProjects = await firebase.listProjects();
final alreadyExists = existingProjects.contains(projectId);
if (alreadyExists) {
print('ā Project ID "$projectId" is already taken in your Firebase console.');
print('š Please choose a unique ID for your new project.');
print('ā Setup stopped.');
return;
}
print('š Found ${existingProjects.length} existing projects.');
// Suggest similar IDs if any
final similar = existingProjects.where((p) => p.contains(projectId) || projectId.contains(p)).toList();
if (similar.isNotEmpty) {
print('\nš” Similar projects found in your account:');
for (var s in similar) {
print(' - $s');
}
final useSimilar = prompt.confirm('Do you want to use one of these instead?');
if (useSimilar) {
final index = prompt.select('Select project', similar);
projectId = similar[index];
print('ā
Selected: $projectId');
// š„ Re-check if the selected "similar" project exists (it obviously does)
// Since the user wants to STOP if it exists, selecting a similar existing one might also mean we stop?
// Actually, if we allow them to select an existing one, and then stop, that's confusing.
// I'll add a warning that choosing an existing one will also stop if they want only NEW projects.
// Wait, the user said "if i choose project id ... and someone has taken ... then can i make ... No".
// They want to make a NEW project.
print('ā Selected project "$projectId" already exists.');
print('ā Setup stopped.');
return;
}
}
/// š¹ Create Project (Mandatory for a clean setup)
final displayName = prompt.ask('Enter Firebase project display name (Optional, press Enter to use ID)');
final finalDisplayName = displayName.isEmpty ? projectId : displayName;
print('š¦ Creating NEW Firebase project "$projectId" ($finalDisplayName)...');
try {
await firebase.createProject(projectId, displayName: finalDisplayName);
print('ā
Project created successfully');
} catch (e) {
print('\nā Project creation failed!');
print('š Error: $e');
print('\nā ļø POSSIBLE REASONS:');
print(' 1. ID "$projectId" is already taken GLOBALLY by another user.');
print(' 2. Quota Limit: You have exceeded the max number of projects for your account.');
print(' 3. Billing/Policy: Some accounts require billing to create new projects.\n');
print('š TIP: Check your Firebase Console to delete old test projects.');
print('ā Setup stopped.');
return;
}
/// š¹ Interactive Platform Selection
final platforms = ['android', 'ios'];
print('š„ļø platform Selection (Android & iOS are default)');
if (prompt.confirm('Enable Windows support?')) {
platforms.add('windows');
}
if (prompt.confirm('Enable macOS support?')) {
platforms.add('macos');
}
/// š¹ Interactive Feature Selection
final selectedFeatures = <String>[];
if (full) {
print('\nš Feature Selection');
if (prompt.confirm('Enable Firebase Auth?')) {
selectedFeatures.add('auth');
}
if (prompt.confirm('Enable Cloud Messaging (FCM)?')) {
selectedFeatures.add('fcm');
}
}
/// š¹ Configure FlutterFire
print('\nāļø Configuring FlutterFire for: ${platforms.join(', ')}...');
await firebase.configure(projectId, platforms: platforms, env: env);
/// š¹ Add Dependencies
print('š¦ Adding Firebase dependencies...');
await flutter.addDeps(); // Adds firebase_core
// Add feature-specific dependencies
for (final f in selectedFeatures) {
if (f == 'auth') await flutter.addDep('firebase_auth');
if (f == 'fcm') await flutter.addDep('firebase_messaging');
}
/// š Setup SHA
print('š Setting up SHA...');
try {
await firebase.setupSha(projectId);
} catch (e) {
print('ā ļø SHA setup failed, skipping...');
}
/// š¹ Create Environment Folder
print('š Setting up environment...');
envService.create(env);
/// š„ Enable selected Features in Console
if (selectedFeatures.isNotEmpty) {
print('š„ Enabling selected features...\n');
for (final f in selectedFeatures) {
await feature.enable(f, projectId: projectId);
}
print('\nš¦ Enabled: ${selectedFeatures.join(', ')}');
} else if (full) {
print('ā¹ļø No additional features selected');
} else {
print('ā¹ļø Skipping feature setup (use --full to enable selection)');
}
print('\nš Firebase setup completed successfully for [$env]');
} catch (e) {
print('\nā Setup failed!');
print('Error: $e');
}
}