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

如何修复Flutter中CachedNetworkImage的“No Host Specified Url”错误?

修复CachedNetworkImage加载空URL时的ArgumentError异常

问题描述

使用CachedNetworkImage从Firebase加载用户头像,期望当profilePhoto URL为null时显示带图标的CircleAvatar,但运行时抛出ArgumentError (Invalid argument(s): No host specified in URI )异常,相关代码如下:

class ProfileScreen extends StatefulWidget {
final String uid;
  const ProfileScreen({
  Key? key,
 required this.uid,
}) : super(key: key);

@override
State<ProfileScreen> createState() => _ProfileScreenState();
}

class _ProfileScreenState extends State<ProfileScreen> {
final ProfileController profileController = Get.put(ProfileController());

@override
void initState() {
    super.initState();
    profileController.updateUserId(widget.uid);
}
@override
Widget build(BuildContext context) {
    return GetBuilder<ProfileController>(
        init: ProfileController(),
        builder: (controller) {
          if (controller.user.isEmpty) {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }
          return Scaffold(
            appBar: AppBar(
              backgroundColor: Colors.black12,
              leading: const Icon(
                Icons.person_add_alt_1_outlined,
              ),
              actions: const [
                Icon(Icons.more_horiz),
              ],
              title: Text(
                controller.user['name']??"",
                style: const TextStyle(
                  fontWeight: FontWeight.bold,
                  color: Colors.white,
                ),
              ),
            ),
            body: SafeArea(
              child: SingleChildScrollView(
                child: Column(
                  children: [
                    SizedBox(
                      child: Column(
                        children: [
                          Row(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                              ClipOval(
                                child: 
                                CachedNetworkImage(
                                  fit: BoxFit.cover,
                                  imageUrl: controller.user['profilePhoto']??"",
                                  height: 100,
                                  width: 100,
                                  placeholder: (context, url) =>
                                    const CircularProgressIndicator(),
                                  errorWidget: (context, url, error) =>
                                      const Icon(
                                    Icons.error,
                                  ),
                                ),
                              )
                            ],
                          ),
                          const SizedBox(
                            height: 15,
                          ),
                          Row(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                              Column(
                                children: [
                                  Text(
                                    controller.user['following'],
                                    style: const TextStyle(
                                      fontSize: 20,
                                      fontWeight: FontWeight.bold,
                                    ),
                                  ),
                                  const SizedBox(height: 5),
                                  const Text(
                                    'Following',
                                    style: TextStyle(
                                      fontSize: 14,
                                    ),
                                  ),
                                ],
                              ),
                              Container(
                                color: Colors.black54,
                                width: 1,
                                height: 15,
                                margin: const EdgeInsets.symmetric(
                                  horizontal: 15,
                                ),
                              ),
                              Column(
                                children: [
                                  Text(
                                    controller.user['followers'],
                                    style: const TextStyle(
                                      fontSize: 20,
                                      fontWeight: FontWeight.bold,
                                    ),
                                  ),
                                  const SizedBox(height: 5),
                                  const Text(
                                    'Followers',
                                    style: TextStyle(
                                      fontSize: 14,
                                    ),
                                  ),
                                ],
                              ),
                              Container(
                                color: Colors.black54,
                                width: 1,
                                height: 15,
                                margin: const EdgeInsets.symmetric(
                                  horizontal: 15,
                                ),
                              ),
                              Column(
                                children: [
                                  Text(
                                    controller.user['likes'],
                                    style: const TextStyle(
                                      fontSize: 20,
                                      fontWeight: FontWeight.bold,
                                    ),
                                  ),
                                  const SizedBox(height: 5),
                                  const Text(
                                    'Likes',
                                    style: TextStyle(
                                      fontSize: 14,
                                    ),
                                  ),
                                ],
                              ),
                            ],
                          ),
                          const SizedBox(
                            height: 15,
                          ),
                          Container(
                            width: 140,
                            height: 47,
                            decoration: BoxDecoration(
                              border: Border.all(
                                color: Colors.black12,
                              ),
                            ),
                            child: Center(
                              child: InkWell(
                                onTap: () {
                                  if (widget.uid == authController.user?.uid) {
                                    authController.signOut();
                                  } else {
                                    controller.followUser();
                                  }
                                },
                                child: Text(
                                  widget.uid == authController.user?.uid
                                      ? 'Sign Out'
                                      : controller.user['isFollowing']
                                          ? 'Unfollow'
                                          : 'Follow',
                                  style: const TextStyle(
                                    fontSize: 15,
                                    fontWeight: FontWeight.bold,
                                  ),
                                ),
                              ),
                            ),
                          ),
                          const SizedBox(
                            height: 25,
                          ),
                          // video list
                          GridView.builder(
                            shrinkWrap: true,
                            physics: const NeverScrollableScrollPhysics(),
                            itemCount: controller.user['thumbnails'].length,
                            gridDelegate:
                                const SliverGridDelegateWithFixedCrossAxisCount(
                              crossAxisCount: 2,
                              childAspectRatio: 1,
                              crossAxisSpacing: 5,
                            ),
                            itemBuilder: (context, index) {
                              String thumbnail =
                                  controller.user['thumbnails'][index];
                              return CachedNetworkImage(
                                imageUrl: thumbnail,
                                fit: BoxFit.cover,
                              );
                            },
                          )
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            ),
          );
        });
  }
}

错误原因

当controller.user['profilePhoto']为null时,代码通过?? ""赋值了空字符串作为imageUrl,CachedNetworkImage会尝试将空字符串解析为合法URI,这直接导致了No host specified in URI的参数错误。

修复方案

方案1:条件渲染(推荐)

在构建头像时,先判断profilePhoto是否存在且有效,有效则使用CachedNetworkImage,否则直接渲染CircleAvatar:

修改头像部分的代码:

Row(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    ClipOval(
      child: controller.user['profilePhoto'] != null && 
             controller.user['profilePhoto'].isNotEmpty
          ? CachedNetworkImage(
              fit: BoxFit.cover,
              imageUrl: controller.user['profilePhoto'],
              height: 100,
              width: 100,
              placeholder: (context, url) =>
                  const CircularProgressIndicator(),
              errorWidget: (context, url, error) =>
                  const CircleAvatar(
                    child: Icon(Icons.person),
                  ),
            )
          : const CircleAvatar(
              radius: 50,
              child: Icon(Icons.person),
            ),
    )
  ],
),

方案2:优化空值处理

如果希望保留CachedNetworkImage的错误处理逻辑,需要避免传入空字符串,可将空值替换为一个无效但合法的占位URL,同时修改errorWidget为CircleAvatar:

CachedNetworkImage(
  fit: BoxFit.cover,
  imageUrl: controller.user['profilePhoto'] ?? "https://placeholder.invalid",
  height: 100,
  width: 100,
  placeholder: (context, url) => const CircularProgressIndicator(),
  errorWidget: (context, url, error) =>
      const CircleAvatar(
        child: Icon(Icons.person),
      ),
),

额外注意

对于下方的缩略图列表GridView.builder,同样需要处理thumbnail为空的情况,避免触发相同错误:

itemBuilder: (context, index) {
  String? thumbnail = controller.user['thumbnails'][index];
  return thumbnail != null && thumbnail.isNotEmpty
      ? CachedNetworkImage(
          imageUrl: thumbnail,
          fit: BoxFit.cover,
          errorWidget: (context, url, error) =>
              const Icon(Icons.broken_image),
        )
      : const Icon(Icons.broken_image);
},

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 18:01:26