如何在 Flutter 中一段时间​​不活动后执行函数

dea*_*zvi 10 timer dart flutter

我需要创建一个应用程序,在一段时间不活动后将用户导航到。

我尝试将我的应用程序包装在 GestureDetector 中并在指定时间段后运行计时器,如下所示,但它不起作用:

class _MyAppState extends State<MyApp> {
  Timer _timer;

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

    _initializeTimer();
  }

  void _initializeTimer() {
    _timer = Timer.periodic(const Duration(seconds: 20), (_) => _logOutUser);
  }

  void _logOutUser() {
Navigator.push(
        context,
        MaterialPageRoute(
            builder: (context) => WelcomePage()));
    _timer.cancel();
  }

  void _handleUserInteraction([_]) {
    if (!_timer.isActive) {
      return;
    }

    _timer.cancel();
    _initializeTimer();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleUserInteraction,
      onPanDown: _handleUserInteraction,
      child: MaterialApp(
        title: 'Hello',
        home: WelcomePage(),
        routes: <String, WidgetBuilder>{
          '/welcome': (BuildContext context) => new WelcomePage(),
          '/login': (BuildContext context) => new LoginPage(),
        },
        debugShowCheckedModeBanner: false,
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是在我的 flutter 应用程序中一段时间​​不活动后,我应该如何最好地实现运行功能?

小智 9

FWIW,我GestureDetector按照建议尝试使用 a ,但没有按预期工作。问题是当没有活动时,我收到了连续的手势流。这包括当我尝试更严格的 onTap: 回调时。

我在模拟器上的调试模式下看到了这一点。我没有进一步试验以查看真实手机是否表现出相同的行为,因为即使它们现在没有,将来也可能:显然没有规范保证 GestureDetector不会收到虚假事件。对于不活动超时等与安全相关的事情,这是不可接受的。

对于我的用例,我决定改为检测应用程序何时不可见超过设定的时间是可以的。我对预期用途的推理是,真正的危险是当应用程序不可见时,他们忘记了它在那里。

设置这种不活动超时非常容易。我安排startKeepAlive()在应用程序访问敏感信息的那一刻(例如在输入密码后)打电话。对于我的使用,超时后直接退出应用程序就可以了;显然,如果需要,可以变得更复杂。Anyhoo,这是相关的代码:

const _inactivityTimeout = Duration(seconds: 10);
Timer _keepAliveTimer;

void _keepAlive(bool visible) {
  _keepAliveTimer?.cancel();
  if (visible) {
    _keepAliveTimer = null;
  } else {
    _keepAliveTimer = Timer(_inactivityTimeout, () => exit(0));
  }
}

class _KeepAliveObserver extends WidgetsBindingObserver {
  @override didChangeAppLifecycleState(AppLifecycleState state) {
    switch(state) {
      case AppLifecycleState.resumed:
        _keepAlive(true);
        break;
      case AppLifecycleState.inactive:
      case AppLifecycleState.paused:
      case AppLifecycleState.detached:
        _keepAlive(false);  // Conservatively set a timer on all three
        break;
    }
  }
}

/// Must be called only when app is visible, and exactly once
void startKeepAlive() {
  assert(_keepAliveTimer == null);
  _keepAlive(true);
  WidgetsBinding.instance.addObserver(_KeepAliveObserver());
}

Run Code Online (Sandbox Code Playgroud)

在生产中,我可能会延长超时时间:-)


Igo*_*din 8

这是我的解决方案。一些细节:

  • 我说Navigator家里应用程序的窗口小部件,因此它可以访问导航之外MaterialApp通过GlobalKey;
  • GestureDetector行为设置为HitTestBehavior.translucent将点击传播到其他小部件;
  • 您不需要Timer.periodic为此目的。周期性定时器用于重复执行回调(例如,每10秒);
  • 计时器设置小部件何时初始化以及何时发生任何点击。任何后续点击都将取消旧计时器并创建一个新计时器。_logOutUser调用回调后,定时器被取消(如果有的话),每条路由都被弹出并推送新路由。
class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final _navigatorKey = GlobalKey<NavigatorState>();
  Timer _timer;

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

  void _initializeTimer() {
    if (_timer != null) {
      _timer.cancel();
    }

    _timer = Timer(const Duration(seconds: 3), _logOutUser);
  }

  void _logOutUser() {
    _timer?.cancel();
    _timer = null;

    // Popping all routes and pushing welcome screen
    _navigatorKey.currentState.pushNamedAndRemoveUntil('welcome', (_) => false);
  }

  void _handleUserInteraction([_]) {
    _initializeTimer();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      behavior: HitTestBehavior.translucent,
      onTap: _handleUserInteraction,
      onPanDown: _handleUserInteraction,
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: Navigator(
          initialRoute: 'welcome',
          key: _navigatorKey,
          onGenerateRoute: (settings) {
            return MaterialPageRoute(
              builder: (context) {
                return Scaffold(
                  appBar: AppBar(),
                  body: SafeArea(
                    child: Text(settings.name)
                  ),
                  floatingActionButton: FloatingActionButton(
                    onPressed: () => Navigator.of(context).pushNamed('test'),
                  ),
                );
              }
            );
          },
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)


Bla*_*ckd 5

对于想要严格回答标题问题(“如何在 Flutter 中一段时间​​不活动后执行函数”)的人来说,这是完整的解决方案:

  • 将您的 MaterialApp 包裹在GestureDetector 中,以便您可以检测敲击和平移。
  • 在 GestureDetector 中设置以下属性以避免与标准手势系统混淆:
    • 行为:HitTestBehavior.translucent
  • 在 GestureDetector 中设置回调以在发生点击/平移活动时重新启动计时器:
    • onTap: (_) => _initializeTimer()
    • onPanDown: (_) => _initializeTimer()
    • onPanUpdate: (_) => _initializeTimer()
  • 我们不应忘记打字用户也是活动用户,因此我们还需要在小部件的每个 TextField 中设置回调:
    • onChanged: (_) => _initializeTimer()
  • 添加定时器_timer; 到您的 class_SomethingState。
  • 最后,在 initState() 处初始化 _timer,编写 _initializeTimer() 并编写 _handleInactivity() 包括当发生足够多的不活动时所需的操作:
    @覆盖
    无效的初始化状态(){
      super.initState();
      _initializeTimer();
    }

    // 启动/重启定时器
    void _initializeTimer() {
      如果(_timer != null){
        _timer.cancel();
      }
      // 5 分钟后设置动作
      _timer = Timer(const Duration(minutes: 5), () => _handleInactivity());
    }

    无效_handleInactivity(){
      _timer?.cancel();
      _timer = 空;

      // TODO: 在这里输入你想要的代码
    }