Paulo Keller
Paulo Keller

Reputation: 148

Bloc navigation on state change

I'm really new with flutter blocs and I having some problems with a bloc implementation, I'm trying to navigate after a state change in my splash screen widget.

After the state update to InitSuccess it should navigate to LoginScreen, but this navigation occurs many times.

I'm not able to understand what to do after the state change's to InitSuccess, after this the bloc keeps alive and calling many, many times LoginScreen.

Splash Screen

class SplashScreen extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _SplashScreenState();
}

class _SplashScreenState extends State<SplashScreen> {
  SplashBloc _splashBloc;
  final _scaffoldKey = GlobalKey<ScaffoldState>();

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

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

  void _init() {
    Future.delayed(Duration.zero, () {
      checkDeviceConnection(context);
      BlocSupervisor().delegate = SplashBlocDelegate();
      final bool isIOS = Theme.of(context).platform == TargetPlatform.iOS;
      _splashBloc = SplashBloc(
        firebaseService: FirebaseService(context),
        authService: AuthService(context),
        devicesService: DevicesService(context),
      );
      _splashBloc.dispatch(SplashInitEvent(isIOS: isIOS));
    });

@override
  Widget build(BuildContext context) {
    SystemChrome.setEnabledSystemUIOverlays([]);
    return BlocBuilder<SplashEvent, SplashState>(
      bloc: _splashBloc,
      builder: (
        BuildContext context,
        SplashState state,
      ) {
        if (state is InitFailure) {
          Future.delayed(Duration.zero, () {
            showWarningSnackBar(_scaffoldKey, state.error);
          });
        }

        if (state is InitSuccess) {
          Future.delayed(Duration.zero, () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => LoginScreen(),
              ),
            );
          });
        }

        return Scaffold(
          key: _scaffoldKey,
          body: Container(
            decoration: appScreenGradient,
            alignment: Alignment.center,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Image.asset(
                  "assets/images/splash_screen/logo_splash.png",
                  width: 172.88,
                  height: 144.55,
                  fit: BoxFit.contain,
                ),
                SizedBox(
                  height: 20.0,
                ),
                LoadingSpinner(
                  spinnerColor: Theme.of(context).primaryColorLight,
                ),
              ],
            ),
          ),
        );
      },
    );
  }

Splash Bloc

class SplashBloc extends Bloc<SplashEvent, SplashState> {
  final FirebaseService firebaseService;
  final DevicesService devicesService;
  final AuthService authService;
  final UserPreferences _userPreferences = UserPreferences();

  SplashBloc({
    @required this.firebaseService,
    @required this.devicesService,
    @required this.authService,
  });

  @override
  Stream<SplashEvent> transform(Stream<SplashEvent> events) {
    return (events as Observable<SplashEvent>).debounce(
        Duration(milliseconds: 500));
  }

  @override
  get initialState => SplashInitial();

  @override
  Stream<SplashState> mapEventToState(currentState, event) async* {
    if (event is SplashInitEvent) {
      if (currentState is SplashInitial) {
        yield InitLoading();

        try {
          firebaseService.togglePerformanceCollection(true);
          firebaseService.firebaseCloudMessagingListeners();

          String firebaseToken = await firebaseService
              .getFirebaseMessagingToken();
          bool isRegistered =
              await _userPreferences.getIsDeviceRegistered() ?? false;

          if (!isRegistered) {
            final String platform = event.isIOS ? 'IOS' : 'Android';
            final deviceInfo = await devicesService.getDeviceInfo(platform);
            isRegistered = await devicesService.register(
              deviceToken: firebaseToken,
              deviceInfo: deviceInfo,
            );
            if (isRegistered) {
              _userPreferences.setIsDeviceRegistered(true);
            }
          }

          yield InitSuccess();
        } catch (e) {
          yield InitFailure(error: e.toString());
        }
      }
    }

    if (event is SplashInitialEvent) {
      yield SplashInitial();
    }
  }
}

Upvotes: 3

Views: 2094

Answers (1)

Paulo Keller
Paulo Keller

Reputation: 148

I found the following solution:

if (state is LoggedIn) {
  WidgetsBinding.instance.addPostFrameCallback((_) { 
    // Navigation
  });
}

I wrapped my navigation with this addPostFrame callback for delaying its appearance.

Upvotes: 8

Related Questions