This code will support up Upto Android 11+.
Declare a permission result on Fragment / Activity
I am using a fragment
private val askPermissions =
    registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
        val isGranted = permissions.entries.all {
            it.value == true
        }
        if (isGranted) {
            viewModel.saveImageToGallery(requireContext().contentResolver,
                getString(R.string.my_deshi_qr_code),
                bitmap)
        } else {
            askForWritePermission()
        }
    }
Trigger event
bindingView.downloadQrButton.setOnClickListener {
        requestPermission()
    }
private fun requestPermission() {
    val minSDK = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
    val isWritePermissionGranted = (ContextCompat.checkSelfPermission(requireContext(),
        Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) || minSDK
    if (!isWritePermissionGranted) {
        askForWritePermission()
    } else {
        viewModel.saveImageToGallery(requireContext().contentResolver,
            getString(R.string.my_deshi_qr_code),
            bitmap)
    }
}
private fun askForWritePermission() {
    askPermissions.launch(listOf(Manifest.permission.WRITE_EXTERNAL_STORAGE).toTypedArray())
}
Viewmodel
fun saveImageToGallery(contentResolver: ContentResolver, imageName: String, bitmap: Bitmap?) {
    val imageUri: Uri?
    val contentValues = ContentValues().apply {
        put(MediaStore.MediaColumns.DISPLAY_NAME, "$imageName.jpg")
        put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
        bitmap?.let {
            put(MediaStore.Images.Media.WIDTH, bitmap.width)
            put(MediaStore.Images.Media.HEIGHT, bitmap.height)
        }
    }
    imageUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH,
            Environment.DIRECTORY_PICTURES + File.separator.toString() + "YourFolderName")
        MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
    } else {
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    }
    try {
        val uri = contentResolver.insert(imageUri, contentValues)
        val fos = uri?.let { contentResolver.openOutputStream(it) }
        bitmap?.compress(Bitmap.CompressFormat.JPEG, 100, fos)
        Objects.requireNonNull(fos)
        _showMessage.postValue(Event("Image Saved"))
    } catch (e: Exception) {
        _showMessage.postValue(Event("Image Not Saved \n$e"))
    }
}