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

Flutter运行报错:type 'Null' is not a subtype of type 'String' in type cast

Flutter运行报错:type 'Null' is not a subtype of type 'String' in type cast

报错信息

I/flutter ( 8093): type 'Null' is not a subtype of type 'String' in type cast
I/flutter ( 8093): #0      new Future.value (dart:async/future.dart:349:59)
I/flutter ( 8093): #1      ProxySettings.findProxy (package:Myapp/plugins/proxy_settings.dart:14:21)
I/flutter ( 8093): #2      MyAppProviderState.initAsyncState (package:Myapp/provider.dart:215:39)
I/flutter ( 8093): <asynchronous suspension>
I/flutter ( 8093): In dev mode. Not sending report to Sentry.io.
I/flutter ( 8093): Clearing prompted user for permission

相关代码及配置

provider.dart(第215行对应报错位置)

void initAsyncState() async {
    Environment environment =
        await EnvironmentLoader(environmentPath: ".env.json").load();
    httpService.setMagicHeader(
        environment.magicHeaderName, environment.magicHeaderValue);
    httpService
        .setProxy(await ProxySettings.findProxy(Uri.parse(environment.apiUrl)));
}

environment_loader.dart

import 'dart:async' show Future;
import 'dart:convert' show json;
import 'package:flutter/services.dart' show rootBundle;

class EnvironmentLoader {
  final String? environmentPath;

  EnvironmentLoader({this.environmentPath});

  Future<Environment> load() {
    return rootBundle.loadStructuredData<Environment>(this.environmentPath!,
        (jsonStr) async {
      final environmentLoader = Environment.fromJson(json.decode(jsonStr));
      return environmentLoader;
    });
  }
}

class Environment {
  final String apiUrl;
  final String magicHeaderName;
  final String magicHeaderValue;
  final String sentryDsn;
  final String myappapiurl;
  final String linkPreviewsTrustedProxyUrl;

  const Environment(
  {
    this.sentryDsn = '',
    this.myappapiurl = '',
    this.apiUrl = '',
    this.magicHeaderName = '',
    this.magicHeaderValue = '',
    this.linkPreviewsTrustedProxyUrl = '',
  });

  factory Environment.fromJson(Map<String, dynamic> jsonMap) {
    return new Environment(
      apiUrl: jsonMap["API_URL"],
      magicHeaderName: jsonMap["MAGIC_HEADER_NAME"],
      magicHeaderValue: jsonMap["MAGIC_HEADER_VALUE"],
      sentryDsn: jsonMap["SENTRY_DSN"],
      myappapiurl: jsonMap["KOSMOSNETWORK_SOCIAL_API_URL"],
      linkPreviewsTrustedProxyUrl: jsonMap["LINK_PREVIEWS_TRUSTED_PROXY_URL"],
    );
  }
}

.env.json

{
  "API_URL": "http://'mydomain':80/",
  "MAGIC_HEADER_NAME": "",
  "MAGIC_HEADER_VALUE": "",
  "SENTRY_DSN": "",
  "APP_SOCIAL_API_URL": "http://'mydomain':80/",
  "LINK_PREVIEWS_TRUSTED_PROXY_URL": "http://'mydomain':80/"
}

AndroidManifest.xml(相关配置)

<intent-filter android:autoVerify="true">
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data
                        android:scheme="http"
                        android:host="mydomain"
                        android:pathPrefix="/api/auth/"/>
            </intent-filter>
            <intent-filter android:autoVerify="true">
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data
                        android:scheme="https"
                        android:host="mydomain"
                        android:pathPrefix="/api/auth/"/>

问题分析与解决方法

核心问题

  1. 环境变量键名不匹配:Environment.fromJson中读取myappapiurl用的键是KOSMOSNETWORK_SOCIAL_API_URL,但.env.json里对应的键是APP_SOCIAL_API_URL,导致该字段被赋值为null,触发非空String类型的转换错误。
  2. 未处理null值兜底:从JSON读取字段时直接赋值,没有处理键缺失或值为null的情况,违反了Dart的空安全规则。
  3. ProxySettings.findProxy内部逻辑问题:报错堆栈显示该方法第14行将null当作String处理,导致Future.value触发类型转换异常。

修复步骤

1. 修正环境变量键名

修改.env.json中的键名,与代码保持一致,同时清理URL中的多余单引号:

{
  "API_URL": "http://mydomain:80/",
  "MAGIC_HEADER_NAME": "",
  "MAGIC_HEADER_VALUE": "",
  "SENTRY_DSN": "",
  "KOSMOSNETWORK_SOCIAL_API_URL": "http://mydomain:80/",
  "LINK_PREVIEWS_TRUSTED_PROXY_URL": "http://mydomain:80/"
}

2. 给环境变量添加null安全兜底

修改Environment.fromJson方法,用空值合并运算符??确保字段不会为null:

factory Environment.fromJson(Map<String, dynamic> jsonMap) {
  return Environment(
    apiUrl: jsonMap["API_URL"] ?? "",
    magicHeaderName: jsonMap["MAGIC_HEADER_NAME"] ?? "",
    magicHeaderValue: jsonMap["MAGIC_HEADER_VALUE"] ?? "",
    sentryDsn: jsonMap["SENTRY_DSN"] ?? "",
    myappapiurl: jsonMap["KOSMOSNETWORK_SOCIAL_API_URL"] ?? "",
    linkPreviewsTrustedProxyUrl: jsonMap["LINK_PREVIEWS_TRUSTED_PROXY_URL"] ?? "",
  );
}

3. 修复ProxySettings.findProxy的空值问题

检查proxy_settings.dart第14行代码,确保返回值不为null,示例修正:

static Future<String> findProxy(Uri uri) async {
  String? proxyConfig = await getProxyConfig(); // 假设原方法可能返回null
  return proxyConfig ?? ""; // 兜底返回空字符串
}

4. 增加URL合法性校验

避免非法URL导致的解析错误:

void initAsyncState() async {
  Environment environment =
      await EnvironmentLoader(environmentPath: ".env.json").load();
  httpService.setMagicHeader(
      environment.magicHeaderName, environment.magicHeaderValue);
  
  final uri = Uri.tryParse(environment.apiUrl);
  if (uri != null) {
    httpService.setProxy(await ProxySettings.findProxy(uri));
  } else {
    httpService.setProxy(""); // 非法URL时使用默认值
  }
}

内容的提问来源于stack exchange,提问作者Ananth

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 13:15:54