如何修复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
相关产品推荐
相关产品推荐

