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

Localizations Delegate #10

Closed peter910921 closed 1 year ago

peter910921 commented 1 year ago

Hi,

I have this issue where the whole MaterialApp() seems like rebuild whenever i try to do sessionStateStream.add(). After troubleshooting a while i found out that after i remove the AppLocalizations delegate from my MaterialApp(), it is working as usual without keep rebuilding everytime i do sessionStateStream.add(). Do you have any idea what is going on?

Thanks in advance.

class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final AuthBloc _signInBloc = AuthBloc();

  @override
  Widget build(BuildContext context) {
    final sessionConfig = SessionConfig(
      invalidateSessionForAppLostFocus: const Duration(seconds: 240),
      invalidateSessionForUserInactivity: const Duration(seconds: 240),
    );
    sessionConfig.stream.listen((SessionTimeoutState timeoutEvent) {
      // stop listening, as user will already be in auth page
      _signInBloc.state.sessionStateStream.add(SessionState.stopListening);
      if (timeoutEvent == SessionTimeoutState.userInactivityTimeout) {
        // handle user  inactive timeout
        _signInBloc.add(Logout());
        navigatorKey.currentState?.popUntil((route) => route.isFirst);
      } else if (timeoutEvent == SessionTimeoutState.appFocusTimeout) {
        _signInBloc.add(Logout());
        navigatorKey.currentState?.popUntil((route) => route.isFirst);
      }
    });
    return SessionTimeoutManager(
      sessionConfig: sessionConfig,
      sessionStateStream: _signInBloc.state.sessionStateStream.stream,
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        theme: !appStore.isDarkModeOn
            ? AppThemeData.lightTheme
            : AppThemeData.darkTheme,
        onGenerateRoute: (settings) {
          return generateRoute(settings, _signInBloc, sessionConfig);
        },
        navigatorKey: navigatorKey,
        scrollBehavior: SBehavior(),
        supportedLocales: LanguageDataModel.languageLocales(),
        localizationsDelegates: [
          THIS LINE HERE
          // AppLocalizations(),
          GlobalMaterialLocalizations.delegate,
          GlobalWidgetsLocalizations.delegate
        ],
        localeResolutionCallback: (locale, supportedLocales) => locale,
        locale: Locale(appStore.selectedLanguageCode),
      ),
    );
  }

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

My AppLocalizations class

class AppLocalizations extends LocalizationsDelegate<BaseLanguage> {
  const AppLocalizations();

  @override
  Future<BaseLanguage> load(Locale locale) async {
    switch (locale.languageCode) {
      case 'en':
        return LanguageEn();
      case 'ar':
        return LanguageAr();
      case 'hi':
        return LanguageHi();
      case 'fr':
        return LanguageFr();
      default:
        return LanguageEn();
    }
  }

  @override
  bool isSupported(Locale locale) => LanguageDataModel.languages().contains(locale.languageCode);

  @override
  bool shouldReload(LocalizationsDelegate<BaseLanguage> old) => false;
}
SankethBK commented 1 year ago

Hi,

you are referring to add in this line right?

 _signInBloc.state.sessionStateStream.add(SessionState.stopListening);
peter910921 commented 1 year ago

Hi,

you are referring to add in this line right?

 _signInBloc.state.sessionStateStream.add(SessionState.stopListening);

Yes this line. Other than this line, i also have a page which login page whenever they press login i will start listen _signInBloc.state.sessionStateStream.add(SessionState.startListening);

Same thing happen when they press the login button which it will start listening

SankethBK commented 1 year ago

Can you post the code related to AuthBloc? (Onlythe state definition of the bloc)

peter910921 commented 1 year ago

Can you post the code related to AuthBloc? (Onlythe state definition of the bloc)

// ignore_for_file: public_member_api_docs, sort_constructors_first
part of 'auth_bloc.dart';

class AuthState extends Equatable {
  final LoadingStatus status;
  final CustomErr err;
  final StreamController<SessionState> sessionStateStream;
  final bool sessionExpired;

  AuthState({
    required this.status,
    required this.err,
    required this.user,
    required this.sessionStateStream,
    required this.sessionExpired,
  });

  factory AuthState.initial() {
    return AuthState(
      sessionStateStream: StreamController<SessionState>(), 
      sessionExpired: false,
      status: LoadingStatus.initial, 
      err: CustomErr(errMsg: '')
    );
  }

  @override
  List<Object> get props => [status];

  @override
  bool get stringify => true;

  AuthState copyWith({
    LoadingStatus? status,
    CustomErr? err,
    StreamController<SessionState>? sessionStateStream,
    SessionConfig? sessionConfig,
    bool? sessionExpired,
  }) {
    return AuthState(
      status: status ?? this.status,
      err: err ?? this.err,
      sessionStateStream: sessionStateStream ?? this.sessionStateStream,
      sessionExpired: sessionExpired ?? this.sessionExpired,
    );
  }
}
SankethBK commented 1 year ago

Little unusual that it goes away after Applocalizations is commented.

While AppLocalizations is not commented, did you notice rebuilds for MaterialApp at any other point (other than sessionStream.add)

peter910921 commented 1 year ago

Little unusual that it goes away after Applocalizations is commented.

While AppLocalizations is not commented, did you notice rebuilds for MaterialApp at any other point (other than sessionStream.add)

Everything just working fine actually only until the point i click on the login button which trigger

context.read<AuthBloc>().state.sessionStateStream.add(SessionState.startListening);

then it rebuild but it doesn't happen after i comment out AppLocalizations(). Its really kind a weird, i try to move SessionTimeoutManager to the Login Page, it works without comment AppLocalizations(), but then it is not at the top of the widget tree, so whenever i pushNamed, it cant detect user activity anymore, so i have no choice to put at MaterialApp().

I tried it without using bloc and the result is still the same.

SankethBK commented 1 year ago

Can you post a minimal example to reproduce it if possible

peter910921 commented 1 year ago

Can you post a minimal example to reproduce it if possible

i think i need some time on this. Will come back to you once i finished the minimal example. Thanks.

SankethBK commented 1 year ago

@peter910921 let me know if you need any help to create the minimal example

peter910921 commented 1 year ago

@SankethBK Hi sorry for the late reply, been busy on my work.

Actually i think i end up changing my app localizations delegates by following the flutter official guide. After that all working fine now. https://docs.flutter.dev/development/accessibility-and-localization/internationalization

localizationsDelegates: [
    AppLocalizations.delegate,
    GlobalCupertinoLocalizations.delegate,
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate
],
SankethBK commented 1 year ago

Cool