# Building a social media app with AWS Amplify and Flutter(PART 2)

<a href="https://www.loom.com/share/f7eb11266c4b41308a3fc410b1177f9d">
    <p>1 October, 2021 - Loom Recording - Watch Video</p>
    <img style="max-width:300px;" src="https://cdn.loom.com/sessions/thumbnails/f7eb11266c4b41308a3fc410b1177f9d-with-play.gif">
  </a>
[PART 1](https://phatrabbitapps.com/building-a-social-media-app-with-aws-amplify-and-flutterpart-1)
<br>
Hi there, 
in the [first part of this series](https://phatrabbitapps.com/building-a-social-media-app-with-aws-amplify-and-flutterpart-1), we briefly introduced and added amplify to our Flutter application.
<br>
In this second part, we'll continue from where we left off.
<br>
[FULL SOURCE CODE FOR THIS SECTION IS IN THIS BRANCH](https://github.com/trey-rosius/friends/tree/registration_login)
<h3> Configure Amplify For Flutter</h3>
We need to configure and initialize Amplify in our application before running any `amplify` related requests.<br>
<br>
Let's do just that 

```
 Future<void> _initializeApp() async{
    await _configureAmplify();
  }

  final AmplifyDataStore _amplifyDataStore =  AmplifyDataStore(modelProvider: ModelProvider.instance,);
  Future<void> _configureAmplify() async {

   try{


    await Amplify.addPlugins([
     _amplifyDataStore,
      AmplifyAuthCognito(),
      AmplifyAPI(),
      AmplifyStorageS3()
    ]);

      // Once Plugins are added, configure Amplify
      await Amplify.configure(amplifyconfig);

    } catch(e) {
    print('an error occured during amplify configuration: $e');



    }


  }


``` 

From the code above, we add the  Datastore, Auth,Api and storage plugins, then run `Amplify.configure()`.
<br>
Now, call the `_initializeApp()` function in the initState method of your`main.dart` file.

```
  @override
  void initState() {
 
    _initializeApp();
    super.initState();

  }
``` 
Here's the full code for the `main.dart` file [main.dart](https://github.com/trey-rosius/friends/blob/registration_login/lib/main.dart).
<br>
In the `main.dart` file, the `homepage` is wrapped with a `MultiProvider ` method, gotten from the `provider` state management plugin we added earlier to the `pubspec.yaml` file. </br>

We provide the `ProfileRepository` and `SharedPrefUtils`. </br>


```
home: MultiProvider(
          providers: [
            ChangeNotifierProvider(create: (_) => ProfileRepository.instance(),),
            ChangeNotifierProvider(create: (_) => SharedPrefsUtils.instance(),),

          ],
          child: HomePage(),

        )
``` 
`SharedPrefUtils` contains methods to save, get and delete shared preferences from our app. We use it to save and retrieve the `userId` whenever we need it. <br>
 <br>
`ProfileRepository` has methods, getters, and setters for all dependencies we'll need in the profile section of our application. <br>
<br>
In the `homepage.dart` the application retrieves the `userId` value stored in shared preferences and checks if it has a value or if it's `null`.
<br>
If `null`, then the user doesn't exist, meaning no user has logged into the app from that device yet. So you get directed to the login screen.
<h2> Log In with Google </h2>

While setting up the authentication category for Amplify, we choose to have regular username and password signup, alongside google sign up. 
<br>
And if you followed the link I attached, you must have set up google credentials properly. <br>
Let's take a look at the code for logging in to our app using google.<br>
This code is in the `loginRepository.dart` file .

```
 Future<void>googleSignIn(BuildContext context) async{
    googleLoading = true;
    try {
      var res = await Amplify.Auth.signInWithWebUI(provider: AuthProvider.google);


        isSignedIn = res.isSignedIn;
      if(isSignedIn){


        googleLoading = false;
        retrieveCurrentUser().then((AuthUser authUser) async{
          SharedPrefsUtils.instance().saveUserId(authUser.userId).then((value) {
            print("user id saved successfully");
          });
          print("User id is"+authUser.userId);

          List<User> user = await Amplify.DataStore.query(User.classType, where: User.ID.eq(authUser.userId));
          if(user.isNotEmpty){
            Navigator.push(context, MaterialPageRoute(builder: (context){
              // return RegisterScreen();
              //return LoginScreen();

              return ChangeNotifierProvider(create: (_)=>ProfileRepository.instance(),
                child: EditProfileScreen(authUser.userId),);


            }));
          }else{
            Navigator.push(context, MaterialPageRoute(builder: (context){
              // return RegisterScreen();
              //return LoginScreen();

              return ChangeNotifierProvider(create: (_)=>ProfileRepository.instance(),
                child: CreateProfileScreen(),);


            }));
          }




        });


      }else{
        googleLoading = false;
      }

    } on AmplifyException catch (e) {
      print(e.message);
      googleLoading = false;
    }



  }
  Future<AuthUser>retrieveCurrentUser() async{
    AuthUser authUser = await Amplify.Auth.getCurrentUser();
    return authUser;
  }
``` 
What this code does is, firstly, it sets a loading indicator value to true. On the `loginscreen` we'll display or not display a loading indicator when the google sign-in button is clicked, based on this value. <br>
Then we sign in the user with `Amplify.Auth.signInWithWebUI`. <br>
If successful, we call a method `retrieveCurrentUser` which retrieves user information like `userId`, `email`. We save `userId` to shared preferences. <br>
<br>
Next, we check if a user with the same userId already exists in our datastore. If they do, then they are a returning user. So we navigate to the `updateProfileScreen.dart` screen. <br>
Else, they are new, so we navigate to `createProfileScreen.dart` screen.
<br>
Please refer to the [github repository](https://github.com/trey-rosius/friends/tree/registration_login) for the full source code.

<h3> Create/Update User Profile </h3>
After successfully login in, we get to the create/update profile screen.
<br>
We want our users to be able to update their profile picture,firstNames, and LastNames.

![Screenshot_1633204709.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1633205393986/1zeaFgJ2I.png)
<br>
Firstly, we upload the user's profile picture and get its public URL.
<br>
We first assign some metadata such as the name and description of the picture to ` S3UploadFileOptions` and also set the accessLevel of the image to `StorageAccessLevel.guest`.
<br>
`StorageAccessLevel` is an enumeration with values such as `guest`, `private`, `protected`.
<br>
Using `Amplify.Storage.uploadFile`, firstly we pass the `key`, which is a unique identifier, then the `local` which is the relative path to the local file, and the `options` object we created above.` S3UploadFileOptions`.
<br>
We then use `await Amplify.Storage.getUrl(key: profilePicKey)` to retrieve the URL.


```

      Map<String, String> metadata = <String, String>{};
      metadata['name'] = "user_$uuid";

      metadata['desc'] = 'A profile picture ';
      S3UploadFileOptions  options = S3UploadFileOptions(accessLevel: StorageAccessLevel.guest, metadata: metadata);
      try {
      UploadFileResult result  =  await Amplify.Storage.uploadFile(
            key: uuid,
            local: croppedFile,
            options: options
        );
      profilePicKey  = result.key;
      print("the key is "+profilePicKey);
      GetUrlResult resultDownload = await Amplify.Storage.getUrl(key: profilePicKey);
      print(resultDownload.url);
      profilePic = resultDownload.url;
      loading = false;

      } on StorageException catch (e) {
        print("error message is" + e.message);
       loading= false;
      }

``` 





`saveUserProfileDetails`

```

 Future<void>saveUserProfileDetails() async{
  loading = true;
    User newUser = User(
        id:userId,username:username,firstName: firstNamesController.text.trim(),lastName: lastNamesController.text.trim(),
    profilePicUrl: profilePic,email: email,createdOn: TemporalDateTime.now(),isVerified: true);

    await Amplify.DataStore.save(newUser).then((_) => loading = false);


  }
``` 
The above function saves `userId`, `username`,`firstName` etc to the datastore.

`updateProfileDetails`

```
 Future<void>updateUserProfileDetails(String userId) async{
    loading = true;

    List<User> user = await Amplify.DataStore.query(User.classType, where: User.ID.eq(userId));
    User newUser = user[0].copyWith(id: user[0].id,firstName: firstNamesController.text.trim(),lastName: lastNamesController.text.trim(),
        profilePicUrl: profilePic,updatedOn: TemporalDateTime.now());

    await Amplify.DataStore.save(newUser).then((_) => loading = false);




  }
``` 
`getUserProfile`

```
Future<User>getUserProfile(String userId) async{

    List<User> user = await Amplify.DataStore.query(User.classType, where: User.ID.eq(userId));
    print(user[0]);

    firstNamesController.text = user[0].firstName;
    lastNamesController.text = user[0].lastName!;
    profilePic =user[0].profilePicUrl!;


    return user[0];


}


}
``` 
After user details have been saved properly, we navigate to the HomePage.
<br>
[FULL SOURCE CODE FOR THIS SECTION IS IN THIS BRANCH](https://github.com/trey-rosius/friends/tree/registration_login)
<h2> Conclusion </h2>
In this post, we configured Amplify in our flutter app and then build the login/ registration and user profile screens. 
<br>
Thanks for reading through.
<br>
If you loved it, please leave some feedback in form of a like or comment. 
<br>
If you find any issue with the post(Maybe a typo or incorrect code), please let me know and I'll get to it immediately.
<br>
Till next time ✌🏾


