Flutter歌词显示插件flutter_lyric的使用
Flutter歌词显示插件flutter_lyric的使用
简介
flutter_lyric
是一个用于在Flutter应用中显示歌词的插件。它支持高亮显示、翻译歌词、平滑动画以及自定义UI和解析等功能。以下是该插件的主要特性:
- ✅ 高亮显示(增强型和普通型)
- ✅ 翻译歌词
- ✅ 平滑动画
- ✅ 自定义UI和解析
示例代码
下面是一个完整的示例代码,展示了如何使用 flutter_lyric
插件来显示歌词并控制音频播放。
import 'dart:ui';
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:flutter_lyric/lyrics_reader.dart';
import 'const.dart'; // 假设这是你自己的常量文件
void main() {
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatefulWidget {
[@override](/user/override)
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
AudioPlayer? audioPlayer;
double sliderProgress = 111658;
int playProgress = 111658;
double max_value = 211658;
bool isTap = false;
bool useEnhancedLrc = false;
var lyricModel = LyricsModelBuilder.create()
.bindLyricToMain(normalLyric)
.bindLyricToExt(transLyric)
.getModel();
var lyricUI = UINetease();
[@override](/user/override)
void initState() {
super.initState();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: buildContainer(),
);
}
Widget buildContainer() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
buildReaderWidget(),
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
...buildPlayControl(),
...buildUIControl(),
],
),
),
),
],
);
}
var lyricPadding = 40.0;
Stack buildReaderWidget() {
return Stack(
children: [
...buildReaderBackground(),
LyricsReader(
padding: EdgeInsets.symmetric(horizontal: lyricPadding),
model: lyricModel,
position: playProgress,
lyricUi: lyricUI,
playing: playing,
size: Size(double.infinity, MediaQuery.of(context).size.height / 2),
emptyBuilder: () => Center(
child: Text(
"No lyrics",
style: lyricUI.getOtherMainTextStyle(),
),
),
selectLineBuilder: (progress, confirm) {
return Row(
children: [
IconButton(
onPressed: () {
LyricsLog.logD("点击事件");
confirm.call();
setState(() {
audioPlayer?.seek(Duration(milliseconds: progress));
});
},
icon: Icon(Icons.play_arrow, color: Colors.green)),
Expanded(
child: Container(
decoration: BoxDecoration(color: Colors.green),
height: 1,
width: double.infinity,
),
),
Text(
progress.toString(),
style: TextStyle(color: Colors.green),
)
],
);
},
)
],
);
}
List<Widget> buildPlayControl() {
return [
Container(
height: 20,
),
Text(
"Progress:$sliderProgress",
style: TextStyle(
fontSize: 16,
color: Colors.green,
),
),
if (sliderProgress < max_value)
Slider(
min: 0,
max: max_value,
label: sliderProgress.toString(),
value: sliderProgress,
activeColor: Colors.blueGrey,
inactiveColor: Colors.blue,
onChanged: (double value) {
setState(() {
sliderProgress = value;
});
},
onChangeStart: (double value) {
isTap = true;
},
onChangeEnd: (double value) {
isTap = false;
setState(() {
playProgress = value.toInt();
});
audioPlayer?.seek(Duration(milliseconds: value.toInt()));
},
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: () async {
if (audioPlayer == null) {
audioPlayer = AudioPlayer()..play(AssetSource("music1.mp3"));
setState(() {
playing = true;
});
audioPlayer?.onDurationChanged.listen((Duration event) {
setState(() {
max_value = event.inMilliseconds.toDouble();
});
});
audioPlayer?.onPositionChanged.listen((Duration event) {
if (isTap) return;
setState(() {
sliderProgress = event.inMilliseconds.toDouble();
playProgress = event.inMilliseconds;
});
});
audioPlayer?.onPlayerStateChanged.listen((PlayerState state) {
setState(() {
playing = state == PlayerState.playing;
});
});
} else {
audioPlayer?.resume();
}
},
child: Text("Play")),
Container(
width: 10,
),
TextButton(
onPressed: () async {
audioPlayer?.pause();
},
child: Text("Pause")),
Container(
width: 10,
),
TextButton(
onPressed: () async {
audioPlayer?.stop();
audioPlayer = null;
},
child: Text("Stop")),
],
),
];
}
var playing = false;
List<Widget> buildReaderBackground() {
return [
Positioned.fill(
child: Image.asset(
"bg.jpeg",
fit: BoxFit.cover,
),
),
Positioned.fill(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
color: Colors.black.withOpacity(0.3),
),
),
)
];
}
var mainTextSize = 18.0;
var extTextSize = 16.0;
var lineGap = 16.0;
var inlineGap = 10.0;
var lyricAlign = LyricAlign.CENTER;
var highlightDirection = HighlightDirection.LTR;
List<Widget> buildUIControl() {
return [
Container(
height: 30,
),
Text("UI setting", style: TextStyle(fontWeight: FontWeight.bold)),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Checkbox(
value: lyricUI.enableHighlight(),
onChanged: (value) {
setState(() {
lyricUI.highlight = (value ?? false);
refreshLyric();
});
}),
Text("enable highLight"),
Checkbox(
value: useEnhancedLrc,
onChanged: (value) {
setState(() {
useEnhancedLrc = value!;
lyricModel = LyricsModelBuilder.create()
.bindLyricToMain(value ? advancedLyric : normalLyric)
.bindLyricToExt(transLyric)
.getModel();
});
}),
Text("use Enhanced lrc")
],
),
buildTitle("highlight direction"),
Row(
mainAxisSize: MainAxisSize.min,
children: HighlightDirection.values
.map(
(e) => Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Radio<HighlightDirection>(
activeColor: Colors.orangeAccent,
value: e,
groupValue: highlightDirection,
onChanged: (v) {
setState(() {
highlightDirection = v!;
lyricUI.highlightDirection = highlightDirection;
refreshLyric();
});
}),
Text(e.toString().split(".")[1])
],
),
)),
)
.toList(),
),
buildTitle("lyric padding"),
Slider(
min: 0,
max: 100,
label: lyricPadding.toString(),
value: lyricPadding,
activeColor: Colors.blueGrey,
inactiveColor: Colors.blue,
onChanged: (double value) {
setState(() {
lyricPadding = value;
});
},
),
buildTitle("lyric primary text size"),
Slider(
min: 15,
max: 30,
label: mainTextSize.toString(),
value: mainTextSize,
activeColor: Colors.blueGrey,
inactiveColor: Colors.blue,
onChanged: (double value) {
setState(() {
mainTextSize = value;
});
},
onChangeEnd: (double value) {
setState(() {
lyricUI.defaultSize = mainTextSize;
refreshLyric();
});
},
),
buildTitle("lyric secondary text size"),
Slider(
min: 15,
max: 30,
label: extTextSize.toString(),
value: extTextSize,
activeColor: Colors.blueGrey,
inactiveColor: Colors.blue,
onChanged: (double value) {
setState(() {
extTextSize = value;
});
},
onChangeEnd: (double value) {
setState(() {
lyricUI.defaultExtSize = extTextSize;
refreshLyric();
});
},
),
buildTitle("lyric line spacing"),
Slider(
min: 10,
max: 80,
label: lineGap.toString(),
value: lineGap,
activeColor: Colors.blueGrey,
inactiveColor: Colors.blue,
onChanged: (double value) {
setState(() {
lineGap = value;
});
},
onChangeEnd: (double value) {
setState(() {
lyricUI.lineGap = lineGap;
refreshLyric();
});
},
),
buildTitle("primary and secondary lyric spacing"),
Slider(
min: 10,
max: 80,
label: inlineGap.toString(),
value: inlineGap,
activeColor: Colors.blueGrey,
inactiveColor: Colors.blue,
onChanged: (double value) {
setState(() {
inlineGap = value;
});
},
onChangeEnd: (double value) {
setState(() {
lyricUI.inlineGap = inlineGap;
refreshLyric();
});
},
),
buildTitle("select line bias"),
Slider(
min: 0.3,
max: 0.8,
label: bias.toString(),
value: bias,
activeColor: Colors.blueGrey,
inactiveColor: Colors.blue,
onChanged: (double value) {
setState(() {
bias = value;
});
},
onChangeEnd: (double value) {
setState(() {
lyricUI.bias = bias;
refreshLyric();
});
},
),
buildTitle("lyric align"),
Row(
mainAxisSize: MainAxisSize.min,
children: LyricAlign.values
.map(
(e) => Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Radio<LyricAlign>(
activeColor: Colors.orangeAccent,
value: e,
groupValue: lyricAlign,
onChanged: (v) {
setState(() {
lyricAlign = v!;
lyricUI.lyricAlign = lyricAlign;
refreshLyric();
});
}),
Text(e.toString().split(".")[1])
],
),
)),
)
.toList(),
),
buildTitle("select line base"),
Row(
children: LyricBaseLine.values
.map((e) => Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Radio<LyricBaseLine>(
activeColor: Colors.orangeAccent,
value: e,
groupValue: lyricBiasBaseLine,
onChanged: (v) {
setState(() {
lyricBiasBaseLine = v!;
lyricUI.lyricBaseLine = lyricBiasBaseLine;
refreshLyric();
});
}),
Text(e.toString().split(".")[1])
],
),
),
))
.toList(),
),
];
}
void refreshLyric() {
lyricUI = UINetease.clone(lyricUI);
}
var bias = 0.5;
var lyricBiasBaseLine = LyricBaseLine.CENTER;
Text buildTitle(String title) => Text(title,
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.green));
}
更多关于Flutter歌词显示插件flutter_lyric的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter歌词显示插件flutter_lyric的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用flutter_lyric
插件来显示歌词的一个基本示例。这个插件允许你以一种用户友好的方式在应用中显示歌词,通常用于音乐播放器应用。
首先,确保你已经在pubspec.yaml
文件中添加了flutter_lyric
依赖:
dependencies:
flutter:
sdk: flutter
flutter_lyric: ^最新版本号 # 请替换为最新的版本号
然后运行flutter pub get
来安装依赖。
接下来,你可以在你的Flutter应用中实现歌词显示功能。以下是一个简单的示例:
import 'package:flutter/material.dart';
import 'package:flutter_lyric/flutter_lyric.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Lyric Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: LyricPage(),
);
}
}
class LyricPage extends StatefulWidget {
@override
_LyricPageState createState() => _LyricPageState();
}
class _LyricPageState extends State<LyricPage> {
final FlutterLyricController _lyricController = FlutterLyricController();
@override
void initState() {
super.initState();
// 假设这是你的歌词数据,通常你会从服务器或本地文件获取
String lyrics = """
[00:00.00] 作曲 : 周杰伦
[00:01.00] 作词 : 方文山
[00:02.00] 歌手 : 周杰伦
[00:25.76]素胚勾勒出青花笔锋浓转淡
[00:32.37]瓶身描绘的牡丹一如你初妆
[00:39.03]冉冉檀香透过窗心事我了然
[00:45.64]宣纸上 走笔至此搁一半
...
""";
// 解析歌词
_lyricController.parseLyric(lyrics);
// 你可以在这里设置当前播放的时间,用于高亮显示当前歌词行
// 例如,模拟一个播放时间更新
Timer.periodic(Duration(seconds: 1), (timer) {
int currentTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
// 这里你应该使用歌曲的实际播放时间,而不是当前时间
// 这里只是为了演示
_lyricController.updateCurrentTime(currentTime * 1000); // 转换为毫秒
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Lyric Demo'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: FlutterLyric(
controller: _lyricController,
style: TextStyle(fontSize: 18), // 你可以自定义歌词的样式
),
),
);
}
@override
void dispose() {
_lyricController.dispose(); // 释放资源
super.dispose();
}
}
在这个示例中,我们创建了一个简单的Flutter应用,其中包含一个歌词显示页面。我们使用了FlutterLyricController
来解析歌词数据,并在页面上显示它们。我们还使用了一个Timer
来模拟播放时间的更新,以便高亮显示当前播放的歌词行。请注意,在实际应用中,你应该根据歌曲的播放时间来更新当前时间,而不是使用当前系统时间。
确保在实际项目中处理歌词数据的加载和错误处理,以及根据需要进行样式调整。