颤动| 如何知道小部件是否位于导航堆栈顶部(可见)

问题描述 投票:0回答:2

在我的 Flutter 应用程序中,如果将新页面推送到导航堆栈,我希望暂停动画。

问题:从小部件的角度来看,有没有办法知道小部件是否位于导航堆栈的顶部?

示例:

while (isTopOfNavigationStack) {
    // Do the animation
}

编辑1:更多细节

我正在使用库 widget_marquee 来创建水平滚动条。在将新页面推送到导航堆栈之前,该库一直运行良好。发生这种情况时,

while-loop
变得无限大并且应用程序冻结。

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');
}
flutter flutter-animation flutter-navigation
2个回答
18
投票

您可以通过

ModalRoute.of(context)?.isCurrent
了解当前路线是否是导航中的首要路线。在你的例子中应该是这样的

final _isTopOfNavigationStack = ModalRoute.of(context)?.isCurrent ?? false;

while (_isTopOfNavigationStack) {
    // Do the animation
}

如果您需要更多详细信息,请检查此答案


0
投票

这是一个很老的问题,但我认为以下方法可行:

if (!Navigator.of(context).canPop())

if (!Navigator.of(globalKey.currentContext!).canPop()
© www.soinside.com 2019 - 2024. All rights reserved.