analyze method
Public method or function.
Implementation
@override
/// Public method or function.
Future<DiagnosticResult> analyze(AnalyzerContext context) async {
final startTime = DateTime.now();
final issues = <DiagnosticIssue>[];
final fs = context.fileSystem;
final projectPath = context.projectPath;
final androidPath = fs.join(projectPath, 'android');
if (!fs.exists(androidPath) || !fs.isDirectory(androidPath)) {
return DiagnosticResult(
analyzerName: name,
status: CheckStatus.skipped,
issues: issues,
duration: DateTime.now().difference(startTime),
timestamp: DateTime.now(),
);
}
final googleServicesPath = fs.join(
fs.join(projectPath, 'android', 'app'),
'google-services.json',
);
Map<String, dynamic>? googleServicesConfig;
if (!fs.exists(googleServicesPath)) {
issues.add(
DiagnosticIssue(
severity: Severity.critical,
code: 'FD400',
title: 'Missing google-services.json',
description:
'The android/app/google-services.json file is missing. '
'This file is required for Firebase services on Android.',
recommendation:
'Run "flutterfire configure" to generate the '
'google-services.json file.',
filePath: googleServicesPath,
),
);
} else {
final content = fs.readAsString(googleServicesPath);
googleServicesConfig = _parseGoogleServicesJson(content);
if (googleServicesConfig == null) {
issues.add(
DiagnosticIssue(
severity: Severity.error,
code: 'FD401',
title: 'Invalid google-services.json',
description:
'The android/app/google-services.json file contains invalid JSON.',
recommendation:
'Regenerate the file using "flutterfire configure" '
'or fix the JSON syntax.',
filePath: googleServicesPath,
),
);
}
}
final appDir = fs.join(projectPath, 'android', 'app');
final appBuildGradlePath = fs.join(appDir, 'build.gradle');
final appBuildGradleKtsPath = fs.join(appDir, 'build.gradle.kts');
String? gradleContent;
String? gradlePath;
if (fs.exists(appBuildGradlePath)) {
gradleContent = fs.readAsString(appBuildGradlePath);
gradlePath = appBuildGradlePath;
} else if (fs.exists(appBuildGradleKtsPath)) {
gradleContent = fs.readAsString(appBuildGradleKtsPath);
gradlePath = appBuildGradleKtsPath;
}
String? expectedPackageName;
if (gradleContent != null) {
final gradleInfo = _parseBuildGradle(gradleContent);
if (!gradleInfo.plugins.contains('com.google.gms.google-services')) {
issues.add(
DiagnosticIssue(
severity: Severity.error,
code: 'FD403',
title: 'Missing google-services plugin',
description:
'The com.google.gms.google-services plugin is not applied '
'in the app-level build.gradle.',
recommendation:
'Add the plugin to your app-level build.gradle:\n'
' plugins {\n'
' id "com.google.gms.google-services" version "4.4.0"\n'
' }',
filePath: gradlePath,
),
);
}
if (gradleInfo.compileSdk != null && gradleInfo.compileSdk! < 34) {
issues.add(
DiagnosticIssue(
severity: Severity.warning,
code: 'FD407',
title: 'compileSdk version is below 34',
description:
'The compileSdk is set to ${gradleInfo.compileSdk}. '
'Version 34 or higher is recommended for latest Firebase SDKs.',
recommendation:
'Update compileSdk to 34 in your app-level build.gradle:\n'
' android {\n'
' compileSdk = 34\n'
' }',
filePath: gradlePath,
),
);
}
if (gradleInfo.minSdk != null && gradleInfo.minSdk! < 21) {
issues.add(
DiagnosticIssue(
severity: Severity.info,
code: 'FD408',
title: 'minSdk version is below 21',
description:
'The minSdk is set to ${gradleInfo.minSdk}. '
'firebase_core requires a minimum SDK version of 21.',
recommendation:
'Update minSdk to 21 in your app-level build.gradle:\n'
' defaultConfig {\n'
' minSdk = 21\n'
' }',
filePath: gradlePath,
),
);
}
if (gradleInfo.targetSdk != null && gradleInfo.targetSdk! < 34) {
issues.add(
DiagnosticIssue(
severity: Severity.warning,
code: 'FD409',
title: 'targetSdk version is below 34',
description:
'The targetSdk is set to ${gradleInfo.targetSdk}. '
'Version 34 or higher is recommended.',
recommendation:
'Update targetSdk to 34 in your app-level build.gradle:\n'
' defaultConfig {\n'
' targetSdk = 34\n'
' }',
filePath: gradlePath,
),
);
}
expectedPackageName = gradleInfo.applicationId;
}
if (googleServicesConfig != null &&
googleServicesConfig['package_name'] != null &&
expectedPackageName != null) {
final gsPackageName = googleServicesConfig['package_name'] as String;
if (gsPackageName != expectedPackageName) {
issues.add(
DiagnosticIssue(
severity: Severity.error,
code: 'FD402',
title: 'Package name mismatch in google-services.json',
description:
'The package name "$gsPackageName" in google-services.json '
'does not match the applicationId "$expectedPackageName" '
'in build.gradle.',
recommendation:
'Update the applicationId in build.gradle or regenerate '
'google-services.json with "flutterfire configure".',
filePath: googleServicesPath,
),
);
}
}
final manifestPath = fs.join(
fs.join(fs.join(projectPath, 'android', 'app'), 'src', 'main'),
'AndroidManifest.xml',
);
if (fs.exists(manifestPath)) {
final manifestContent = fs.readAsString(manifestPath);
final manifestInfo = _parseAndroidManifestXml(manifestContent);
if (!manifestInfo.permissions.contains('android.permission.INTERNET')) {
issues.add(
DiagnosticIssue(
severity: Severity.error,
code: 'FD404',
title: 'Missing INTERNET permission',
description:
'The INTERNET permission is not declared in '
'AndroidManifest.xml.',
recommendation:
'Add the INTERNET permission to your AndroidManifest.xml:\n'
' <uses-permission android:name="android.permission.INTERNET"/>',
filePath: manifestPath,
),
);
}
if (!manifestInfo.permissions.contains(
'android.permission.POST_NOTIFICATIONS',
)) {
issues.add(
DiagnosticIssue(
severity: Severity.warning,
code: 'FD405',
title: 'Missing POST_NOTIFICATIONS permission',
description:
'The POST_NOTIFICATIONS permission is not declared. '
'Required for Android 13+ (API level 33+) notification support.',
recommendation:
'Add the POST_NOTIFICATIONS permission to '
'your AndroidManifest.xml:\n'
' <uses-permission '
'android:name="android.permission.POST_NOTIFICATIONS"/>',
filePath: manifestPath,
),
);
}
if (!manifestInfo.permissions.contains('android.permission.WAKE_LOCK')) {
issues.add(
DiagnosticIssue(
severity: Severity.info,
code: 'FD406',
title: 'Missing WAKE_LOCK permission',
description:
'The WAKE_LOCK permission is not declared in '
'AndroidManifest.xml.',
recommendation:
'Add the WAKE_LOCK permission to your AndroidManifest.xml:\n'
' <uses-permission android:name="android.permission.WAKE_LOCK"/>',
filePath: manifestPath,
),
);
}
}
final hasCriticalOrError = issues.any(
(i) => i.severity == Severity.critical || i.severity == Severity.error,
);
final hasWarning = issues.any((i) => i.severity == Severity.warning);
CheckStatus status;
if (hasCriticalOrError) {
status = CheckStatus.failed;
} else if (hasWarning) {
status = CheckStatus.warning;
} else {
status = CheckStatus.passed;
}
return DiagnosticResult(
analyzerName: name,
status: status,
issues: issues,
duration: DateTime.now().difference(startTime),
timestamp: DateTime.now(),
);
}