Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parse error on installing apk in flutter programmatically

I want to make an app with flutter which can install other apks. When I try to install apks using the flutter_app_installer or app_installer packages I get a pop-up on the phone saying: "An error occurred while parsing the package."

Usually this error message means the .apk file is damaged. However, using the file explorer on android I can install the apk just fine.

Furthermore, there could be a permission problem. My App asks for Storage permission and permission to install apks, I granted those.

I've tried different flutter-packages for installing apks, as mentioned above. Both throw the same error message.

When I provide a wrong path to the apk on purpose I dont get an error message. I did that to verify that the path to the apk is correct.

I tried running this on two android devices.

In the flutter debug console is no error message shown, only on the phone.

Here the error message (on a german phone): Error message shown on phone

I tried googling and asking chattie, cant find any helpful advice. I am stuck at this point for a while.

This is my flutter code:

import 'dart:io';

import 'package:app_installer/app_installer.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'package:path/path.dart' as path;
import 'package:flutter_app_installer/flutter_app_installer.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Request runtime permission
  await Permission.storage.request();
  await Permission.requestInstallPackages.request();

  runApp(const MainApp());
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: TextButton(
            onPressed: () async {
              await AppInstaller.installApk(
                "storage/emulated/0/Download/test.apk",
              );
            },
            child: const Text('Install App'),
          ),
        ),
      ),
    );
  }
}

This is my AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.apk_install_test">
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
    <application
        android:label="apk_install_test"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
              />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <!-- Provider -->
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.fileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
</manifest>
like image 830
ostue Avatar asked Sep 14 '26 20:09

ostue


1 Answers

after 3 days of pain I made it :)

Here is what I did: First of all, I tried every flutter plugin that installs apks, none worked. Then I switched to an intend-based approach, which had even more problems. The open_file plugin is another way, but I didnt get that right either.

So I had to dig deeper. In the newer android versions, storage permissions are much stricter. Therefore most file IO is not possible with basic permissions. I had to request MANAGE_EXTERNAL_STORAGE using Permission.manageExternalStorage.request()

Only with that permission I was able to access the Downloads folder. Of course, you have to add the downloads folder to your file_paths.xml like this: <external-path name="external_files_download" path="Download" />

My App wasnt meant to install apks from the downloads folder but apks downloaded by the app directly, which are stored in the apps own data folder. This data folder is a protected folder as I learned, which forbids other apps (like apk-installers) from reading the file.

I had to write this function to copy apks from internal to external storage. Only after that I could access it and install it:

/// Copies the given file to the external storage directory and returns the new file
/// Needed, because the app can only install apks from the external storage directory
Future<File> copyFileToExternalStorage(File sourceFile) async {
  String fileName = sourceFile.path.split('/').last; // Extract the file name
  Directory? externalDir = await getExternalStorageDirectory(); // Get external directory

  if (externalDir == null) {
    throw Exception("External storage directory not found");
  }

  File destinationFile = File('${externalDir.path}/$fileName'); // Create new file at destination path

  await sourceFile.copy(destinationFile.path); // Copy source file to destination

  return destinationFile; // Return the new file object
}

With these changes I could use the apk installer plugins from pub.dev. It is important to notice, that different plugins reqiure a different provider name in AndroidManifest.xml So remember to name it, as stated in the plugin documentation.

like image 180
ostue Avatar answered Sep 16 '26 11:09

ostue



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!