如何在Flutter Html Widget中展示API返回的内嵌视频?
Flutter中加载WordPress接口返回的视频内容问题
问题描述
调用WordPress REST接口 https://wrestlingworld.co/wp-json/wp/v2/posts?categories=22 后,从content->rendered字段获取到包含视频的HTML内容,使用flutter_html的Html组件渲染时,文本内容正常显示,但视频仅呈现占位样式,无法播放。当前使用的代码如下:
Padding( padding: const EdgeInsets.symmetric(horizontal: 5), child: Align( alignment: Alignment.center, child: Html( data: newsDetails.content!.rendered!, style: { "body": Style( fontSize: const FontSize(16), fontFamily: 'NunitoSans'), }, ), ), ),
解决方案
flutter_html 默认不支持原生渲染<video>或<iframe>标签的视频内容,需要通过自定义渲染器或提前解析视频链接来解决,以下是两种可行方案:
方案一:自定义视频标签渲染器
通过customRender参数扩展Html组件,将HTML中的视频标签替换为Flutter原生视频播放器:
- 添加依赖
在pubspec.yaml中引入所需包:
dependencies: flutter_html: ^3.0.0-beta.2 # 使用最新稳定版 video_player: ^2.6.1 # 若接口返回iframe嵌入的视频(如YouTube),需额外添加webview_flutter webview_flutter: ^4.2.0
- 实现视频播放器组件
class VideoPlayerWidget extends StatefulWidget { final String videoUrl; const VideoPlayerWidget({Key? key, required this.videoUrl}) : super(key: key); @override State<VideoPlayerWidget> createState() => _VideoPlayerWidgetState(); } class _VideoPlayerWidgetState extends State<VideoPlayerWidget> { late VideoPlayerController _controller; @override void initState() { super.initState(); _controller = VideoPlayerController.networkUrl(Uri.parse(widget.videoUrl)) ..initialize().then((_) => setState(() {})); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return _controller.value.isInitialized ? Column( children: [ AspectRatio( aspectRatio: _controller.value.aspectRatio, child: VideoPlayer(_controller), ), IconButton( icon: Icon( _controller.value.isPlaying ? Icons.pause : Icons.play_arrow, ), onPressed: () => setState(() { _controller.value.isPlaying ? _controller.pause() : _controller.play(); }), ), ], ) : const Center(child: CircularProgressIndicator()); } }
- 修改Html组件代码
添加customRender来处理视频标签:
Padding( padding: const EdgeInsets.symmetric(horizontal: 5), child: Align( alignment: Alignment.center, child: Html( data: newsDetails.content!.rendered!, style: { "body": Style( fontSize: const FontSize(16), fontFamily: 'NunitoSans', ), }, customRender: { // 处理原生video标签 "video": (context, attributes, _) { final videoSrc = attributes['src']; return videoSrc != null ? VideoPlayerWidget(videoUrl: videoSrc) : const SizedBox.shrink(); }, // 处理iframe嵌入的视频(可选) "iframe": (context, attributes, _) { final iframeSrc = attributes['src']; return iframeSrc != null ? SizedBox( width: double.infinity, height: 200, child: WebViewWidget( controller: WebViewController() ..loadRequest(Uri.parse(iframeSrc)), ), ) : const SizedBox.shrink(); }, }, ), ), ),
方案二:提前解析视频链接单独展示
通过正则表达式从HTML内容中提取视频链接,单独渲染视频播放器,避免HTML渲染兼容性问题:
// 提取video标签的src链接 RegExp videoRegExp = RegExp(r'<video[^>]+src="([^"]+)"'); Match? videoMatch = videoRegExp.firstMatch(newsDetails.content!.rendered!); String? videoUrl = videoMatch?.group(1); // 界面中先展示视频,再渲染文本内容 Column( children: [ if (videoUrl != null) VideoPlayerWidget(videoUrl: videoUrl), Padding( padding: const EdgeInsets.symmetric(horizontal: 5), child: Align( alignment: Alignment.center, child: Html( data: newsDetails.content!.rendered!, style: { "body": Style( fontSize: const FontSize(16), fontFamily: 'NunitoSans', ), }, // 隐藏原HTML中的视频占位 customRender: { "video": (context, attributes, _) => const SizedBox.shrink(), }, ), ), ), ], )
内容的提问来源于stack exchange,提问作者Sakib ovi
相关产品推荐
相关产品推荐

