Flutter Bloc State Not Changing

Issue

I am new to blocs in Flutter and I am trying to use them to create, read, edit, and store user inputted data. I am currently using a bloc for my app’s onboarding process. Ideally, once the user creates an account using Firebase email & password authentication, they will be prompted to answer a list of questions. The problem is, my state does not change from loading to loaded. I have it set up to display a circular progress bar until it is loaded, then the text will appear, but there is no state change. I could really use some guidance or insight please :).

Note I’ve watched several videos and have had no luck, and I really need some help/guidance

Main dart:

int? isViewed;
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  await FirebaseAppCheck.instance.activate();
  
  final prefs = await SharedPreferences.getInstance();
  final showLogin = prefs.getBool('showLogin') ?? false;
  Paint.enableDithering = true;
  
// This is for our onboarding screen
isViewed = prefs.getInt('onboard');

  runApp(MyApp(showLogin: showLogin));
}

class MyApp extends StatelessWidget {
  final bool showLogin;
  
  const MyApp({Key? key,
  required this.showLogin}) : super(key: key);


  @override
  Widget build(BuildContext context) {
    return MultiRepositoryProvider(
      providers: [
        RepositoryProvider(
          create: (context) => DatabaseRepository()
          ),
          RepositoryProvider(
            create: (context) => StorageRepository()
            )
      ],
      child: MultiBlocProvider(
        providers: [
          BlocProvider<OnboardingBloc>(
            create: (context) => OnboardingBloc(
              databaseRepository: context.read<DatabaseRepository>(),
              storageRepository: context.read<StorageRepository>()
            ))
        ],
          child: MaterialApp(
            title: 'Strength',
            debugShowCheckedModeBanner: false,
            initialRoute: AccountOnboarding.routeName, // Splash Screen
            routes: {
              '/splash screen' : (context) => const SplashScreen(),
              '/main onboarding' : (context) => const OnboardingScreen(),
              '/landing' : (context) => LandingScreen(),
              '/login' : (context) => const LoginScreen2(),
              '/dashboard' : (context) => const Dashboard(),
              '/profile onboarding' : (context) => const AccountOnboarding()
            },
            
            home: AccountOnboarding() // FINAL SCREEN IS SPLASH SCREEN
          )),
    );
      }
  }

Onboarding Bloc:

part 'onboarding_event.dart';
part 'onboarding_state.dart';

class OnboardingBloc extends Bloc<OnboardingEvent, OnboardingState> {
  final DatabaseRepository _databaseRepository;
  final StorageRepository _storageRepository;

  OnboardingBloc({
    required DatabaseRepository databaseRepository,
    required StorageRepository storageRepository,
  }) : 
  _databaseRepository = databaseRepository,
  _storageRepository = storageRepository,
  super(OnboardingLoading()) {
    on<StartOnboarding>(_onStartOnboarding);
    on<UpdateUser>(_onUpdateUser);
    on<UpdateUserImage>(_onUpdateUserImage);
  }

  void _onStartOnboarding(
    StartOnboarding event, 
    Emitter<OnboardingState> emit) 
    async {
      await _databaseRepository.createUser(event.user);
      emit(OnboardingLoaded(user: event.user));
    }

  void _onUpdateUser(
    UpdateUser event, 
    Emitter<OnboardingState> emit) {
      if (state is OnboardingLoaded) {
        _databaseRepository.UpdateUser(event.user);
        emit(OnboardingLoaded(user: event.user));
      }
    }

  void _onUpdateUserImage(
    UpdateUserImage event, 
    Emitter<OnboardingState> emit) 
    async{
      if (state is OnboardingLoaded) {
        User user = (state as OnboardingLoaded).user;

        await _storageRepository.uploadImage(user, event.image);

        _databaseRepository.getUser(user.id!).listen((user) {
          add(UpdateUser(user: user));
        });
      }
    }
}

Onboarding States:

part of 'onboarding_bloc.dart';

abstract class OnboardingState extends Equatable {
  const OnboardingState();

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

class OnboardingLoading extends OnboardingState {}

class OnboardingLoaded extends OnboardingState {
  final User user;

  OnboardingLoaded({required this.user});

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

Onboarding Screen format:

class AccountOnboarding extends StatelessWidget {
  static const String routeName = '/profile onboarding';

  static Route route() {
    return MaterialPageRoute(
      settings: RouteSettings(name: routeName),
      builder: (context) => MultiBlocProvider(
        providers: [
          BlocProvider<OnboardingBloc>
          (create: (_) => OnboardingBloc(
            databaseRepository: context.read<DatabaseRepository>(), 
            storageRepository: context.read<StorageRepository>(),
            )..add(StartOnboarding())
            )
        ],
        child: AccountOnboarding(),
      ));
  }

    static const List<Tab> tabs = <Tab>[
    Tab(text: 'Name'),
    Tab(text: 'Age and Profile'),
    Tab(text: 'Bio and Interests'),
    Tab(text: 'Selection')
  ];

  @override
  Widget build(BuildContext context) {

    return DefaultTabController(
      length: tabs.length,
       child: Builder(builder: (BuildContext context) {
        final TabController tabController = DefaultTabController.of(context)!;
        tabController.addListener(() {
          if (!tabController.indexIsChanging) {}
        });
        return Scaffold(
          resizeToAvoidBottomInset: false,
          backgroundColor: const Color(0xff31708c),
          appBar: AppBar(
            automaticallyImplyLeading: false,
            backgroundColor: Colors.transparent,
            elevation: 0,
            title: Row(
              children: [
                Expanded(
                  child: Image.asset('assets/images/Logo_Strength.png',
                  height: 50),
                ),
                Expanded(
                  flex: 2,
                  child: RichText(
                              text: TextSpan(
                                style: GoogleFonts.montserrat(
                                  fontSize: 30),
                                  children: <TextSpan> [
                                    TextSpan(text: 'Stren', 
                                    style: GoogleFonts.montserrat(
                                      color: Colors.white, 
                                      fontWeight: FontWeight.bold, 
                                      letterSpacing: 1,
                                      shadows: [
                                        Shadow(
                                          color: Colors.black.withOpacity(0.7),
                                          offset: const Offset(1.5, 0.0))
                                      ])),
                
                                    TextSpan(text: ';', 
                                    style: GoogleFonts.montserrat(
                                      color: const Color(0xffef6a7a), fontWeight: FontWeight.bold, 
                                      letterSpacing: 1,
                                      shadows: [
                                        Shadow(
                                          color: Colors.black.withOpacity(0.7),
                                          offset: const Offset(1.5, 0.0))
                                      ])),
                
                                    TextSpan(text: 'th', 
                                    style: GoogleFonts.montserrat(
                                      color: Colors.white, 
                                      fontWeight: FontWeight.bold, 
                                      letterSpacing: 1,
                                      shadows: [
                                        Shadow(
                                          color: Colors.black.withOpacity(0.7),
                                          offset: const Offset(1.5, 0.0))
                                      ]))
                          ],
                        ),
                      ),
                ),
              ],
            )
        ),
        body: TabBarView(
              // physics: const NeverScrollableScrollPhysics(),
              children: [
                NamePage(tabController: tabController,),
                ageAndPicture(tabController: tabController,),
                bioAndInterests(tabController: tabController,),
                SelectionPage(tabController: tabController,)
              ],
            ));
            }
          ));}}

First onboarding screen (where states are addressed):

class NamePage extends StatelessWidget {
  final TabController tabController;

  const NamePage({Key? key,
  required this.tabController}) 
  : super(key: key);

  @override

  Widget build(BuildContext context) {
    double _height = MediaQuery.of(context).size.height;

    return BlocBuilder<OnboardingBloc, OnboardingState>(
      builder: (context, state) {
        if (state is OnboardingLoading) {
          return Center(
            child: CircularProgressIndicator(),
          );
        }

        if (state is OnboardingLoaded) {
        return Scaffold(
            resizeToAvoidBottomInset: false,
            backgroundColor: const Color(0xff31708c),
        
          body: Padding(
            padding: EdgeInsets.only(
              left: 30,
              right: 30,
              top: _height * 0.055,
              bottom: _height * 0.05),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Column(
                    children: [
                      Column(
                        children: <Widget>[
                          Text('Here at Strength, we respect your preferences. How would you like to be addressed?',
                          style: GoogleFonts.montserrat(
                            color: Colors.white,
                            fontSize: 19,
                            fontWeight: FontWeight.w600
                          ),
                          textAlign: TextAlign.center,),
                          Padding(
                            padding: EdgeInsets.only(
                              top: _height * 0.22),
                              child: SingleChildScrollView(
                                child: Column(
                                  crossAxisAlignment: CrossAxisAlignment.start,
                                  children: <Widget>[
                                    TextField(
                                      maxLength: 14,
                                      maxLengthEnforcement: MaxLengthEnforcement.enforced,
                                      onChanged: (value) {
                                        context.read<OnboardingBloc>()
                                        .add(UpdateUser(
                                          user: state.user.copyWith(name: value)));
                                      },
                                      cursorColor: Colors.white,
                                      style: GoogleFonts.montserrat(
                                        color: Colors.white,
                                        fontSize: 19,
                                        height: 2
                                      ),
                                      decoration: InputDecoration(
                                        enabledBorder: InputBorder.none,
                                        disabledBorder: InputBorder.none,
                                        focusedBorder: InputBorder.none,
                                        filled: true,
                                        helperText: 'Your name',
                                        helperStyle: GoogleFonts.montserrat(
                                          color: Colors.white,
                                          fontSize: 14.5,
                                          fontWeight: FontWeight.w600
                                        ),
                                        counterStyle: GoogleFonts.montserrat(
                                          fontSize: 14.5,
                                          fontWeight: FontWeight.w600
                                        ),
                                        // counterText: "",
                                        labelStyle: 
                                        GoogleFonts.montserrat(
                                          color: Colors.white,
                                          fontSize: 18),
                                        hintText: 'Please call me . . .',
                                        hintStyle: GoogleFonts.montserrat(
                                          color: Colors.white54,
                                          fontWeight: FontWeight.w600,
                                          fontSize: 18),
                                          border: InputBorder.none),
                                    ),
                                  ],
                                ),
                              ),),
                        ],
                      ),
                    ],
                  ),
                  Column(
                    children: [
                      Align(
                        alignment: 
                        Alignment.topLeft,
                        child: Text('1/4',
                        style: GoogleFonts.montserrat(
                          color: Colors.white,
                          fontWeight: FontWeight.w500
                        ),)),
                      const SizedBox(height: 5,),
                      const StepProgressIndicator(
                        totalSteps: 4, 
                        currentStep: 1,
                        roundedEdges: Radius.circular(20),
                        size: 3,
                        padding: 3,
                        selectedColor: Colors.white,
                        unselectedColor: Color.fromARGB(255, 20, 83, 106),),
                        const SizedBox(height: 13,),
                        CustomButton(tabController: tabController)
                    ],
                  ),
                ],
              ),
            ),
        );
      }
      else {
        return const Text('Something went wrong.');
      }
      }
    );
}
}

Solution

Update: I was able to get it to work.

I ended up creating blocs for first authenticating the new user with email and password signup, then the new account triggered a state change for firestore.

Answered By – Ta-Ty

Answer Checked By – Robin (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *