How to call a function in cubit class in initState?
Asked Answered
S

5

10

So, I have made a function in Cubit Class it is to get data from API. For now I just can get the data if I pressed a button. I wanna make the function automatically called when the page/screen is open. For your information, this page is the first page that will be launched when user open the app. Here is some of my codes.

class UsersCubit extends Cubit<UsersState> {
  UsersCubit() : super(UsersInitial());

  UserRepository _userRepository = UserRepository();

  void getAllUsers() async{
    emit(UsersLoading());
    try{
      ResponseUsers _data = await _userRepository.getUsers();
      emit(UsersSuccess(_data));
    } catch(e){
      emit(UsersError(e.toString()));
    }
  }
}
class UsersPage extends StatefulWidget {
  const UsersPage({Key? key}) : super(key: key);

  @override
  _UsersPageState createState() => _UsersPageState();
}

class _UsersPageState extends State<UsersPage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Users")),
      body: BlocProvider(
        create: (context) => UsersCubit(),
        child: BlocConsumer<UsersCubit, UsersState>(
          listener: (context, state) {
            if(state is UsersLoading){
              print("getting users ...");
            } else if (state is UsersSuccess){
              print(state.data.users[1].identity!.name);
            } else if (state is UsersError){
              print(state.errorMessage);
            }
          },
          builder: (context, state) {
            return Stack(
              children: [
                (state is UsersSuccess) ? listViewUsers(state.data.users) : progressBar(),
                ElevatedButton(
                  onPressed: (){
                    context.read<UsersCubit>().getAllUsers();
                  },
                  child: Text("GET USERS"),
                )
              ],
            );
          },
        ),
      ),
    );
  }
}

I have tried to call the function directly in initState but when I run the app it returns an error.

  @override
  void initState() {
    context.read<UsersCubit>().getAllUsers();
    super.initState();
  }

error:

Error: Could not find the correct Provider<UsersCubit> above this UsersPage Widget

This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:

- You added a new provider in your `main.dart` and performed a hot-reload.
  To fix, perform a hot-restart.

- The provider you are trying to read is in a different route.

  Providers are "scoped". So if you insert of provider inside a route, then
  other routes will not be able to access that provider.

- You used a `BuildContext` that is an ancestor of the provider you are trying to read.

  Make sure that UsersPage is under your MultiProvider/Provider<UsersCubit>.
  This usually happens when you are creating a provider and trying to read it immediately.

  For example, instead of:

  ...
  Widget build(BuildContext context) {
    return Provider<Example>(
      create: (_) => Example(),
      // Will throw a ProviderNotFoundError, because `context` is associated
      // to the widget that is the parent of `Provider<Example>`
      child: Text(context.watch<Example>()),
    ),
  }
  ...

  consider using `builder` like so:

  ...
  Widget build(BuildContext context) {
    return Provider<Example>(
      create: (_) => Example(),
      // we use `builder` to obtain a new `BuildContext` that has access to the provider
      builder: (context) {
        // No longer throws
        return Text(context.watch<Example>()),
      }
    ),
  }

Is there any way to solve this case?

Scofield answered 3/11, 2021 at 1:43 Comment(8)
can you share your code ? which have BlocProvider for UsersCubit and class use this Cubit ?Tiffinytiffy
Since you're using Cubit, you're locked in to BloC. So short answer: I can't help you. Longer answer: if BloC is the ONLY state management you're using with Flutter, then PLEASE consider alternative approaches. Very often, you don't need anything more complex than StatefulWidget. You SELDOM need anything more complex than Provider. Flutter provides many - great! - choices. Use the "best tool for the job" :)Smokeless
PS: when I run the app it returns an error...: PLEASE UPDATE YOUR POST WITH THE EXACT ERROR MESSAGE!Smokeless
i hava updated the post @TiffinytiffyScofield
you should wrap the parent widget with the BlocProvider.Gramercy
the Scaffold? or MaterialApp? @HamedScofield
Provide your cubit above the MaterialApp and you can access it from everywhere inside your app.Tatter
@EhsanAskari thank you so much! It works for my case!Scofield
C
7

You don't need to call a function inside initState when using Bloc or cubit, just call it when creating the Cubit inside BlocProvider like this >>.

class UsersPage extends StatefulWidget {
  const UsersPage({Key? key}) : super(key: key);

  @override
  _UsersPageState createState() => _UsersPageState();
}

class _UsersPageState extends State<UsersPage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Users")),
      body: BlocProvider(
        /// after create the cubit you can call the method.
        create: (context) => UsersCubit()..getAllUsers();,
        child: BlocConsumer<UsersCubit, UsersState>(
          listener: (context, state) {
            if(state is UsersLoading){
              print("getting users ...");
            } else if (state is UsersSuccess){
              print(state.data.users[1].identity!.name);
            } else if (state is UsersError){
              print(state.errorMessage);
            }
          },
          builder: (context, state) {
            return Stack(
              children: [
                (state is UsersSuccess) ? listViewUsers(state.data.users) : progressBar(),
                ElevatedButton(
                  onPressed: (){
                    context.read<UsersCubit>().getAllUsers();
                  },
                  child: Text("GET USERS"),
                )
              ],
            );
          },
        ),
      ),
    );
  }
}
Camelopardalis answered 8/6, 2022 at 11:45 Comment(0)
T
6

Can you try wrapp BlocConsumer inside a Builder ?

import 'package:flutter/material.dart';

class UsersPage extends StatefulWidget {
  const UsersPage({Key? key}) : super(key: key);

  @override
  _UsersPageState createState() => _UsersPageState();
}

class _UsersPageState extends State<UsersPage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Users")),
      body: BlocProvider(
        create: (context) {
          final cubit = UsersCubit();
          cubit.getAllUsers();
          return cubit;
        },
        child: Builder(builder: (context) {
          return BlocConsumer<UsersCubit, UsersState>(
            listener: (context, state) {
              if(state is UsersLoading){
                print("getting users ...");
              } else if (state is UsersSuccess){
                print(state.data.users[1].identity!.name);
              } else if (state is UsersError){
                print(state.errorMessage);
              }
            },
            builder: (context, state) {
              return Stack(
                children: [
                  (state is UsersSuccess) ? listViewUsers(state.data.users) : progressBar(),
                  ElevatedButton(
                    onPressed: (){
                      context.read<UsersCubit>().getAllUsers();
                    },
                    child: Text("GET USERS"),
                  )
                ],
              );
            },
          );
        }),
      ),
    );
  }
}

Tiffinytiffy answered 3/11, 2021 at 3:48 Comment(3)
i have just tried it and it returns same errorScofield
I have just update solution, you can try my code again?Tiffinytiffy
this case is solved already :)Scofield
H
5

you can call it from your Blocprovider by accessing your class like this :-

 BlocProvider(
      create: (context) => Usercubit()..getAllUsers(),
      build : (context) => Scaffold() ........
    
Hellbender answered 9/3, 2022 at 22:7 Comment(0)
S
1

I have solved this case with help from @Ehsan Askari. He suggests me to provide the cubit above the MaterialApp, then I did it. Here is my code now

class AppWidget extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => UsersCubit(),
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: UsersPage(),
      ),
    );
  }
}
class UsersPage extends StatefulWidget {
  const UsersPage({Key? key}) : super(key: key);

  @override
  _UsersPageState createState() => _UsersPageState();
}

class _UsersPageState extends State<UsersPage> {

  @override
  void initState() {
    context.read<UsersCubit>().getAllUsers();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Users")),
      body: BlocConsumer<UsersCubit, UsersState>(
        listener: (context, state) {
          if(state is UsersLoading){
            print("getting users ...");
          } else if (state is UsersSuccess){
            print(state.data.users[1].identity!.name);
          } else if (state is UsersError){
            print(state.errorMessage);
          }
        },
        builder: (context, state) {
          return (state is UsersSuccess) ? listViewUsers(state.data.users) : progressBar();
        },
      ),
    );
  }
}
Scofield answered 3/11, 2021 at 4:48 Comment(0)
C
0

You can easily do this UsersCubit() : super(UsersInitial()){getAllUsers();} your function automatically run when "UsersPage" load very first time.

Colorfast answered 15/4 at 8:28 Comment(1)
Using this you also avoid to use initState functionColorfast

© 2022 - 2024 — McMap. All rights reserved.