SankethBK / local_session_timeout

Redirect user to authentication page if the application doesn't receive any user interaction, or been running in the background for "x" duration.
https://pub.dev/packages/local_session_timeout
BSD 3-Clause "New" or "Revised" License
12 stars 19 forks source link

reset timer when user interactive on ui #6

Closed thisKK closed 2 years ago

thisKK commented 2 years ago
 child: Listener(
            onPointerDown: (event) {
              sessionStateStream.add(SessionState.stopListening);
            },
            onPointerUp: (event) {
              sessionStateStream.add(SessionState.startListening);
            },
            child: Stack(...

I try to detect user activity on the reading page like this but after do like this timer does not start again is it the limit to this lib? or what am I doing wrong?

SankethBK commented 2 years ago

Doesn't look like an issue to me, can you please post a minimal working demo code

SankethBK commented 2 years ago

Hi @thisKK , there was an issue with _userTapActivityRecordEnabled variable, which caused the timer to not start when stopped and started in short interval. I have fixed the issue in version 2.1.1 can you please check if your issue is resilved

thisKK commented 2 years ago

hi, @SankethBK thank for your support. I had tried to fix follow in the version 2.1.1 draft and it works the timer come back to start again

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:local_session_timeout/local_session_timeout.dart';

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

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

  final _navigatorKey = GlobalKey<NavigatorState>();
  NavigatorState get _navigator => _navigatorKey.currentState!;

  /// Make this stream available throughout the widget tree with with any state management library
  /// like bloc, provider, GetX, ..
  final sessionStateStream = StreamController<SessionState>();

  @override
  Widget build(BuildContext context) {
    final sessionConfig = SessionConfig(
      invalidateSessionForAppLostFocus: const Duration(seconds: 3),
      invalidateSessionForUserInactiviity: const Duration(seconds: 5),
    );
    sessionConfig.stream.listen((SessionTimeoutState timeoutEvent) {
      // stop listening, as user will already be in auth page
      sessionStateStream.add(SessionState.stopListening);
      if (timeoutEvent == SessionTimeoutState.userInactivityTimeout) {
        // handle user  inactive timeout
        _navigator.push(MaterialPageRoute(
          builder: (_) => AuthPage(
              sessionStateStream: sessionStateStream,
              loggedOutReason: "Logged out because of user inactivity"),
        ));
      } else if (timeoutEvent == SessionTimeoutState.appFocusTimeout) {
        // handle user  app lost focus timeout
        _navigator.push(MaterialPageRoute(
          builder: (_) => AuthPage(
              sessionStateStream: sessionStateStream,
              loggedOutReason: "Logged out because app lost focus"),
        ));
      }
    });
    return SessionTimeoutManager(
      sessionConfig: sessionConfig,
      sessionStateStream: sessionStateStream.stream,
      child: MaterialApp(
        navigatorKey: _navigatorKey,
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: AuthPage(
          sessionStateStream: sessionStateStream,
        ),
      ),
    );
  }
}

class AuthPage extends StatelessWidget {
  StreamController<SessionState> sessionStateStream;

  String loggedOutReason;

  AuthPage(
      {Key? key, required this.sessionStateStream, this.loggedOutReason = ""})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              if (loggedOutReason != "")
                Container(
                  padding: const EdgeInsets.symmetric(
                    vertical: 10,
                    horizontal: 15,
                  ),
                  child: Text(loggedOutReason),
                ),
              const SizedBox(
                height: 20,
              ),
              ElevatedButton(
                onPressed: () async {
                  // start listening only after user logs in
                  sessionStateStream.add(SessionState.startListening);
                  loggedOutReason = await Navigator.of(context).push(
                    MaterialPageRoute(
                      builder: (_) => MyHomePage(
                        sessionStateStream: sessionStateStream,
                      ),
                    ),
                  );
                },
                child: const Text("Login"),
              ),
            ],
          ),
        ));
  }
}

class MyHomePage extends StatelessWidget {
  StreamController<SessionState> sessionStateStream;

  MyHomePage({Key? key, required this.sessionStateStream}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text("Home page"),
            const SizedBox(height: 10),
            ElevatedButton(
              onPressed: () async {
                await Navigator.of(context).push(
                  MaterialPageRoute(
                    builder: (_) => ReadingPage(
                      sessionStream: sessionStateStream,
                    ),
                  ),
                );
              },
              child: const Text("Reading page"),
            ),
            const SizedBox(height: 10),
            ElevatedButton(
              onPressed: () {
                Navigator.of(context).push(
                  MaterialPageRoute(
                    builder: (_) => WritingPage(
                      sessionStream: sessionStateStream,
                    ),
                  ),
                );
              },
              child: const Text("Writing Page"),
            ),
          ],
        ),
      ),
    );
  }
}

// This [age can text content, which user might be reading without any user activity
// If you want to disable the session timeout listeners when user enters such pages
// follow the below code
class ReadingPage extends StatefulWidget {
  final StreamController<SessionState> sessionStream;
  const ReadingPage({Key? key, required this.sessionStream}) : super(key: key);

  @override
  State<ReadingPage> createState() => _ReadingPageState();
}

class _ReadingPageState extends State<ReadingPage> {
  @override
  Widget build(BuildContext context) {
    widget.sessionStream.add(SessionState.startListening);
    return Scaffold(
        appBar: AppBar(),
        body: Listener(
          onPointerDown: (event) =>
              {widget.sessionStream.add(SessionState.stopListening)},
          onPointerUp: (event) =>
              {widget.sessionStream.add(SessionState.startListening)},
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: const [
                Text(
                    "This page can have lot of extent content, and user might be reading this without performing any user activity. So you might want to disable sesison timeout listeners only for this page"),
              ],
            ),
          ),
        ));
  }
}

// If the user is typing into the textbox, you may want to disable the session
// timeout listeners because typing events aren't captured by session timeout
// manager and it may conclude that user is inactive
class WritingPage extends StatefulWidget {
  final StreamController<SessionState> sessionStream;
  const WritingPage({Key? key, required this.sessionStream}) : super(key: key);

  @override
  State<WritingPage> createState() => _WritingPageState();
}

class _WritingPageState extends State<WritingPage> {
  @override
  Widget build(BuildContext context) {
    widget.sessionStream.add(SessionState.startListening);
    if (MediaQuery.of(context).viewInsets.bottom > 0) {
      // softkeyboard is open
      widget.sessionStream.add(SessionState.stopListening);
    } else {
      // keyboard is closed
      widget.sessionStream.add(SessionState.startListening);
    }
    return Scaffold(
      appBar: AppBar(),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: const [
            Text(
                "If the user is typing into the textbox, you may want to disable the session timeout listeners because typing events aren't captured by session timeout manager and it may conclude that user is inactive"),
            SizedBox(
              height: 20,
            ),
            TextField(
              decoration: InputDecoration(
                border: OutlineInputBorder(
                    borderSide:
                        BorderSide(color: Color.fromARGB(255, 16, 17, 17))),
                hintText: 'Start typing here',
                helperText: 'when keyboard is open, session won"t expire',
                prefixText: ' ',
                suffixText: 'USD',
                suffixStyle: TextStyle(
                  color: Color.fromARGB(255, 14, 14, 14),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}