Search code examples
user-interfacetextdartfluttermarquee

How to make a Text widget act like marquee when the text overflows in Flutter


I'm looking for a way to implement Marquee style on a Text widget so that it automatically start scrolling when the text is overflowed from the screen. Is there a way to do it. I've tried all the decoration modes but cant seem to find a Marquee option there.


Solution

  • This widget is what I came up with, and I think it serves your needs:

    class MarqueeWidget extends StatefulWidget {
      final Widget child;
      final Axis direction;
      final Duration animationDuration, backDuration, pauseDuration;
    
      const MarqueeWidget({
        Key? key,
        required this.child,
        this.direction = Axis.horizontal,
        this.animationDuration = const Duration(milliseconds: 6000),
        this.backDuration = const Duration(milliseconds: 800),
        this.pauseDuration = const Duration(milliseconds: 800),
      }) : super(key: key);
    
      @override
      _MarqueeWidgetState createState() => _MarqueeWidgetState();
    }
    
    class _MarqueeWidgetState extends State<MarqueeWidget> {
      late ScrollController scrollController;
    
      @override
      void initState() {
        scrollController = ScrollController(initialScrollOffset: 50.0);
        WidgetsBinding.instance.addPostFrameCallback(scroll);
        super.initState();
      }
    
      @override
      void dispose() {
        scrollController.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return SingleChildScrollView(
          physics: NeverScrollableScrollPhysics(),
          child: widget.child,
          scrollDirection: widget.direction,
          controller: scrollController,
        );
      }
    
      void scroll(_) async {
        while (scrollController.hasClients) {
          await Future.delayed(widget.pauseDuration);
          if (scrollController.hasClients) {
            await scrollController.animateTo(
              scrollController.position.maxScrollExtent,
              duration: widget.animationDuration,
              curve: Curves.ease,
            );
          }
          await Future.delayed(widget.pauseDuration);
          if (scrollController.hasClients) {
            await scrollController.animateTo(
              0.0,
              duration: widget.backDuration,
              curve: Curves.easeOut,
            );
          }
        }
      }
    }
    

    Its functionality should be pretty obvious. An example implementation would look like this:

    class FlutterMarqueeText extends StatefulWidget {
      const FlutterMarqueeText({Key? key}) : super(key: key);
    
      @override
      _FlutterMarqueeTextState createState() => _FlutterMarqueeTextState();
    }
    
    class _FlutterMarqueeTextState extends State<FlutterMarqueeText> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text("Flutter Marquee Text"),
          ),
          body: const Center(
            child: SizedBox(
              width: 200.0,
              child: MarqueeWidget(
                direction: Axis.horizontal,
                child: Text("This text is to long to be shown in just one line"),
              ),
            ),
          ),
        );
      }
    }