You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

148 lines
3.8 KiB

library auto_scroll_text;
// Created by Bomsamdi on 2022
// Copyright © 2022 Bomsamdi. All rights reserved.
import 'dart:async';
import 'package:flutter/material.dart';
import 'after_layout_mixin.dart';
/// [AutoScrollText] is a solution when you need
/// text widget for long texts without overlaping or overflow.elipsis
/// [AutoScrollText] supports both directions [Axis.horizontal] and [Axis.vertical]
class AutoScrollText extends StatefulWidget {
/// [Text.text] of [AutoScrollText]
final String text;
/// [TextStyle] of [Text]
final TextStyle? textStyle;
/// [SingleChildScrollView.scrollDirection] default value [Axis.horizontal]
final Axis scrollDirection;
/// [Curve] of scroll animation
final Curve curve;
/// Distance per tick
final double moveDistance;
/// Reset timer
final int timerRest;
const AutoScrollText({
super.key,
required this.text,
this.textStyle,
this.scrollDirection = Axis.horizontal,
this.curve = Curves.linear,
this.moveDistance = 3.0,
this.timerRest = 100,
});
@override
State<StatefulWidget> createState() {
return AutoScrollTextState();
}
}
class AutoScrollTextState extends State<AutoScrollText>
with SingleTickerProviderStateMixin, AfterLayoutMixin {
/// Final text for scrolling
String textToScroll = "";
/// SingleChildScrollView controller
final ScrollController _scrollController = ScrollController();
/// Actual position of scroll
double position = 0.0;
/// Repeatable timer
Timer? timer;
/// if text is to long for axis, define auto scroll action
bool _isScrollable = false;
/// SingleChildScrollView key
final GlobalKey _scrollKey = GlobalKey();
/// Default [TextStyle] if [widget.textStyle] is null
TextStyle get defaultTextStyle => const TextStyle();
@override
void initState() {
textToScroll = widget.text;
super.initState();
}
/// Timer for animation
void _startTimer() {
if (_scrollKey.currentContext != null) {
timer = Timer.periodic(Duration(milliseconds: widget.timerRest), (timer) {
double maxScrollExtent = _scrollController.position.maxScrollExtent;
double pixels = _scrollController.position.pixels;
if (pixels + widget.moveDistance >= maxScrollExtent) {
position = 0;
_scrollController.jumpTo(position);
}
position += widget.moveDistance;
_scrollController.animateTo(position,
duration: Duration(milliseconds: widget.timerRest),
curve: widget.curve);
});
}
}
/// Check if autoscroll animation is needed
void _checkIsAutoScrollNeeded() {
setState(() {
_isScrollable = _scrollController.position.maxScrollExtent > 0;
});
}
/// Text builder
Widget _text() {
if (widget.scrollDirection == Axis.vertical) {
String newString = textToScroll.split("").join("\n");
return Text(
newString,
style: widget.textStyle ?? defaultTextStyle,
textAlign: TextAlign.center,
);
}
return Text(
textToScroll,
style: widget.textStyle ?? defaultTextStyle,
textAlign: TextAlign.justify,
);
}
@override
void dispose() {
super.dispose();
// dispose timer when needed
timer?.cancel();
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
key: _scrollKey,
scrollDirection: widget.scrollDirection,
controller: _scrollController,
physics: _isScrollable
? const AlwaysScrollableScrollPhysics()
: const NeverScrollableScrollPhysics(),
child: _text(),
);
}
@override
FutureOr<void> afterFirstLayout(BuildContext context) {
_checkIsAutoScrollNeeded();
if (_isScrollable) {
setState(() {
textToScroll = " $textToScroll ";
});
_startTimer();
}
}
}