Search code examples
flutterflutter-animationbloc

How to add animated transitions when changing Widget on BLoC pattern?


so I was following bloc login tutorial, and while I managed to complete it, I'm still fairly new to Flutter & Dart.

There is a portion of the code where, depending on the state, the code returns a different widget, instead of a new Scaffold. Since it's not using routes, the transition between pages looks choppy and akward.

return BlocProvider<AuthenticationBloc>(
  bloc: authenticationBloc,
  child: MaterialApp(
    debugShowCheckedModeBanner: false,
    home: BlocBuilder<AuthenticationEvent, AuthenticationState>(
      bloc: authenticationBloc,
      builder: (BuildContext context, AuthenticationState state) {
        if (state is AuthenticationUninitialized) {
          return SplashPage();
        }
        if (state is AuthenticationAuthenticated) {
          return HomePage();
        }
        if (state is AuthenticationUnauthenticated) {
          return LoginPage(userRepository: userRepository);
        }
        if (state is AuthenticationLoading) {
          return LoadingIndicator();
        }
      },
    ),
  ),
);

I've tried adding a Navigation.push wrapping the returns, like this:

if (state is AuthenticationUninitialized) {
  Navigation.push(
    return SplashPage();
  ),
}

But while is not syntactically wrong, that crashes the app. Does anyone know a way to implement this while maintaining the BLoC example? Thanks.


Solution

  • You can wrap the pages with AnimatedSwitcher:

    return BlocProvider<AuthenticationBloc>(
      bloc: authenticationBloc,
      child: MaterialApp(
        home: BlocBuilder<AuthenticationEvent, AuthenticationState>(
          bloc: authenticationBloc,
          builder: (BuildContext context, AuthState state) {
            return AnimatedSwitcher(
              duration: Duration(milliseconds: 250),
              child: _buildPage(context, state),
            );
          },
        ),
      ),
    );
    

    By default it uses fade transition and animates old and new widgets in reverse order.


    To keep old widget in place during animation, pass to AnimatedSwitcher

    switchOutCurve: Threshold(0),
    

    To mimic Navigator.push transition in Android, pass it

    transitionBuilder: (Widget child, Animation<double> animation) {
      return SlideTransition(
        position: Tween<Offset>(
          begin: const Offset(0, 0.25),
          end: Offset.zero,
        ).animate(animation),
        child: child,
      );
    },
    

    To use system transitions, try something like

    transitionBuilder: (Widget child, Animation<double> animation) {
      final theme = Theme.of(context).pageTransitionsTheme;
      final prev = MaterialPageRoute(builder: (_) => widget);
      return theme.buildTransitions(prev, context, animation, null, child);
    },
    

    (the last isn't tested well)