Flutter字幕显示插件subtitle的使用
Flutter字幕显示插件subtitle的使用
Flutter 开发中,字幕显示是一个常见的需求,例如在视频播放、语言学习等场景。subtitle
插件提供了一种简单且高效的方式来处理多种字幕文件格式,并支持高度定制化。本文将详细介绍如何使用 subtitle
插件,并提供完整的示例代码。
介绍
subtitle
是一个用于处理多种字幕文件格式的库,具有以下特点:
- 支持多种字幕格式:WebVTT、SRT(SubRip)、TTML(Timed Text Markup Language)、DFXP(Distribution Format Exchange Profile)和自定义字幕格式。
- 高度可定制化(90%),支持 Sound Null Safety。
- 创建者:Muhammad Hasan Alasady,遵循 MIT LICENSE。
使用方法
1. 网络字幕
网络字幕可以通过两种方式加载:使用 SubtitleController
或者 SubtitleProvider
和 SubtitleParser
。
使用 SubtitleController
import 'package:subtitle/subtitle.dart';
void main() async {
var url = Uri.parse(
'https://brenopolanski.github.io/html5-video-webvtt-example/MIB2-subtitles-pt-BR.vtt');
var controller = SubtitleController(
provider: SubtitleProvider.fromNetwork(url),
);
await controller.initial();
// 打印所有字幕
printResult(controller.subtitles);
}
void printResult(List<Subtitle> subtitles) {
subtitles.sort((s1, s2) => s1.compareTo(s2));
for (var result in subtitles) {
print('(${result.index}) Start: ${result.start}, end: ${result.end} [${result.data}]');
}
}
使用 SubtitleProvider
和 SubtitleParser
import 'package:subtitle/subtitle.dart';
void main() async {
var url = Uri.parse(
'https://brenopolanski.github.io/html5-video-webvtt-example/MIB2-subtitles-pt-BR.vtt');
SubtitleProvider provider = NetworkSubtitle(url);
SubtitleObject object = await provider.getSubtitle();
SubtitleParser parser = SubtitleParser(object);
// 打印解析后的字幕
printResult(parser.parsing());
}
void printResult(List<Subtitle> subtitles) {
subtitles.sort((s1, s2) => s1.compareTo(s2));
for (var result in subtitles) {
print('(${result.index}) Start: ${result.start}, end: ${result.end} [${result.data}]');
}
}
2. 文件字幕
文件字幕的加载方式与网络字幕类似,只需要替换 SubtitleProvider
为 FileSubtitle
或 SubtitleProvider.fromFile()
。
import 'package:subtitle/subtitle.dart';
import 'dart:io';
void main() async {
var file = File('example/data.vtt');
var controller = SubtitleController(
provider: FileSubtitle(file),
);
await controller.initial();
// 打印所有字幕
printResult(controller.subtitles);
}
void printResult(List<Subtitle> subtitles) {
subtitles.sort((s1, s2) => s1.compareTo(s2));
for (var result in subtitles) {
print('(${result.index}) Start: ${result.start}, end: ${result.end} [${result.data}]');
}
}
3. 字符串字幕
字符串字幕可以直接从字符串加载,使用 StringSubtitle
或 SubtitleProvider.fromString()
。
import 'package:subtitle/subtitle.dart';
const vttData = '''WEBVTT FILE
5
00:00:19.000 --> 00:00:24.000
Which is why we are bringing TV, internet and phone together in <c.highlight>one</c> super package
3
00:00:11.000 --> 00:00:14.000 A:end
Phone conversations where people truly <c.highlight>connect</c>
1
00:00:03.500 --> 00:00:05.000 D:vertical A:start
Everyone wants the most from life
2
00:00:06.000 --> 00:00:09.000 A:start
Like internet experiences that are rich <b>and</b> entertaining
4
00:00:14.500 --> 00:00:18.000
Your favourite TV programmes ready to watch at the touch of a button
6
00:00:24.500 --> 00:00:26.000
<c.highlight>One</c> simple way to get everything
7
00:00:26.500 --> 00:00:27.500 L:12%
UPC
8
00:00:28.000 --> 00:00:30.000 L:75%
Simply for <u>everyone</u>
''';
void main() async {
var controller = SubtitleController(
provider: SubtitleProvider.fromString(
data: vttData,
type: SubtitleType.vtt,
),
);
await controller.initial();
// 打印所有字幕
printResult(controller.subtitles);
}
void printResult(List<Subtitle> subtitles) {
subtitles.sort((s1, s2) => s1.compareTo(s2));
for (var result in subtitles) {
print('(${result.index}) Start: ${result.start}, end: ${result.end} [${result.data}]');
}
}
4. Flutter 资源文件
对于 Flutter 项目中的资源文件,可以通过扩展 SubtitleProvider
并使用 rootBundle
来加载字幕文件。
import 'package:subtitle/subtitle.dart';
import 'package:flutter/services.dart' show rootBundle;
class AssetSubtitle extends SubtitleProvider {
final String path;
final SubtitleType? type;
const AssetSubtitle(this.path, {this.type});
@override
Future<SubtitleObject> getSubtitle() async {
final data = await rootBundle.loadString(path);
final ext = extension(path);
final subtitleType = this.type ?? getSubtitleType(ext);
return SubtitleObject(data: data, type: subtitleType);
}
}
void main() async {
var path = 'files/subtitles/1.vtt';
var controller = SubtitleController(
provider: AssetSubtitle(path),
);
await controller.initial();
// 打印所有字幕
printResult(controller.subtitles);
}
void printResult(List<Subtitle> subtitles) {
subtitles.sort((s1, s2) => s1.compareTo(s2));
for (var result in subtitles) {
print('(${result.index}) Start: ${result.start}, end: ${result.end} [${result.data}]');
}
}
组件介绍
- SubtitleProvider: 简化了从多个来源获取字幕文件数据的过程。
- SubtitleRepository: 直接与平台交互以获取或下载所需数据并提交给提供者。
- SubtitleParser: 用于分析和转换字幕文件,使其成为可查看和使用的软件对象。
- CustomSubtitleParser: 可自定义的字幕解析器,支持自定义正则表达式。
- SubtitleRegexObject: 提供解码字幕文件内容所需的正则表达式,包括 WebVTT、SubRip、TTML/DFXP 和自定义正则表达式。
- SubtitleObject: 存储字幕文件数据及其格式类型。
- SubtitleController: 控制字幕的准备、解码、排序和搜索。
示例
以下是处理 TTML 格式的完整示例:
import 'package:subtitle/subtitle.dart';
const ttmlText = '''
<?xml version="1.0" encoding="UTF-8"?>
<tt xmlns="http://www.w3.org/ns/ttml">
<head>
<metadata xmlns:ttm="http://www.w3.org/ns/ttml#metadata">
<ttm:title>Timed Text TTML Example</ttm:title>
<ttm:copyright>The Authors (c) 2006</ttm:copyright>
</metadata>
<styling xmlns:tts="http://www.w3.org/ns/ttml#styling">
<style xml:id="s1"
tts:color="white"
tts:fontFamily="proportionalSansSerif"
tts:fontSize="22px"
tts:textAlign="center"
/>
<style xml:id="s2" style="s1" tts:color="yellow"/>
<style xml:id="s1Right" style="s1" tts:textAlign="end" />
<style xml:id="s2Left" style="s2" tts:textAlign="start" />
</styling>
<layout xmlns:tts="http://www.w3.org/ns/ttml#styling">
<region xml:id="subtitleArea"
style="s1"
tts:extent="560px 62px"
tts:padding="5px 3px"
tts:backgroundColor="black"
tts:displayAlign="after"
/>
</layout>
</head>
<body region="subtitleArea">
<div>
<p xml:id="subtitle1" begin="0.76s" end="3.45s">
It seems a paradox, does it not,
</p>
<p xml:id="subtitle2" begin="5.0s" end="10.0s">
that the image formed on<br/>
the Retina should be inverted?
</p>
<p xml:id="subtitle3" begin="10.0s" end="16.0s" style="s2">
It is puzzling, why is it<br/>
we do not see things upside-down?
</p>
<p xml:id="subtitle4" begin="17.2s" end="23.0s">
You have never heard the Theory,<br/>then, that the Brain also is inverted?
</p>
<p xml:id="subtitle5" begin="23.0s" end="27.0s" style="s2">
No indeed! What a beautiful fact!
</p>
</div>
</body>
</tt>
''';
Future<void> main() async {
var sp = SubtitleProvider.fromString(
data: ttmlText,
type: SubtitleType.ttml,
);
var sc = SubtitleController(provider: sp);
await sc.initial();
var s = sc.durationSearch(Duration(seconds: 12));
print(s?.data ?? 'No subtitle in range of provided duration.');
}
void printResult(List<Subtitle> subtitles) {
subtitles.sort((s1, s2) => s1.compareTo(s2));
for (var result in subtitles) {
print('(${result.index}) Start: ${result.start}, end: ${result.end} [${result.data}]');
}
}
通过以上示例,您可以根据需要选择不同的字幕来源,并轻松地集成到您的 Flutter 应用程序中。希望这些信息对您有所帮助!
更多关于Flutter字幕显示插件subtitle的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter字幕显示插件subtitle的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何在Flutter中使用subtitle
插件来显示字幕的示例代码。subtitle
插件通常用于在视频播放时显示同步字幕。虽然没有一个广泛认可的、名为subtitle
的官方Flutter插件,但我们可以基于常见的字幕显示需求,使用video_player
插件配合自定义字幕显示逻辑来实现类似功能。
首先,确保你已经添加了video_player
和chewie
(一个基于video_player
的封装,提供了更丰富的播放控制UI)插件到你的pubspec.yaml
文件中:
dependencies:
flutter:
sdk: flutter
video_player: ^2.2.10 # 请检查最新版本
chewie: ^1.2.2 # 请检查最新版本,用于视频播放UI
然后运行flutter pub get
来安装这些依赖。
接下来是一个完整的示例代码,展示了如何在Flutter应用中播放视频并显示同步字幕:
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import 'package:chewie/chewie.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Video Player with Subtitles'),
),
body: Center(
child: VideoPlayerWithSubtitles(),
),
),
);
}
}
class VideoPlayerWithSubtitles extends StatefulWidget {
@override
_VideoPlayerWithSubtitlesState createState() => _VideoPlayerWithSubtitlesState();
}
class _VideoPlayerWithSubtitlesState extends State<VideoPlayerWithSubtitles> {
late VideoPlayerController _controller;
late ChewieController _chewieController;
late List<String> subtitles;
late List<Duration> subtitleTimes;
@override
void initState() {
super.initState();
// 初始化视频控制器
_controller = VideoPlayerController.network(
'https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_20mb.mp4',
)
..initialize().then((_) {
// 初始化字幕和时间点
setState(() {
subtitles = [
'Hello, this is the first subtitle.',
'This is the second subtitle appearing later.',
'Final subtitle here.',
];
subtitleTimes = [
Duration(seconds: 5),
Duration(seconds: 15),
Duration(seconds: 30),
];
});
});
// 初始化Chewie控制器
_chewieController = ChewieController(
_controller,
aspectRatio: _controller.value.aspectRatio,
autoPlay: false,
looping: false,
customSource: null,
);
// 监听视频播放进度
_controller.addListener(() {
if (_controller.value.position >= _getCurrentSubtitleTime()) {
showSubtitle();
}
});
}
// 获取当前应该显示的字幕时间
Duration _getCurrentSubtitleTime() {
for (int i = 0; i < subtitleTimes.length; i++) {
if (_controller.value.position >= subtitleTimes[i] &&
(i == subtitleTimes.length - 1 ||
_controller.value.position < subtitleTimes[i + 1])) {
return subtitleTimes[i];
}
}
return Duration.zero;
}
// 显示字幕的方法(这里简单使用Snackbar,实际项目中可能需要更复杂的UI)
void showSubtitle() {
final subtitleText = subtitles[subtitleTimes.indexOf(_getCurrentSubtitleTime())];
ScaffoldMessenger.of(context).showSnackbar(
SnackBar(
content: Text(subtitleText),
duration: Duration(seconds: 5), // 根据需要调整显示时间
),
);
}
@override
Widget build(BuildContext context) {
return _controller.value.isInitialized
? Chewie(
controller: _chewieController,
)
: Center(child: CircularProgressIndicator());
}
@override
void dispose() {
_controller.dispose();
_chewieController.dispose();
super.dispose();
}
}
在这个示例中,我们做了以下几件事:
- 使用
VideoPlayerController
来加载和播放视频。 - 使用
ChewieController
来提供视频播放的UI控制。 - 初始化了一个包含字幕文本和对应显示时间的列表。
- 通过监听视频播放进度,在达到指定时间点时显示相应的字幕。
请注意,这里的字幕显示使用了Snackbar
,这在实际应用中可能不是最佳的用户体验。你可能需要创建一个自定义的UI组件来更优雅地显示字幕。此外,这个例子假设字幕和时间是预先定义好的,实际应用中你可能需要从字幕文件(如SRT或SSA格式)中解析这些数据。