Flutter实现滚动(Marquee)文本Widget的方案咨询
Flutter 实现自动左右滚动的跑马灯(Marquee)文本效果
方案一:使用支持空安全的第三方包 marquee
这个包维护活跃且完全支持空安全,可直接实现所需的滚动效果。
步骤:
- 在
pubspec.yaml中添加依赖:
dependencies: marquee: ^2.2.3 # 建议使用pub.dev上的最新版本
- 执行
flutter pub get安装依赖 - 代码示例:
import 'package:flutter/material.dart'; import 'package:marquee/marquee.dart'; class MarqueeText extends StatelessWidget { final String content; const MarqueeText({super.key, required this.content}); @override Widget build(BuildContext context) { return Marquee( text: content, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500), scrollAxis: Axis.horizontal, blankSpace: 25.0, // 文本滚动到末尾后预留的空白间距 velocity: 45.0, // 滚动速度,单位:像素/秒 pauseAfterRound: const Duration(seconds: 1), // 每轮滚动结束后暂停1秒 startPadding: 15.0, // 初始状态下文本左侧的内边距 accelerationDuration: const Duration(milliseconds: 800), // 加速到指定速度的时长 decelerationDuration: const Duration(milliseconds: 500), // 减速停止的时长 ); } } // 使用示例 void main() { runApp(const MaterialApp( home: Scaffold( body: Center( child: MarqueeText( content: '这是一段需要自动左右滚动的长文本,模拟OBS等工具中的滚动字幕效果', ), ), ), )); }
方案二:自定义实现(无第三方依赖)
如果不想依赖外部包,可通过AnimatedBuilder结合Transform.translate手动实现,灵活性更高。
代码示例:
import 'package:flutter/material.dart'; class CustomMarquee extends StatefulWidget { final String text; final double speed; // 滚动速度(像素/秒) final TextStyle? style; final double gap; // 重复文本之间的空白间隔 const CustomMarquee({ super.key, required this.text, this.speed = 35, this.style, this.gap = 20, }); @override State<CustomMarquee> createState() => _CustomMarqueeState(); } class _CustomMarqueeState extends State<CustomMarquee> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<double> _animation; double? _textWidth; double? _containerWidth; @override void initState() { super.initState(); _controller = AnimationController(vsync: this)..repeat(); } @override void didChangeDependencies() { super.didChangeDependencies(); // 计算文本实际宽度 final textPainter = TextPainter( text: TextSpan(text: widget.text, style: widget.style), textDirection: TextDirection.ltr, )..layout(maxWidth: MediaQuery.of(context).size.width); _textWidth = textPainter.width; _containerWidth = MediaQuery.of(context).size.width; // 根据总滚动距离和速度计算动画时长 final totalDistance = _textWidth! + _containerWidth! + widget.gap; final duration = Duration(seconds: (totalDistance / widget.speed).ceil()); _controller.duration = duration; // 定义滚动动画的起始和结束偏移量 _animation = Tween<double>( begin: _containerWidth!, end: -(_textWidth! + widget.gap), ).animate(CurvedAnimation(parent: _controller, curve: Curves.linear)); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return ClipRect( child: AnimatedBuilder( animation: _animation, builder: (context, child) { return Transform.translate( offset: Offset(_animation.value, 0), child: Row( children: [ Text(widget.text, style: widget.style), SizedBox(width: widget.gap), // 重复文本实现无缝滚动效果 Text(widget.text, style: widget.style), ], ), ); }, ), ); } } // 使用示例 void main() { runApp(const MaterialApp( home: Scaffold( body: Center( child: SizedBox( width: 300, // 限制容器宽度触发滚动 child: CustomMarquee( text: '自定义跑马灯效果,无需依赖第三方包,完全支持空安全', speed: 40, style: TextStyle(fontSize: 16, color: Colors.deepPurple), ), ), ), ), )); }
内容的提问来源于stack exchange,提问作者John Harrington
相关产品推荐
相关产品推荐

