我对 flutter 还很陌生,我正在努力复制 Audible 应用程序中长曲目标题的文本滚动功能(参见附件)。这是我想要实现的行为:
• 最初显示长标题 - 其中一部分将不可见,因为它太长。
• 暂停预定的秒数,文本应在此暂停状态下等待几秒钟,然后再开始滚动。
• 开始滚动文本。
• 确保通过滚动显示整个文本。
• 在文本末尾插入特定数量的空格。
• 当空格结束时,我希望文本的开头重新出现并继续滚动。
• 文本应该滚动,直到看起来回到原来的位置。
• 重新开始循环(等待 -> 滚动 -> 循环)。
我已经成功实现了其中的大部分内容。然而,我的重置点并不完美(见附件)。我复制了文本以使其滚动两次,然后当第二组到达第一个文本的开始位置时重置动画。时间不精确,导致重置不完美。
只是觉得可能有比我所做的更好的方法来解决这个问题,但我对 Flutter 很陌生,我不知道那种方法可能是什么。
有没有更有效的方法来实现这一点,或者有人有改进我现有代码的建议吗?
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'dart:async';
import 'package:the_audiobook_app/utils/audio_player_handler.dart';
class ScrollingText extends StatefulWidget {
final String? text;
final double speed;
final double containerWidth;
final double containerHeight;
final int spaceCount;
final Duration delay;
ScrollingText({
this.text,
this.speed = 1.0,
this.containerWidth = 150.0,
this.containerHeight = 50.0,
this.spaceCount = 5,
this.delay = const Duration(seconds: 2),
});
@override
_ScrollingTextState createState() => _ScrollingTextState();
}
class _ScrollingTextState extends State<ScrollingText> with TickerProviderStateMixin {
late ScrollController _controller;
late Ticker _ticker;
late TextPainter textPainter;
bool shouldScroll = false;
late String spacedText; // Added this to ensure spaceCount is applied consistently
double singleTextWidth = 0;
@override
void initState() {
super.initState();
_controller = ScrollController();
_ticker = Ticker(_onTick);
//spacedText = widget.text + ' ' * widget.spaceCount + widget.text;
//spacedText = 'Loading...${' ' * widget.spaceCount}Loading...'; // The fact this is being set here is probably not a good thing
}
void initializeScrollingText(String title){
spacedText = title + ' ' * widget.spaceCount + title;
textPainter = TextPainter(
text: TextSpan(text: title, style: TextStyle(fontSize: 20)),
textDirection: TextDirection.ltr,
)..layout();
singleTextWidth = textPainter.width;
if (textPainter.width >= widget.containerWidth) {
textPainter.text = TextSpan(text: spacedText, style: TextStyle(fontSize: 20));
textPainter.layout();
}
shouldScroll = textPainter.width > widget.containerWidth;
if (shouldScroll && !_ticker.isActive) {
_startScrollingWithDelay();
}
}
void _startScrollingWithDelay() {
Future.delayed(widget.delay, () {
if (mounted) {
_ticker.start();
}
});
}
void _onTick(Duration elapsed) {
double current = _controller.offset + widget.speed;
if (current >= singleTextWidth) { // When the second instance is at the beginning.
_ticker.stop();
_controller.jumpTo(0);
Future.delayed(widget.delay, () {
if (mounted) {
_ticker.start();
}
});
} else {
_controller.jumpTo(current);
}
}
@override
Widget build(BuildContext context) {
return StreamBuilder<int?>(
stream: AudioPlayerHandler.instance.player.currentIndexStream,
builder: (context, snapshot) {
String title = 'Loading...'; // default text
if (snapshot.connectionState == ConnectionState.active && snapshot.data != null) {
title = AudioPlayerHandler.instance.getCurrentTrackTitle() ?? 'NULL VALUE';
}
initializeScrollingText(title); // Initialize scrolling for the new title
return Container(
width: widget.containerWidth,
height: widget.containerHeight,
child: shouldScroll
? ListView.builder(
itemCount: 1,
controller: _controller,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Text(spacedText,
style: TextStyle(fontSize: 20),
softWrap: false,
overflow: TextOverflow.visible);
},
)
: Center(
child: Text(spacedText.split(' ')[0], // Since spacedText has repetitions, we only take the first occurrence
style: TextStyle(fontSize: 20),
softWrap: true,
overflow: TextOverflow.ellipsis),
),
);
},
);
}
@override
void dispose() {
_ticker.dispose();
_controller.dispose();
super.dispose();
}
}
这种滚动动画称为“跑马灯”。您正是需要这个扩展:https://pub.dev/packages/text_scroll
我为你的案例写了一个例子;
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
children: [
const Expanded(flex: 2, child: Text('Static text')),
Flexible(
flex: 1,
child: Container(
color: Colors.grey,
padding: const EdgeInsets.symmetric(vertical: 5),
child: const TextScroll(
'This is the sample text for Flutter TextScroll widget. ',
velocity: Velocity(pixelsPerSecond: Offset(50, 0)),
pauseBetween: Duration(milliseconds: 2000), // This will pause animation after it ends. You need this for your case
mode: TextScrollMode.endless,
),
),
),
],
),
],
)
祝你好运👋