You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter Android端出现PathNotFoundException:无法找到证书文件

问题原因

Flutter在iOS和Android平台对assets资源的处理逻辑不同:

  • iOS会将assets映射为可直接通过File访问的文件路径;
  • Android则将assets打包在APK内部,并非文件系统中的真实文件,无法用dart:io的File类直接读取,这就是Android端报错PathNotFoundException的核心原因。
修复方案

需要改用Flutter官方提供的rootBundle加载assets资源,同时调整初始化逻辑适配异步加载(因为rootBundle的资源加载是异步操作)。

方案一:使用异步工厂构造函数

修改UserServiceHandler的实现,通过工厂方法异步加载证书并创建实例:

import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:grpc/grpc.dart';
import 'package:injectable/injectable.dart';
import 'package:test/src/core/secure/secure_storage_service.dart';
import 'package:test/src/features/auth/data/interceptor/auth_interceptor.dart';
import '../../../../generated/user.pbgrpc.dart';

@Injectable(as: UserServiceClient)
class UserServiceHandler extends UserServiceClient {
  UserServiceHandler._(ClientChannel channel, SecureStorageService storageService)
      : super(
            channel,
            interceptors: [AuthInterceptor(storageService)],
            options: const CallOptions(timeout: Duration(seconds: 30)),
          );

  static Future<UserServiceHandler> create(SecureStorageService storageService) async {
    // 通过rootBundle加载证书文件
    final ByteData certData = await rootBundle.load('assets/certs/ca_bundle.crt');
    final List<int> certBytes = certData.buffer.asUint8List();

    final channel = ClientChannel(
      _host,
      port: 50051,
      options: ChannelOptions(
        credentials: ChannelCredentials.secure(certificates: certBytes),
      ),
    );

    return UserServiceHandler._(channel, storageService);
  }

  static String get _host => 'localhost';
}

之后在Injectable的依赖配置中,指定使用create方法创建该类的实例(可通过@factoryMethod注解或手动修改injectable.config.dart实现)。

方案二:App启动时提前加载证书

在App初始化阶段提前加载证书字节,再传入UserServiceHandler的构造函数:

// main.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get_it/get_it.dart';
import 'package:test/src/core/secure/secure_storage_service.dart';
import 'package:test/src/features/auth/data/handler/user_service_handler.dart';

final getIt = GetIt.instance;

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

  // 提前加载证书资源
  final ByteData certData = await rootBundle.load('assets/certs/ca_bundle.crt');
  final List<int> certBytes = certData.buffer.asUint8List();

  // 注册依赖时传入证书字节
  getIt.registerSingleton<UserServiceHandler>(
    UserServiceHandler(getIt<SecureStorageService>(), certBytes),
  );

  runApp(const MyApp());
}

对应的UserServiceHandler构造函数修改:

@Injectable()
class UserServiceHandler extends UserServiceClient {
  UserServiceHandler(SecureStorageService storageService, List<int> certBytes)
      : super(
            ClientChannel(
              _host,
              port: 50051,
              options: ChannelOptions(
                credentials: ChannelCredentials.secure(certificates: certBytes),
              ),
            ),
            interceptors: [AuthInterceptor(storageService)],
            options: const CallOptions(timeout: Duration(seconds: 30)),
          );

  static String get _host => 'localhost';
}
额外检查项
  1. 确认pubspec.yaml的assets配置无误:- assets/certs/会自动包含该目录下的所有文件,无需单独声明单个文件;
  2. 检查文件名大小写:Android系统严格区分文件名大小写,确保代码中的路径assets/certs/ca_bundle.crt与实际文件的大小写完全一致。

内容的提问来源于stack exchange,提问作者Адильжан Омаркан

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 22:07:26