Skip to content
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ If you wish to be specific about which platforms you want to configure, use the
flutterfire configure --yes --project=<FIREBASE_PROJECT_ID> --platforms=android,ios,web
```

If you want to specify a display name (i.e. how it appears in the Firebase console), use the `--display-name` flag. It will still have the platform as a suffix (e.g. `--display-name=test` will appear as "test (ios), "test (web)", etc"):
```bash
flutterfire configure --yes --project=<FIREBASE_PROJECT_ID> --display-name=test
```

FlutterFire CLI now supports Windows applications. The Firebase console does not allow you to create an application for Windows, FlutterFire CLI will create a **web app** if you specify windows as a platform you want to configure:

```bash
Expand Down
14 changes: 14 additions & 0 deletions packages/flutterfire_cli/lib/src/commands/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ class ConfigCommand extends FlutterFireCommand {
'Optionally specify the platforms to generate configuration options for '
'as a comma separated list. For example "android,ios,macos,web,linux,windows".',
);
argParser.addOption(
kDisplayNameFlag,
valueHelp: 'displayName',
abbr: 'n',
help: 'The display name of your app, e.g. "My Cool App". '
'If no display name is provided then an attempt will be made to '
'automatically detect it from your project configuration (if it exists).',
);
argParser.addOption(
kIosBundleIdFlag,
valueHelp: 'bundleIdentifier',
Expand Down Expand Up @@ -243,6 +251,11 @@ class ConfigCommand extends FlutterFireCommand {
.toList();
}

String get displayName {
final name = argResults![kDisplayNameFlag] as String?;
return name ?? flutterApp!.package.pubSpec.name;
}

bool get applyGradlePlugins {
return argResults!['apply-gradle-plugins'] as bool;
}
Expand Down Expand Up @@ -625,6 +638,7 @@ class ConfigCommand extends FlutterFireCommand {
iosBundleId: iosBundleId,
macosBundleId: macosBundleId,
token: token,
displayName: displayName,
serviceAccount: serviceAccount,
webAppId: webAppId,
windowsAppId: windowsAppId,
Expand Down
1 change: 1 addition & 0 deletions packages/flutterfire_cli/lib/src/common/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const String kConfigurations = 'configurations';
const String kOutFlag = 'out';
const String kYesFlag = 'yes';
const String kPlatformsFlag = 'platforms';
const String kDisplayNameFlag = 'display-name';
const String kIosBundleIdFlag = 'ios-bundle-id';
const String kMacosBundleIdFlag = 'macos-bundle-id';
const String kAndroidAppIdFlag = 'android-app-id';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ extension FirebaseAndroidOptions on FirebaseOptions {
String? firebaseAccount,
required String? token,
required String? serviceAccount,
required String displayName,
}) async {
var selectedAndroidApplicationId =
androidApplicationId ?? flutterApp.androidApplicationId;
Expand All @@ -56,7 +57,7 @@ extension FirebaseAndroidOptions on FirebaseOptions {
);
final firebaseApp = await firebase.findOrCreateFirebaseApp(
packageNameOrBundleIdentifier: selectedAndroidApplicationId,
displayName: flutterApp.package.pubSpec.name,
displayName: displayName,
platform: kAndroid,
project: firebaseProjectId,
account: firebaseAccount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ extension FirebaseAppleOptions on FirebaseOptions {
String? firebaseAccount,
required String? token,
required String? serviceAccount,
required String displayName,
}) async {
final platformIdentifier = macos ? kMacos : kIos;
var selectedAppleBundleId = appleBundleIdentifier ??
Expand All @@ -58,7 +59,7 @@ extension FirebaseAppleOptions on FirebaseOptions {
);
final firebaseApp = await firebase.findOrCreateFirebaseApp(
packageNameOrBundleIdentifier: selectedAppleBundleId,
displayName: flutterApp.package.pubSpec.name,
displayName: displayName,
platform: platformIdentifier,
project: firebaseProjectId,
account: firebaseAccount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ extension FirebaseDartOptions on FirebaseOptions {
String platform = kWeb,
required String? token,
required String? serviceAccount,
required String displayName,
}) async {
final firebaseApp = await firebase.findOrCreateFirebaseApp(
displayName: flutterApp.package.pubSpec.name,
displayName: displayName,
platform: platform,
project: firebaseProjectId,
account: firebaseAccount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Future<FirebasePlatformOptions> fetchAllFirebaseOptions({
String? windowsAppId,
String? token,
String? serviceAccount,
required String displayName,
}) async {
FirebaseOptions? androidOptions;
FirebaseOptions? iosOptions;
Expand All @@ -56,6 +57,7 @@ Future<FirebasePlatformOptions> fetchAllFirebaseOptions({
firebaseAccount: firebaseAccount,
token: token,
serviceAccount: serviceAccount,
displayName: displayName,
);
}

Expand All @@ -67,6 +69,7 @@ Future<FirebasePlatformOptions> fetchAllFirebaseOptions({
firebaseAccount: firebaseAccount,
token: token,
serviceAccount: serviceAccount,
displayName: displayName,
);
}
if (macos) {
Expand All @@ -78,6 +81,7 @@ Future<FirebasePlatformOptions> fetchAllFirebaseOptions({
macos: true,
token: token,
serviceAccount: serviceAccount,
displayName: displayName,
);
}

Expand All @@ -89,6 +93,7 @@ Future<FirebasePlatformOptions> fetchAllFirebaseOptions({
webAppId: webAppId,
token: token,
serviceAccount: serviceAccount,
displayName: displayName,
);
}

Expand All @@ -101,6 +106,7 @@ Future<FirebasePlatformOptions> fetchAllFirebaseOptions({
token: token,
webAppId: windowsAppId,
serviceAccount: serviceAccount,
displayName: displayName,
);
}

Expand All @@ -112,6 +118,7 @@ Future<FirebasePlatformOptions> fetchAllFirebaseOptions({
platform: kLinux,
token: token,
serviceAccount: serviceAccount,
displayName: displayName,
);
}

Expand Down
33 changes: 33 additions & 0 deletions packages/flutterfire_cli/test/test_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,39 @@ Future<String> createFlutterProject() async {

final flutterProjectPath = p.join(tempDir.path, flutterProject);

// Set iOS minimum deployment target to 15.0
const iosVersion = '15.0';

// Update project.pbxproj
final pbxprojResult = await Process.run(
'sed',
[
'-i',
'',
's/IPHONEOS_DEPLOYMENT_TARGET = [0-9.]*;/IPHONEOS_DEPLOYMENT_TARGET = $iosVersion;/',
'ios/Runner.xcodeproj/project.pbxproj',
],
workingDirectory: flutterProjectPath,
);

if (pbxprojResult.exitCode != 0) {
throw Exception(
'Failed to set iOS deployment target: ${pbxprojResult.stderr}',
);
}

// Update Podfile if it exists
final podfilePath = p.join(flutterProjectPath, 'ios', 'Podfile');
final podfile = File(podfilePath);
if (podfile.existsSync()) {
var podfileContent = podfile.readAsStringSync();
podfileContent = podfileContent.replaceFirst(
RegExp("platform :ios, '[0-9.]+'"),
"platform :ios, '$iosVersion'",
);
podfile.writeAsStringSync(podfileContent);
}

return flutterProjectPath;
}

Expand Down
44 changes: 44 additions & 0 deletions packages/flutterfire_cli/test/unit_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:convert';

import 'package:flutterfire_cli/src/common/strings.dart';
import 'package:flutterfire_cli/src/common/utils.dart';
import 'package:flutterfire_cli/src/firebase/firebase_app.dart';
import 'package:test/test.dart';

void main() {
Expand Down Expand Up @@ -142,6 +143,49 @@ void main() {
});
});

group('FirebaseApp displayName handling', () {
test('FirebaseApp.fromJson correctly parses displayName from JSON', () {
final json = {
'platform': 'ANDROID',
'appId': '1:123456:android:abc',
'displayName': 'My Cool App',
'name': 'projects/test/apps/abc',
'packageName': 'com.example.app',
};

final app = FirebaseApp.fromJson(json);

expect(app.displayName, 'My Cool App');
expect(app.name, 'projects/test/apps/abc');
expect(app.platform, 'android');
expect(app.packageNameOrBundleIdentifier, 'com.example.app');
});

test('FirebaseApp.fromJson handles null or missing displayName', () {
// Test with null displayName
final jsonWithNull = {
'platform': 'IOS',
'appId': '1:123456:ios:xyz',
'displayName': null,
'name': 'projects/test/apps/xyz',
'bundleId': 'com.example.app',
};

final appWithNull = FirebaseApp.fromJson(jsonWithNull);
expect(appWithNull.displayName, null);

// Test with missing displayName field
final jsonWithoutField = {
'platform': 'WEB',
'appId': '1:123456:web:def',
'name': 'projects/test/apps/def',
};

final appWithoutField = FirebaseApp.fromJson(jsonWithoutField);
expect(appWithoutField.displayName, null);
});
});

group(
'Firebase CLI JSON response parser function `firebaseCLIJsonParse()`',
() {
Expand Down
Loading