Bootstrap

了解 Flutter 的Timer类和Timer.periodic【Flutter专题19】

在构建移动应用程序时,我们经常会遇到必须在一定时间后执行任务的场景。还记得在进入应用程序之前看到闪亮的启动画面吗?

或者我们可能需要一段代码在一段时间后重复执行,比如显示剩余时间限制以填充一次性密码或每秒更改小部件的颜色以创建漂亮的动画。

为了解决 Flutter 应用程序中的这些需求,我们创建了这个类。因此,在本文中,我们将介绍以下几点,以最好地了解如何将这些功能实现到您自己的 Flutter 应用程序中:

  • 什么是类以及我们如何使用它

  • 如何创建一个定期计时器

  • 如何创建可重启的计时器

  • 使用类的例子

有了这个,让我们开始吧!

什么是类?

Flutter 的类允许我们创建一个倒数计时器。它在其生命周期中分别经历以下状态:

  • 创建一个计时器

  • 执行回调

  • 计时器结束

要使用属于 Dart 异步库一部分的Timer类,我们可以使用以下 import 语句导入它:

import 'dart:async';

创建一个简单的计时器

现在,要创建一个简单的 3 秒计时器,请添加以下内容,它会在执行后触发回调:

final timer = Timer(
  const Duration(seconds: 3),
  () {
    //做你想做的
  },
);

例如,一旦回调触发,我们就可以让用户做你想做的。但是请注意,回调仅触发一次。

创建一个简单的周期性计时器

使用,我们可以创建一个在给定持续时间后执行的重复计时器。定期计时器保持活动状态,直到它们被手动取消。Flutter 有一个名为 periodic的不同工厂方法来创建这样的计时器。

周期性计时器的一个很好的例子是显示时间敏感操作的剩余时间,例如在 10 分钟内完成付款。在以下示例中,代码生成一个计时器,每秒触发一次回调:

final periodicTimer = Timer.periodic(
  const Duration(seconds: 1),
  (timer) {
    // Update user about remaining time
  },
);

请注意,默认情况下,周期性计时器无限期保持活动状态。

很简单,不是吗?是的,但是在实际用例中使用它时,我们还必须回答其他问题:

  • 如何取消活动计时器

  • 如何知道计时器是否仍然处于活动状态

  • 如何知道经过的持续时间

如何使用类

该班给了我们很多的其他选项可以轻松地使用它。让我们深入了解如何使用这些其他选项以及它们如何与普通和定期计时器一起工作。

如何取消活动计时器

所述类具有抵消任何活动定时器的方法。对于普通计时器,调用不会调用回调。对于周期定时器,方法变得非常重要,因为我们必须完成定时器:

final periodicTimer = Timer.periodic(
  const Duration(seconds: 1),
  (timer) {
    // Update user about remaining time
  },
);

final shouldStop = true; //No more tick-tock now! Please

if (shouldStop) {
  timer.cancel();
}

请注意,我们可以根据需要调用任意多次而不会产生任何副作用;进一步的调用将被忽略。

如何知道计时器是否仍处于活动状态

active如果回调没有触发并且我们没有明确取消它,则会调用普通计时器。

另一方面,如果我们没有特别取消它们,周期性计时器总是处于活动状态:

final timer = Timer(
  const Duration(seconds: 3),
  () {
    // Navigate to your favorite place
  },
);

if (timer.isActive) {
  //
}

如何知道时间过去了

创建一个持续时间为 1 秒的定期计时器将在一分钟内滴答 60 次。虽然我们知道这本质上是正确的,但我们如何才能确切地知道计数?

这就是tick进来的地方。一个tick值从零开始,每次发生计时器事件时都会增加;此值是反映通过的持续时间数的计数。

例如,持续时间为 10 秒的周期性计时器将在一分钟内有六个事件,atick将给出相对于当前时间点的值。也就是说,半分钟后,该tick值将3在每个事件上继续递增:

final periodicTimer = Timer.periodic(
  const Duration(seconds: 10),
  (timer) {
    // Update user about remaining time
  },
);

final howMuchTimeBuddy = periodicTimer.tick;

在上面的代码中,我们创建了一个持续时间为 10 秒的周期性计时器。我们可以获取任何给定时间点的tick值periodicTimer.tick。

如何安排回调

Timer该类还有一个更有趣的用例。使用Timer,我们可以安排在异步模式下尽快执行的回调。为此,只需启动一个带有zero持续时间的计时器:

final zeroDurationTimer = Timer(
  Duration.zero,
  () {
    //Execute this callback ASAP but asynchronously
  },
);

还有一个方便的方法,相当于上面的代码,但更简洁:

final zeroDurationTimer = Timer.run(() {
  //Execute this callback ASAP but asynchronously
});

在 Flutter 中创建可重启的计时器

正如我们在上面看到的,我们可以Timer通过调用该cancel()方法来取消。但是,Timer除非我们重新创建计时器实例,否则类中没有直接的方法来重新启动相同的计时器。

为了实现这一点,Flutter 具有RestartableTimer. 这个可重启定时器的底层实现与创建定时器实例相同。因为Timer是一个抽象类,所以RestartableTimer是它的具体实现之一。

RestartableTimer是包含实用程序类的 async 包的一部分dart:async。它已经是 Flutter SDK 的一部分,可以通过以下方式导入:

import 'package:async/async.dart';

导入后,我们可以创建一个简单的 3 秒RestartableTimer:

final restartableTimer = RestartableTimer(
  const Duration(seconds: 3),
  () {
    //Callback
  },
);

//Restart the timer
restartableTimer.reset();

重要的是要注意,重置计时器会从其原始持续时间重新创建计时器。RestartableTimer仅用于非周期性定时器。

FlutterTimer示例和用例

在导航离开之前创建特定持续时间的屏幕

在开发应用程序时,我们必须经常创建一个在一段时间内保持活动状态的屏幕,然后继续应用程序的流程。这可能是启动画面、“您的订单已下单”屏幕或任何其他过渡元素。

在这个例子中,我们有第一个屏幕,RelaxingScreen,它将对用户可见 3 秒,并NextScreen在时间结束时导航到。

使用时Timer,重要的是在处置时取消。这保证了当从树中删除相应的小部件时没有计时器保持活动状态。

看看下面的代码以使用Timer该类实现相同的目的:

import 'dart:async';

import 'package:flutter/material.dart';

const Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: darkBlue,
      ),
      debugShowCheckedModeBanner: false,
      home: const Scaffold(
        body: Center(
          child: RelaxingScreen(),
        ),
      ),
    );
  }
}

/// Releaxing screen that stays visible for 3 seconds
class RelaxingScreen extends StatefulWidget {
  const RelaxingScreen({Key? key}) : super(key: key);

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

class _RelaxingScreenState extends State {
  //Declare a timer
  Timer? timer;


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

    /// Initialize timer for 3 seconds, it will be active as soon as intialized
    timer = Timer(
      const Duration(seconds: 3),
      () {
        /// Navigate to seconds screen when timer callback in executed
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => const NextScreen(),
          ),
        );
      },
    );
  }

  /// cancel the timer when widget is disposed, 
  /// to avoid any active timer that is not executed yet
  @override
  void dispose() {
    super.dispose();
    timer?.cancel();
  }

  @override
  Widget build(BuildContext context) {
    return const Text("Relaxing Screen!!");
  }
}

class NextScreen extends StatelessWidget {
  const NextScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: Text("Next Screen"),
      ),
    );
  }
}

在上面的例子中,我们首先RelaxingScreen对用户可见,这是一个有状态的小部件,然后我们必须在initState() 注册TIMER。

在 3 秒计时器后,将触发回调以将用户导航到NextScreen。取消dispose()方法上的计时器很重要,以避免在用户离开RelaxingScreen回调触发之前可能创建的任何异常。

自动将计数器应用程序增加 1 秒

作为 Flutter 开发人员,您很可能熟悉非常著名的计数器应用程序 😅。在这个例子中,我们将创建一个类似的计数器应用程序,但不是按 + 按钮,我们将每 1 秒自动增加计数器并通过重建Text小部件将其显示给用户:

import 'dart:async';

import 'package:flutter/material.dart';

const Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: darkBlue,
      ),
      debugShowCheckedModeBanner: false,
      home: const Scaffold(
        body: Center(
          child: CounterScreen(),
        ),
      ),
    );
  }
}

class CounterScreen extends StatefulWidget {
  const CounterScreen({Key key}) : super(key: key);

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

class _CounterScreenState extends State {
  /// declare a cound variable with initial value
  int count = 0;

  /// declare a timer
  Timer timer;

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

    /// Initialize a periodic timer with 1 second duration
    timer = Timer.periodic(
      const Duration(seconds: 1),
      (timer) {
        /// callback will be executed every 1 second, increament a count value
        /// on each callback
        setState(() {
          count++;
        });
      },
    );
  }

  /// Since periodic timer doesn't cancels untill expicitely called
  /// It is important to cancel them on dispose, so that it doesn't stays active
  /// when widget is not binded to tree
  @override
  void dispose() {
    super.dispose();
    timer?.cancel();
  }

  @override
  Widget build(BuildContext context) {
    return Text("Counter reached $count");
  }
}

在上面的例子中,有一个StatefulWidget, CounterScreen,我们注册了一个周期性的计时器,每 1 秒滴答一次。在每次触发回调时,我们都会增加状态变量count。然后Text小部件显示 的最新值count。

也可以采用类似的方法来显示倒数计时器或剩余持续时间(例如,一次性密码超时)。

FlutterTimer类的局限性

当我们想到通用计时器时,通常会期望诸如暂停或恢复计时器之类的实用程序。到目前为止,我们已经看到,Flutter 的Timer类旨在为以后安排一个代码块或在特定持续时间内重复执行它。

要在 Flutter 中实现诸如暂停和恢复计时器之Stopwatch类的实用程序,您可以使用类.

总结

Flutter 的Timer类处理与倒数计时器相关的每个用例。有了它,我们可以创建一个具有完整实用程序的普通和定期计时器,例如取消计时器、识别计时器是否处于活动状态以及滴答计数。

我们还看到了如何使用RestartableTimer可以重置和再次启动计时器。

感谢您的阅读,不要忘记分享您在我这儿学习到的知识,我是坚果。