在我的 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');
}
您可以通过
ModalRoute.of(context)?.isCurrent
了解当前路线是否是导航中的首要路线。在你的例子中应该是这样的
final _isTopOfNavigationStack = ModalRoute.of(context)?.isCurrent ?? false;
while (_isTopOfNavigationStack) {
// Do the animation
}
如果您需要更多详细信息,请检查此答案。
这是一个很老的问题,但我认为以下方法可行:
if (!Navigator.of(context).canPop())
或
if (!Navigator.of(globalKey.currentContext!).canPop()