Flutter安卓包管理插件android_package_manager的使用

发布于 1周前 作者 zlyuanteng 来自 Flutter

Flutter安卓包管理插件android_package_manager的使用

简介

android_package_manager 插件为 Flutter 应用提供了访问 Android 原生 PackageManager API 的能力,可以获取各种信息,如已安装的应用程序、包、权限等。

关于此插件

该插件仅适用于 Android。它提供了对 Android PackageManager API 最常用方法的更简单访问。部分方法改编自现有的 package_manager 插件,但该插件似乎最近没有维护。

开始使用

导入插件

import 'package:android_package_manager/android_package_manager.dart';

单例访问示例

final pm = AndroidPackageManager();

可用方法

请参阅官方文档 PackageManager API。

免责声明: 我尝试适应大多数可用的方法,并进行了一些简单的仪器测试。测试过的方法可以在 example/integration_test 目录下找到。

示例 Flutter 应用演示了 getInstalledPackages 方法的使用。

注意事项

请注意某些方法中的 flags 可选参数。这些参数会影响请求 API 的输出。例如,如果未指定 GET_PERMISSIONS 标志,则 PackageInfo 不会显示任何权限信息。

可选权限

默认情况下,从 Android 11(API 级别 30)开始,已安装应用列表是有限的。更多详细信息请参阅 这里。要访问设备上安装的所有应用的完整列表,请在 AndroidManifest.xml 文件中添加以下权限:

<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />

待办事项

  • 完善文档
  • 添加 DartDocs
  • 调整一些方法以兼容 Android Tiramisu(弃用一些方法)
  • 文档化位掩码值
  • 将接口分离到单独的包中(可能)

问题

如果您有任何问题或建议,请在此处提交 issues

示例代码

以下是一个完整的示例代码,展示了如何使用 android_package_manager 插件获取已安装的包信息并显示在 Flutter 应用中。

main.dart

import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:android_package_manager/android_package_manager.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MainPage(),
    );
  }
}

class MainPage extends StatefulWidget {
  const MainPage({Key? key}) : super(key: key);

  @override
  State<MainPage> createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  List<PackageInfo>? _applicationInfoList;

  @override
  void initState() {
    super.initState();
    final flags = PackageInfoFlags({
      PMFlag.getMetaData,
      PMFlag.getPermissions,
      PMFlag.getReceivers,
      PMFlag.getServices,
      PMFlag.getProviders,
    });
    _pm.getInstalledPackages(flags: flags).then(
      (value) => setState(() => _applicationInfoList = value),
    );
  }

  @override
  Widget build(BuildContext context) {
    final appInfo = _applicationInfoList;
    return Scaffold(
      appBar: AppBar(
        title: const Text("Android Package Manager Demo"),
      ),
      body: ListView.builder(
        padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 32.0),
        itemBuilder: (_, index) {
          final info = appInfo![index];
          return Padding(
            padding: const EdgeInsets.symmetric(vertical: 12.0),
            child: _PackageEntry(info: info),
          );
        },
        itemCount: appInfo?.length ?? 0,
      ),
    );
  }

  AndroidPackageManager get _pm => AndroidPackageManager();
}

class _Permissions extends StatelessWidget {
  const _Permissions({required this.permissions});

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Expanded(child: Text('Permissions (${permissions.length})')),
        TextButton(
          onPressed: () {
            showModalBottomSheet(
              context: context,
              builder: (_) => BottomSheet(
                onClosing: () {},
                builder: (_) {
                  return SizedBox(
                    height: MediaQuery.sizeOf(context).height * 0.5,
                    child: ListView.separated(
                      padding: const EdgeInsets.symmetric(vertical: 16),
                      itemBuilder: (_, i) {
                        return Padding(
                          padding: const EdgeInsets.symmetric(horizontal: 12),
                          child: Text('[${i + 1}] ${permissions[i]}'),
                        );
                      },
                      separatorBuilder: (_, __) => const Divider(height: 16),
                      itemCount: permissions.length,
                    ),
                  );
                },
              ),
            );
          },
          child: const Text(
            'Show',
            style: TextStyle(decoration: TextDecoration.underline),
          ),
        ),
      ],
    );
  }

  final List<String> permissions;
}

class _Receivers extends StatelessWidget {
  const _Receivers({required this.receivers});

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Expanded(child: Text('Receivers (${receivers.length})')),
        TextButton(
          onPressed: () {
            showModalBottomSheet(
              context: context,
              builder: (_) => BottomSheet(
                onClosing: () {},
                builder: (_) {
                  return SizedBox(
                    height: MediaQuery.sizeOf(context).height * 0.5,
                    child: ListView.separated(
                      padding: const EdgeInsets.symmetric(vertical: 16),
                      itemBuilder: (_, i) {
                        return Padding(
                          padding: const EdgeInsets.symmetric(horizontal: 12),
                          child: Text('[${i + 1}] ${receivers[i].name}'),
                        );
                      },
                      separatorBuilder: (_, __) => const Divider(height: 16),
                      itemCount: receivers.length,
                    ),
                  );
                },
              ),
            );
          },
          child: const Text(
            'Show',
            style: TextStyle(decoration: TextDecoration.underline),
          ),
        ),
      ],
    );
  }

  final List<ActivityInfo> receivers;
}

class _Services extends StatelessWidget {
  const _Services({required this.services});

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Expanded(child: Text('Services (${services.length})')),
        TextButton(
          onPressed: () {
            showModalBottomSheet(
              context: context,
              builder: (_) => BottomSheet(
                onClosing: () {},
                builder: (_) {
                  return SizedBox(
                    height: MediaQuery.sizeOf(context).height * 0.5,
                    child: ListView.separated(
                      padding: const EdgeInsets.symmetric(vertical: 16),
                      itemBuilder: (_, i) {
                        return Padding(
                          padding: const EdgeInsets.symmetric(horizontal: 12),
                          child: Text('[${i + 1}] ${services[i].name}'),
                        );
                      },
                      separatorBuilder: (_, __) => const Divider(height: 16),
                      itemCount: services.length,
                    ),
                  );
                },
              ),
            );
          },
          child: const Text(
            'Show',
            style: TextStyle(decoration: TextDecoration.underline),
          ),
        ),
      ],
    );
  }

  final List<ServiceInfo> services;
}

class _Providers extends StatelessWidget {
  const _Providers({required this.providers});

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Expanded(child: Text('Providers (${providers.length})')),
        TextButton(
          onPressed: () {
            showModalBottomSheet(
              context: context,
              builder: (_) => BottomSheet(
                onClosing: () {},
                builder: (_) {
                  return SizedBox(
                    height: MediaQuery.sizeOf(context).height * 0.5,
                    child: ListView.separated(
                      padding: const EdgeInsets.symmetric(vertical: 16),
                      itemBuilder: (_, i) {
                        return Padding(
                          padding: const EdgeInsets.symmetric(horizontal: 12),
                          child: Text('[${i + 1}] ${providers[i].name}'),
                        );
                      },
                      separatorBuilder: (_, __) => const Divider(height: 16),
                      itemCount: providers.length,
                    ),
                  );
                },
              ),
            );
          },
          child: const Text(
            'Show',
            style: TextStyle(decoration: TextDecoration.underline),
          ),
        ),
      ],
    );
  }

  final List<ProviderInfo> providers;
}

class _AppIcon extends StatelessWidget {
  const _AppIcon({required this.info});

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<Uint8List?>(
      future: info.applicationInfo?.getAppIcon(),
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          final iconBytes = snapshot.data!;
          return Image.memory(iconBytes, fit: BoxFit.fill);
        }
        if (snapshot.hasError) {
          return const Icon(Icons.error, color: Colors.red);
        }
        return const SizedBox.shrink();
      },
    );
  }

  final PackageInfo info;
}

class _AppLabel extends StatelessWidget {
  const _AppLabel({required this.info});

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<String?>(
      future: AndroidPackageManager().getApplicationLabel(packageName: info.packageName!),
      builder: (context, snapshot) => Text(snapshot.data ?? "No Name"),
    );
  }

  final PackageInfo info;
}

class _PackageEntry extends StatelessWidget {
  const _PackageEntry({required this.info});

  @override
  Widget build(BuildContext context) {
    return Card(
      child: ListTile(
        leading: SizedBox.square(dimension: 48.0, child: _AppIcon(info: info)),
        title: _AppLabel(info: info),
        subtitle: Padding(
          padding: const EdgeInsets.only(top: 8.0),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text('${info.packageName} (${info.longVersionCode ?? info.versionCode})'),
              Builder(
                builder: (_) {
                  final permissions = info.requestedPermissions;
                  if (permissions == null || permissions.isEmpty) {
                    return const SizedBox.shrink();
                  }
                  return _Permissions(permissions: permissions);
                },
              ),
              Builder(
                builder: (_) {
                  final receivers = info.receivers;
                  if (receivers == null || receivers.isEmpty) {
                    return const SizedBox.shrink();
                  }
                  return _Receivers(receivers: receivers);
                },
              ),
              Builder(
                builder: (_) {
                  final services = info.services;
                  if (services == null || services.isEmpty) {
                    return const SizedBox.shrink();
                  }
                  return _Services(services: services);
                },
              ),
              Builder(
                builder: (_) {
                  final providers = info.providers;
                  if (providers == null || providers.isEmpty) {
                    return const SizedBox.shrink();
                  }
                  return _Providers(providers: providers);
                },
              ),
            ],
          ),
        ),
      ),
    );
  }

  final PackageInfo info;
}

以上代码展示了如何使用 android_package_manager 插件获取已安装的包信息,并在 Flutter 应用中显示这些信息。希望这对您有所帮助!


更多关于Flutter安卓包管理插件android_package_manager的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter安卓包管理插件android_package_manager的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用android_package_manager插件的示例代码案例。android_package_manager插件允许你在Flutter应用中管理和查询已安装的Android应用包信息。

首先,确保你已经在pubspec.yaml文件中添加了android_package_manager依赖:

dependencies:
  flutter:
    sdk: flutter
  android_package_manager: ^x.y.z  # 请替换为最新版本号

然后运行flutter pub get来安装依赖。

接下来,你需要处理Android平台的特定权限配置。在你的android/app/src/main/AndroidManifest.xml文件中,添加以下权限(如果需要访问敏感信息):

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.yourapp">

    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>

    <!-- 其他配置 -->

</manifest>

注意:PACKAGE_USAGE_STATS权限需要用户手动授予,因此你可能需要在应用中引导用户前往设置页面授予权限。

现在,你可以在你的Flutter代码中使用android_package_manager插件。以下是一个简单的示例,展示如何获取已安装应用列表:

import 'package:flutter/material.dart';
import 'package:android_package_manager/android_package_manager.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<PackageInfo> _installedPackages = [];

  @override
  void initState() {
    super.initState();
    _getInstalledPackages();
  }

  Future<void> _getInstalledPackages() async {
    try {
      final packageManager = AndroidPackageManager();
      List<PackageInfo> packages = await packageManager.getAllPackages();
      setState(() {
        _installedPackages = packages;
      });
    } catch (e) {
      print("Error getting installed packages: $e");
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Installed Packages'),
        ),
        body: ListView.builder(
          itemCount: _installedPackages.length,
          itemBuilder: (context, index) {
            final package = _installedPackages[index];
            return ListTile(
              title: Text(package.packageName),
              subtitle: Text(package.label),
            );
          },
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个Flutter应用,该应用在启动时获取所有已安装的应用包信息,并将它们显示在一个列表中。每个列表项显示应用的包名和标签(即应用的显示名称)。

请注意,AndroidPackageManager类的具体方法和属性可能会随着插件版本的更新而变化,因此请参考最新的插件文档以获取准确的信息。

此外,由于PACKAGE_USAGE_STATS权限的敏感性,在实际应用中处理权限请求时,应遵循最佳实践,确保用户体验和隐私安全。

回到顶部