updateDarkStylesXml function
Generates dark mode splash screen XML styles for both pre-Android 12 and Android 12+ devices.
This function manages two separate dark mode configurations to support platform-specific
styling requirements. It creates values-night/styles.xml for pre-Android 12 devices and
values-night-v31/styles.xml for Android 12+ devices with their respective styling.
Implementation
Future<void> updateDarkStylesXml({
YamlMap? android12AndAbove,
String? darkColor,
String? darkImage,
String? darkBrandingImage,
String? darkBackgroundImageSource,
}) async {
// Pre-Android 12 dark styles live in values-night/styles.xml, separate from values-night-v31.
final hasPreAndroid12DarkConfig = darkImage != null ||
darkBackgroundImageSource != null ||
darkColor != null;
if (!hasPreAndroid12DarkConfig && android12AndAbove == null) {
// No dark configuration provided. Ensure any previously generated
// dark-style files are removed so they don't reference missing drawables.
try {
const preAndroid12Path =
'${CmdStrings.androidDarkValuesDirectory}/${AndroidStrings.stylesXml}';
final preFile = File(preAndroid12Path);
if (await preFile.exists()) {
await preFile.delete();
log('Removed $preAndroid12Path because no dark config provided.');
}
const v31Path =
'${CmdStrings.androidDarkValuesV31Directory}/${AndroidStrings.stylesXml}';
final v31File = File(v31Path);
if (await v31File.exists()) {
await v31File.delete();
log('Removed $v31Path because no dark config provided.');
}
} catch (e) {
log('Error removing old dark styles: $e');
}
log('Skipping dark styles update: no dark configuration provided.');
return;
}
// Handle Android 12+ dark styles
if (android12AndAbove != null) {
final android12NativeDarkImage =
android12AndAbove[YamlKeys.imageDarkKey] as String?;
const v31 = CmdStrings.androidDarkValuesV31Directory;
await Directory(v31).create(recursive: true);
const styleV31Path = '$v31/${AndroidStrings.stylesXml}';
final styleFileV31 = File(styleV31Path);
if (await styleFileV31.exists()) {
await styleFileV31.delete();
}
await createAndroid12Styles(
styleFile: styleFileV31,
color: android12AndAbove[YamlKeys.colorDarkKey] as String?,
imageSource: android12NativeDarkImage ??
android12AndAbove[YamlKeys.imageKey] as String?,
brandingImageSource: darkBrandingImage ??
android12AndAbove[YamlKeys.brandingImageKey] as String?,
isDarkBranding: darkBrandingImage != null,
isDarkImage: android12NativeDarkImage != null,
);
}
// Handle pre-Android 12 dark styles.
// If no pre-Android 12 dark configuration is provided, ensure any
// existing `values-night/styles.xml` doesn't reference the missing
// dark drawable. Prefer updating it to reference the light drawable so
// builds don't fail.
if (!hasPreAndroid12DarkConfig) {
const androidValuesFolder = CmdStrings.androidDarkValuesDirectory;
const xmlPath = '$androidValuesFolder/${AndroidStrings.stylesXml}';
final xmlFile = File(xmlPath);
if (!await xmlFile.exists()) {
log('Pre-Android 12 values-night/styles.xml not found. Skipping update.');
return;
}
try {
final xmlDoc = XmlDocument.parse(xmlFile.readAsStringSync());
for (final itemElement
in xmlDoc.findAllElements(AndroidStrings.itemElement)) {
if (itemElement.getAttribute(AndroidStrings.nameAttr) ==
AndroidStrings.itemNameAttrValue) {
// Point night styles to the light splash drawable instead of the
// missing dark drawable so resource linking succeeds.
itemElement.innerText = AndroidStrings.drawableSplashScreen;
}
}
await xmlFile.writeAsString(xmlDoc.toXmlString(pretty: true));
log('Pre-Android 12 values-night/styles.xml updated to reference light drawable.');
} catch (e) {
log('Error updating values-night/styles.xml: $e');
}
return;
}
// If we have pre-Android 12 dark configuration, create or update
// values-night/styles.xml to reference the dark drawable.
else {
// Read parent style from existing light styles.xml so we don't hardcode it
final lightStylesFile = File(
'${CmdStrings.androidValuesDirectory}/${AndroidStrings.stylesXml}');
final lightDoc = XmlDocument.parse(lightStylesFile.readAsStringSync());
final parentAttr = lightDoc
.findAllElements(AndroidStrings.styleElement)
.firstWhere((e) =>
e.getAttribute(AndroidStrings.nameAttr) ==
AndroidStrings.styleNameAttrVal)
.getAttribute(AndroidStrings.styleParentAttr) ??
'';
const nightDir = CmdStrings.androidDarkValuesDirectory;
await Directory(nightDir).create(recursive: true);
final nightFile = File('$nightDir/${AndroidStrings.stylesXml}');
if (await nightFile.exists()) await nightFile.delete();
final builder = XmlBuilder();
builder.processing(AndroidStrings.xml, AndroidStrings.xmlVersion);
builder.element(AndroidStrings.resourcesElement, nest: () {
builder.element(AndroidStrings.styleElement, attributes: {
AndroidStrings.nameAttr: AndroidStrings.styleNameAttrVal,
AndroidStrings.styleParentAttr: parentAttr,
}, nest: () {
builder.element(
AndroidStrings.itemElement,
attributes: {
AndroidStrings.nameAttr: AndroidStrings.itemNameAttrValue
},
nest: AndroidStrings.androidDarkDrawable,
);
});
});
await nightFile
.writeAsString(builder.buildDocument().toXmlString(pretty: true));
log('Created values-night/styles.xml referencing dark drawable.');
}
}