How to make http.post call in riverpod?

1,914

You should use a state notifier provider since you already have a repository that handles your API calls. First you can create a state for the provider like below(I am using the Equatable Package):

abstract class FetchUserState extends Equatable {
  FetchUserState();
  }
class UserFetchInitial extends FetchUserState {
 UserFetchInitial();

 @override
 List<Object> get props => [];
}
class UserFetched extends FetchUserState {
 UserFetched();

 @override
 List<Object> get props => [];
}
class UserFetching extends FetchUserState {
 UserFetching();

 @override
 List<Object> get props => [];
}
class UserFetchError extends FetchUserState {
 UserFetchError();

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

Next, we create a StateNotifierProvider

final userProvider =
    StateNotifierProvider<UserController>((ref) => UserController());

Next we create the User Controller that extends our state

class UserController extends StateNotifier<FetchUserState> {
  UserController() :
        super(UserFetchInitial());

  void fetchUser(String mobile, String password) async {
    final userRepository = UserRepository(); 
    state = UserFetching();
    try {
      final res = await userRepository.fetchUser(mobile, password);
      if (res) {
        state = UserFetched();
      } else {
        state = UserFetchError();
      }
    } catch (e) {
      state = UserFetchError();
    }
  }
}

Finally, in your onPressed on your UI, You can easily do:

context.read(userProvider).fetchUser(_phoneController.text.trim(), 
                                                    _passwordController.text);

Let me know if this works for you.

Share:
1,914
Mr.PY
Author by

Mr.PY

Updated on December 27, 2022

Comments

  • Mr.PY
    Mr.PY over 1 year

    I am trying to make a post-call that would send form data to API and get a response.

    I would like to make a network post-call which sends the mobile, password, and get user data from user_repo and store it in the state to be accessed in the future.

    I am unsure regarding how to add the user state provider and call it on button press.

    for eg:

    My AuthScreen: This is where the Ui is implemented.

    class AuthScreen extends StatefulWidget {
      @override
      _AuthScreenState createState() => _AuthScreenState();
    }
    
    class _AuthScreenState extends State<AuthScreen> {
      TextEditingController _phoneController;
      TextEditingController _passwordController;
      bool _isHidden = true;
    
      void _togglePasswordView() {
        setState(() {
          _isHidden = !_isHidden;
        });
      }
    
      @override
      void initState() {
        super.initState();
        _phoneController = TextEditingController();
        _passwordController = TextEditingController();
      }
    
      @override
      void dispose() {
        _phoneController.dispose();
        _passwordController.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return SafeArea(
          child: Scaffold(
            backgroundColor: Colors.white,
            resizeToAvoidBottomInset: false,
            body: Column(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'Login',
                      style: TextStyle(
                        fontSize: 24.0,
                        fontWeight: FontWeight.w500,
                      ),
                    ),
                    TextField(
                      keyboardType: TextInputType.phone,
                      controller: _phoneController,
                      decoration: InputDecoration(
                          prefixIcon: Icon(Icons.phone_android),
                          hintText: 'Enter your registered mobile number'),
                    ),
                    TextField(
                      controller: _passwordController,
                      decoration: InputDecoration(
                        prefixIcon: Icon(Icons.lock),
                        hintText: 'Enter your password',
                        suffixIcon: InkWell(
                          child: Icon(
                              _isHidden ? Icons.visibility_off : Icons.visibility),
                          onTap: _togglePasswordView,
                        ),
                      ),
                      obscureText: _isHidden,
                    ),
                    RaisedButton(
                    onPressed: ()=>{},
                    child: Text("login"),
                  ),
              ],
            ),  
          ),
        );
      }
    

    what should I write for onPressed? context.read(userState)??

    User Modal:

    class UserData {
      UserData({
        this.id,
        this.name,
        this.mobile,
        this.email,
        this.image,
      });
    
      String id;
      String name;
      String mobile;
      String email;
      String image;
    
      factory UserData.fromJson(Map<String, dynamic> json) => UserData(
            id: json["id"],
            name: json["name"],
            mobile: json["mobile"],
            email: json["email"],
            image: json["image"],
          );
    
      Map<String, dynamic> toJson() => {
            "id": id,
            "name": name,
            "mobile": mobile,
            "email": email,
            "image": image,
          };
    }
    

    User Repo:

    class UserRepository {
    
      Future<dynamic> fetchUser(String mobile, String password) async {
        var body = {"mobile": mobile, "password": password};
        final response = await _implementation.post(my_url, body: body);
        return response;
      }
    }
    

    State:

    final userState = FutureProvider<dynamic>((ref) async {
          UserRepository().fetchUser(mobile, password);
    // How to make this work??
    });
    

    Edit: The solution provided by Tayormi worked perfectly.

    I added a bit of code to store the user to access as necessary.

    Created below state provider to store user:

    final userData = StateProvider<UserData>((ref) => null);
    
    

    Modified the userProvider to:

    final userProvider = StateNotifierProvider<UserController>((ref) {
      final user = ref.read(userData);
      return UserController(user);
    });
    

    within the try block of userController we can update the userData as below:

    class UserController extends StateNotifier<FetchUserState> {
      final userData;
      UserController(this.userData) : super(UserFetchInitial());
      void fetchUser(String mobile, String password) async {
        final userRepo = UserRepository();
    
        state = UserFetching();
        try {
          final response = await userRepo.fetchUser(mobile, password);
          if (response.id != null) {
            userData.state = response;
            state = UserFetched();
          } else {
            state = UserFetchError();
          }
        } catch (error) {
          print(error);
          state = UserFetchError();
        }
      }
    }
    

    All thanks to @Tayormi for helping me arrive at the solution..

    • Andrey Rankov
      Andrey Rankov about 3 years
      Thank you for providing this! Can you put more details on how you did observe state changes - show loading animation till the user is fetched, show an error if user not found etc
  • Mr.PY
    Mr.PY over 3 years
    Thank you for your answer. It worked perfectly. I was able to listen to the current user status and update UI accordingly. for eg: show loading animation till the user is fetched. show an error if user not found etc., I added a bit of code to store user details in state as below: final userData = StateProvider<UserData>((ref) => null);
  • LearnFlutter
    LearnFlutter about 2 years
    I was struggling with a task that i need to do and people told me that it can be done with riverpod, stackoverflow.com/questions/70980002/…
  • LearnFlutter
    LearnFlutter about 2 years
    i simply need to have editedFirstName = TextEditingController(text: data3.firstName) globaly the declaration and assigning