【问题标题】:How do I create a time-based Flutter App?如何创建基于时间的 Flutter App?
【发布时间】:2019-03-07 05:48:18
【问题描述】:

我需要创建一个登录表单。用户成功登录后,我需要启动某种计时器(例如:3 分钟),所以如果用户对应用程序或其他词没有反应,如果颤动应用程序状态暂停、暂停或不活动超过 3 分钟。该应用程序将转到主登录页面。只要用户与应用程序进行交互,我就需要取消计时器,并且只需要启动计时器应用程序状态为暂停、暂停或非活动状态。我该怎么做?

我尝试实现“WidgetsBindingObserver”,但它看起来不像我想要的那样工作。如果用户成功进入并在应用程序中导航,则 WidgetsBindingObserver 失败(错误:小部件的状态对象不再出现在小部件树中)。

我的问题是如何实现基于时间的 Flutter 应用程序生命周期,只要用户与应用程序有交互?如果没有用户交互,生命周期计时器将启动,如果在计时器结束之前有用户交互,则必须取消计时器。

class _MyUserHomePageState extends State<MyUserHomePage> with WidgetsBindingObserver {

  AppLifecycleState _appLifecycleState;



@override
void initState() {
  _appStatePasue = false;
  WidgetsBinding.instance.addObserver(this);
  super.initState();
}


// TODO: DID_CHANGE_APP_LIFE_CYCLE
void didChangeAppLifecycleState(AppLifecycleState state) {
  setState(() {
    _appLifecycleState = state;
    if(_appLifecycleState == AppLifecycleState.paused ||
        _appLifecycleState == AppLifecycleState.inactive ||
        _appLifecycleState == AppLifecycleState.suspending) {
      _appStatePasue = true;
      print("timer---fired: $_appLifecycleState");
      _timer = Timer.periodic(Duration(minutes: 1), _capitalCallback);
      print(_appLifecycleState);
    } else {
      _appStatePasue = false;
    }
  });
}

// TODO: APP_LIFE_CYCLE__CALLBACK
void _capitalCallback(_timer) {
  if(_appStatePasue == true) {
    _timer.cancel();
    print("return---main---page: $_appLifecycleState");
    setState(() {
      Navigator.push(
          context,
          SlideRightRoute(widget: MyApp())
      );
    });
  } else {
    _timer.cancel();
    print("timer---canceled: $_appLifecycleState");
  }
}


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

@override
void onDeactivate() {
  super.deactivate();
}

@override
Widget build(BuildContext context) {
    return new Scaffold (

    );
}

}

【问题讨论】:

    标签: timer flutter lifecycle


    【解决方案1】:

    更新到Kirollos Morkos's Answer 我们已使用NavigatorState 键注销。

    这里是AppRootState的完整代码。

    class AppRootState extends State<AppRoot> {
      Timer _timer;
      bool forceLogout = false;
      final navigatorKey = GlobalKey<NavigatorState>();
    
      @override
      void initState() {
        super.initState();
    
        _initializeTimer();
      }
    
      void _initializeTimer() {
        _timer = Timer.periodic(const Duration(minutes: 10), (_) => _logOutUser());
      }
    
      void _logOutUser() {
        // Log out the user if they're logged in, then cancel the timer.
        // You'll have to make sure to cancel the timer if the user manually logs out
        //   and to call _initializeTimer once the user logs in
        _timer.cancel();
        setState(() {
          forceLogout = true;
        });
      }
    
      // You'll probably want to wrap this function in a debounce
      void _handleUserInteraction([_]) {
        print("_handleUserInteraction");
        _timer.cancel();
        _initializeTimer();
      }
    
      void navToHomePage(BuildContext context) {
        //Clear all pref's
        SharedPreferencesHelper.clearAllValues();
    
        navigatorKey.currentState.pushAndRemoveUntil(
            MaterialPageRoute(builder: (context) => LoginPage()),
            (Route<dynamic> route) => false);
      }
    
      @override
      Widget build(BuildContext context) {
        if (forceLogout) {
          print("ForceLogout is $forceLogout");
          navToHomePage(context);
        }
        return GestureDetector(
            onTap: _handleUserInteraction,
            onPanDown: _handleUserInteraction,
            onScaleStart: _handleUserInteraction,
    
            // ... repeat this for all gesture events
            child: MaterialApp(
              navigatorKey: navigatorKey,
              // ...
              // ...
              ));
      }
    }
    

    【讨论】:

      【解决方案2】:

      访问每个屏幕的计时器,并在会话超时后关闭所有屏幕并打开登录屏幕。

      在单独的 Constants.dart 文件中将 Session Expiry 时间定义为 static

      static const int sessionExpireTimeout = 30; //in seconds
      

      现在成功登录后,在下一个屏幕,即 HomeScreen(),初始化一个名为 Future.delayed() 的方法,并在 Widget build(BuildContext上下文) 方法:

      Future.delayed(const Duration(seconds: Constants.sessionTimeout), () async {
            await FirebaseAuth.instance.signOut(); // Firebase Sign out before exit
            // Pop all the screens and Pushes Login Screen only
            Navigator.of(context)
                .pushNamedAndRemoveUntil(LoginScreen(), (route) => false);
          });
      

      记住,您在使用 Navigator 时不必弹出此 HomeScreen()。 每当您想导航到另一个屏幕时。使用 pushNamed() 或 push() 方法。 然后切换到另一个屏幕后,您可以使用任何导航器方法。

      【讨论】:

      • 你不能让几乎每一个段落都完全加粗吗?
      【解决方案3】:

      对于任何有导航问题的人,使用上下文作为静态参数的简单创建类,然后从应用程序中的任何第一个小部件设置上下文,然后您可以在超时函数中使用上下文 创建类:

      class ContextClass{ static BuildContext CONTEXT; }

      从您的第一个小部件构建方法中设置上下文,像这样

          ContextClass.CONTEXT=context;
      

      并像这样在你的超时功能中使用

              Navigator.of(ContextClass.CONTEXT).pushNamedAndRemoveUntil('<Your Route>', (Route<dynamic> route) => false);
      

      【讨论】:

        【解决方案4】:

        您可以使用Timer 类在 3 分钟不活动后触发注销功能。您可以尝试将整个应用程序包装在 GestureDetector 中,以重置任何事件的计时器。您只需要确保您的应用程序中的任何其他GestureDetectors 都使用HitTestBehavior.translucent,以便将事件传播到您的根侦听器。这是一个完整的例子:

        import 'dart:async';
        import 'package:flutter/material.dart';
        
        void main() => runApp(MyApp());
        
        class MyApp extends StatelessWidget {
          @override
          Widget build(BuildContext context) => AppRoot();
        }
        
        class AppRoot extends StatefulWidget {
          @override
          AppRootState createState() => AppRootState();
        }
        
        class AppRootState extends State<AppRoot> {
          Timer _timer;
        
          @override
          void initState() {
            super.initState();
        
            _initializeTimer();
          }
        
          void _initializeTimer() {
            _timer = Timer.periodic(const Duration(minutes: 3), (_) => _logOutUser);
          }
        
          void _logOutUser() {
            // Log out the user if they're logged in, then cancel the timer.
            // You'll have to make sure to cancel the timer if the user manually logs out
            //   and to call _initializeTimer once the user logs in
            _timer.cancel();
          }
        
          // You'll probably want to wrap this function in a debounce
          void _handleUserInteraction([_]) {
            if (!_timer.isActive) {
              // This means the user has been logged out
              return;
            }
        
            _timer.cancel();
            _initializeTimer();
          }
        
          @override
          Widget build(BuildContext context) {
            return GestureDetector(
              onTap: _handleUserInteraction,
              onPanDown: _handleUserInteraction,
              onScaleStart: _handleUserInteraction,
              // ... repeat this for all gesture events
              child: MaterialApp(
                // ... from here it's just your normal app,
                // Remember that any GestureDetector within your app must have
                //   HitTestBehavior.translucent
              ),
            );
          }
        }
        

        更新:我刚刚发现Listener 类在这里可能比GestureDetector 更有意义。我个人从未使用过它,但请随意尝试!查看documentation on gestures 了解更多信息。

        【讨论】:

        • 马科斯感谢您提供的信息。我有 40 多页,那么如何将整个应用包装在 GestureDetector 中?
        • 只要让它成为您的根级小部件之一。我很快就会发布一个完整的例子。
        • 感谢您的信息。只有“HitTestBehavior.translucent”部分我不明白?如何为其他页面实现此功能?
        • 我明白了。非常感谢:)
        • 感谢@Kirollos,计时器正在工作,但我无法导航到登录页面。我可以删除用户的会话凭据,但需要重定向到登录页面。有什么想法吗?
        猜你喜欢
        • 1970-01-01
        • 2011-01-01
        • 2021-11-15
        • 2021-10-08
        • 2020-06-15
        • 2019-09-22
        • 2019-11-14
        • 2019-09-14
        • 1970-01-01
        相关资源
        最近更新 更多