Flutter应用包安装管理插件package_installer_plus的使用

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

Flutter应用包安装管理插件package_installer_plus的使用

package_installer_plus 是一个用于在 Android 应用中安装 APK 的 Flutter 插件。

使用方法

  1. pubspec.yaml 文件中添加依赖:
dependencies:
  package_installer_plus: ^1.0.0
  1. 运行 flutter pub get 以获取并安装该依赖。

Android 配置

无需进行额外配置。

示例代码

以下是一个完整的示例代码,展示了如何使用 package_installer_plus 插件从网络下载 APK 并进行安装。

import 'dart:io';
import 'dart:typed_data';
import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:http/http.dart' as http;
import 'package:package_installer_plus/package_installer_plus.dart';

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

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final GlobalKey<FormState> _formKey = GlobalKey();
  final TextEditingController _urlController = TextEditingController();
  final _packageInstallerPlusPlugin = PackageInstallerPlus();
  bool _isDownloading = false;
  Timer? _logTimer;
  double _percentage = 0;

  [@override](/user/override)
  void initState() {
    super.initState();
  }

  [@override](/user/override)
  void dispose() {
    _urlController.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('安装 APK'),
        ),
        body: SingleChildScrollView(
          child: Container(
            decoration: BoxDecoration(
              color: Colors.blue.withAlpha(10),
              borderRadius: BorderRadius.circular(30),
            ),
            padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16),
            margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 16),
            child: Form(
              key: _formKey,
              child: Column(
                children: [
                  const SizedBox(height: 24),
                  TextFormField(
                    controller: _urlController,
                    style: const TextStyle(
                      fontSize: 12,
                      fontWeight: FontWeight.w500,
                    ),
                    validator: (value) {
                      if (value == null || value.isEmpty) {
                        return '请输入 URL';
                      }
                      return null;
                    },
                    minLines: 4,
                    maxLines: 15,
                    keyboardType: TextInputType.multiline,
                    textInputAction: TextInputAction.done,
                    readOnly: _isDownloading,
                    decoration: InputDecoration(
                      fillColor: Colors.white,
                      hintText: 'APK 下载 URL',
                      hintStyle: const TextStyle(
                        color: Colors.black26,
                        fontSize: 12,
                        fontWeight: FontWeight.w400,
                      ),
                      prefixIcon: Icon(Icons.link_rounded, color: Colors.green.shade800, size: 18),
                      contentPadding: EdgeInsets.zero,
                      border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(10),
                        borderSide: const BorderSide(color: Colors.black38),
                      ),
                      enabledBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(10),
                        borderSide: const BorderSide(color: Colors.black38),
                      ),
                      focusedBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(10),
                        borderSide: const BorderSide(color: Colors.blueAccent, width: 1.5),
                      ),
                      errorBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(10),
                        borderSide: BorderSide(color: Colors.red.shade800),
                      ),
                      focusedErrorBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(10),
                        borderSide: BorderSide(color: Colors.red.shade800, width: 1.5),
                      ),
                    ),
                    onFieldSubmitted: (val) => _downloadApk(),
                  ),
                  const SizedBox(height: 12),
                  Opacity(
                    opacity: _isDownloading ? 0.6 : 1,
                    child: ElevatedButton(
                      onPressed: _isDownloading ? null : () => _downloadApk(),
                      style: ButtonStyle(
                        backgroundColor: MaterialStateProperty.all(Colors.green.shade900),
                        minimumSize: const MaterialStatePropertyAll(Size(100, 35)),
                      ),
                      child: const Text(
                        '安装',
                        style: TextStyle(
                          color: Colors.white,
                          fontSize: 13,
                        ),
                      ),
                    ),
                  ),
                  const SizedBox(height: 12),
                  Visibility(
                    visible: _isDownloading,
                    maintainAnimation: true,
                    maintainSize: false,
                    maintainState: true,
                    child: Row(
                      children: [
                        Expanded(
                          child: LinearProgressIndicator(
                            color: Colors.green.shade800,
                            value: _percentage,
                          ),
                        ),
                        const SizedBox(width: 8),
                        Text('${(_percentage * 100).round()} %',
                          style: const TextStyle(
                            fontSize: 10,
                          ),
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }

  // 异步下载 APK 并安装
  Future<void> _downloadApk() async {
    FocusScope.of(context).unfocus();
    if (_formKey.currentState!.validate()) {
      try {
        if (_logTimer != null && _logTimer!.isActive) {
          _logTimer!.cancel();
        }
        _percentage = 0;
        _logTimer = Timer.periodic(const Duration(seconds: 1), (timer) async {
          if (_percentage < 0.9) {
            setState(() => _percentage = double.parse((_percentage + 0.1).toStringAsFixed(1)));
          }
        });

        setState(() => _isDownloading = true);
        String? filePath = await _downloadAndCreateFile(_urlController.text);
        log('文件路径 : $filePath');
        _logTimer?.cancel();
        setState(() => _percentage = 1);
        await Future.delayed(const Duration(seconds: 1));
        if (filePath != null) {
          bool res = await _packageInstallerPlusPlugin.installApk(filePath: filePath);
          log('安装 APK 结果 : $res');
        }
      } catch (e) {
        log('异常 : $e');
      }
      setState(() => _isDownloading = false);
    }
  }

  // 下载文件并创建文件
  Future<String?> _downloadAndCreateFile(String fileUrl) async {
    Uint8List? res = await _downloadFile(fileUrl);

    if (res != null) {
      Directory folder = await getTemporaryDirectory();
      if (!await folder.exists()) {
        await folder.create();
      }

      File file = File('${folder.path}${Platform.pathSeparator}update.apk')
        ..createSync(recursive: true);
      await file.writeAsBytes(res, flush: true);

      return file.path;
    }
    return null;
  }

  // 下载文件
  Future<Uint8List?> _downloadFile(String fileUrl) async {
    log('开始下载文件');
    Uint8List? returnVal;
    await http.get(
      Uri.parse(fileUrl),
    ).then((response) async {
      log('下载文件结果 : 状态码 ${response.statusCode}, 数据长度 ${response.body.length}');
      if (response.statusCode == 200) {
        returnVal = response.bodyBytes;
      } else {
        returnVal = null;
      }
    }).catchError((error) {
      log('下载文件错误 : ${error.toString()}');
      returnVal = null;
    });
    log('结束下载文件');
    return returnVal;
  }
}

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

1 回复

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


当然,以下是如何在Flutter应用中使用package_installer_plus插件来进行应用包安装管理的示例代码。这个插件允许你在Flutter应用中触发APK文件的安装过程。

首先,你需要在你的pubspec.yaml文件中添加package_installer_plus依赖:

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

然后,运行flutter pub get来获取依赖。

接下来,在你的Flutter应用中,你可以按照以下步骤使用package_installer_plus插件:

  1. 导入插件
import 'package:package_installer_plus/package_installer_plus.dart';
  1. 请求安装权限(在Android上可能需要):

由于安装APK文件可能需要特定的权限,建议在安装前检查并请求这些权限。以下是一个简单的权限请求示例:

import 'package:permission_handler/permission_handler.dart';

Future<void> requestInstallPermissions() async {
  var status = await Permission.installPackages.status;
  if (!status.isGranted) {
    var result = await Permission.installPackages.request();
    if (result.isGranted) {
      print("Install Packages permission granted.");
    } else {
      print("Install Packages permission denied.");
    }
  } else {
    print("Install Packages permission is already granted.");
  }
}

注意:你需要添加permission_handler依赖来处理权限请求。

  1. 安装APK文件

使用PackageInstallerPlus类来安装APK文件。假设你有一个APK文件的路径,你可以这样调用安装方法:

Future<void> installApk(String apkFilePath) async {
  try {
    bool result = await PackageInstallerPlus.install(apkFilePath);
    if (result) {
      print("APK installation succeeded.");
    } else {
      print("APK installation failed.");
    }
  } catch (e) {
    print("Error during APK installation: $e");
  }
}
  1. 完整示例

以下是一个完整的示例,将权限请求和APK安装结合起来:

import 'package:flutter/material.dart';
import 'package:package_installer_plus/package_installer_plus.dart';
import 'package:permission_handler/permission_handler.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('APK Installer Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () async {
              // 请求安装权限
              await requestInstallPermissions();

              // APK文件路径(请替换为你的APK文件路径)
              String apkFilePath = "/path/to/your/app.apk";

              // 安装APK
              await installApk(apkFilePath);
            },
            child: Text('Install APK'),
          ),
        ),
      ),
    );
  }
}

Future<void> requestInstallPermissions() async {
  var status = await Permission.installPackages.status;
  if (!status.isGranted) {
    var result = await Permission.installPackages.request();
    if (!result.isGranted) {
      // 处理权限被拒绝的情况
      throw Exception("Install Packages permission is required to install APK.");
    }
  }
}

Future<void> installApk(String apkFilePath) async {
  try {
    bool result = await PackageInstallerPlus.install(apkFilePath);
    if (result) {
      // 安装成功后的处理
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('APK installed successfully')));
    } else {
      // 安装失败后的处理
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('APK installation failed')));
    }
  } catch (e) {
    // 错误处理
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error: $e')));
  }
}

注意

  • 在实际使用中,请确保APK文件路径是正确的。
  • 在Android上,你可能需要在AndroidManifest.xml中添加相关权限声明,尽管package_installer_plus插件通常已经处理了这些权限。
  • iOS上不支持直接从应用中安装APK文件,这个插件主要用于Android平台。

这个示例展示了如何使用package_installer_plus插件来请求安装权限并安装APK文件。希望这对你有所帮助!

回到顶部