1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

  2. Anuncie Aqui ! Entre em contato fdantas@4each.com.br

[Flutter] Flutter | How to know if widget is on top of navigation stack (visible)

Discussão em 'Mobile' iniciado por Stack, Novembro 13, 2024.

  1. Stack

    Stack Membro Participativo

    In my Flutter app, I wish to pause an animation if a new page is pushed to the navigation stack.

    Question: From a widgets perspective, is there any way to know if the widget is at the top of the navigation stack?

    Example:

    while (isTopOfNavigationStack) {
    // Do the animation
    }


    Edit 1: More details

    Im using the library widget_marquee for creating a horizontal ticker. This library works great until a new page is pushed to the navigation stack. When this happens, the while-loop becomes infinite and the application freezes.

    library widget_marquee;

    import 'dart:developer';
    import 'package:flutter/material.dart';

    /// Rotates the [child] widget indefinitely along the horizontal axis if the
    /// content extends pass the edge of the render area.
    ///
    /// [delayDuration] - One time delay to wait before starting the text rotation
    /// [gap] - Spacing to add between widget end and start
    /// [loopDuration] - Time for one full rotation of the child
    /// [onLoopFinish] - Function to run upon finishing each loop
    /// [onScrollingTap]
    /// [pixelsPerSecond] - Alternate to loop duration
    class Marquee extends StatelessWidget {
    const Marquee({
    Key? key,
    required this.child,
    this.delayDuration = const Duration(milliseconds: 1500),
    this.gap = 50,
    this.loopDuration = const Duration(milliseconds: 8000),
    this.onLoopFinish = _onLoopFinish,
    this.onScrollingTap = _onScrollingTap,
    this.pixelsPerSecond = 0,
    }) : super(key: key);

    final Widget child;
    final Duration delayDuration;
    final double gap;
    final Duration loopDuration;
    final Future<void> Function() onLoopFinish;
    final Future<void> Function() onScrollingTap;
    final int pixelsPerSecond;

    @override
    Widget build(BuildContext context) {
    return _Marquee(
    key: UniqueKey(),
    child: child,
    delay: delayDuration,
    gap: gap,
    loopDuration: loopDuration,
    onLoopFinish: onLoopFinish,
    onScrollingTap: onScrollingTap,
    pps: pixelsPerSecond,
    );
    }
    }

    class _Marquee extends StatefulWidget {
    const _Marquee({
    required Key key,
    required this.child,
    required this.delay,
    required this.gap,
    required this.loopDuration,
    required this.onLoopFinish,
    required this.onScrollingTap,
    required this.pps,
    }) : super(key: key);

    final Widget child;
    final Duration delay;
    final double gap;
    final Duration loopDuration;
    final Future<void> Function() onLoopFinish;
    final Future<void> Function() onScrollingTap;
    final int pps;

    @override
    _MarqueeState createState() => _MarqueeState();
    }

    class _MarqueeState extends State<_Marquee> with TickerProviderStateMixin {
    late double contentArea;
    bool isScrolling = false;
    late ScrollController scrollController;
    List<Widget> widgets = <Widget>[];


    @override
    void initState() {
    super.initState();
    }

    @override
    void didChangeDependencies() {
    scrollController = ScrollController(
    initialScrollOffset: 0.0,
    keepScrollOffset: false,
    );

    widgets = <Widget>[widget.child];

    // Initialize the scroll controller

    WidgetsBinding.instance?.addPostFrameCallback(scroll);
    super.didChangeDependencies();
    }

    void scroll(_) async {
    if (scrollController.position.maxScrollExtent > 0) {
    late Duration duration;
    final double initMax = scrollController.position.maxScrollExtent;

    // Add a sized box and duplicate widget to the row
    setState(() {
    widgets.add(SizedBox(width: widget.gap));
    widgets.add(widget.child);
    });

    await Future<dynamic>.delayed(widget.delay);

    try {
    setState(() {
    isScrolling = true;
    });

    while (scrollController.hasClients) {

    // Calculate the position where the duplicate widget lines up with the original
    final scrollExtent =
    (initMax * 2) - (initMax - contentArea) + widget.gap;

    // Set the duration of the animation
    if (widget.pps <= 0) {
    duration = widget.loopDuration;
    } else {
    duration = Duration(
    // Calculate the duration based on the pixels per second
    milliseconds: ((scrollExtent / widget.pps) * 1000).toInt(),
    );
    }

    await scrollController.animateTo(
    scrollExtent,
    duration: duration,
    curve: Curves.linear,
    );

    // Jump to the beginning of the view to imitate loop
    scrollController.jumpTo(0);
    await widget.onLoopFinish();
    }
    } catch (e) {
    log('Marquee element has been disposed');
    }
    }
    }

    @override
    Widget build(BuildContext context) {
    return LayoutBuilder(
    builder: (BuildContext context, BoxConstraints constraints) {
    contentArea = constraints.maxWidth;

    // Thanks to how widgets work, the gesture detector is only triggered
    // if there's nothing clickable in the child
    return GestureDetector(
    onTap: () async {
    if (isScrolling) {
    await widget.onScrollingTap();
    }
    },
    child: Container(
    alignment: Alignment.center,
    child: SingleChildScrollView(
    physics: const NeverScrollableScrollPhysics(),
    child: Row(
    children: widgets,
    ),
    scrollDirection: Axis.horizontal,
    controller: scrollController,
    ),
    ),
    );
    },
    );
    }

    @override
    void dispose() {
    scrollController.dispose();
    super.dispose();
    }
    }

    Future<void> _onLoopFinish() async {}

    Future<void> _onScrollingTap() async {
    log('Marquee onScrollingTap function triggered');
    }

    Continue reading...

Compartilhe esta Página