eCommerce App with Flutter, Firebase, Cloud Functions and Stripe [2024]

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what's up guys welcome to another flatter tutorial today we're going to build a full stack e-commerce application with flatter and Firebase and then in this project we will process payments using stripe and a serverless backand that we will build using Cloud functions anyway as we go through the process of building this flatter Ecommerce app you will learn a few things including how to manage the data in your applications in fact we will focus on how to efficiently get the data from our cloud store database by the end you will understand when to use Futures over streams and vice versa because there are going to be some features of the application they need to keep listening to realtime data updates while other features will retrieve the data synchronously but only once then we will work with firestore data bundles and we will use static data files from Cloud farore to improve the efficiency and to optimize the number of reads so that we can potentially reduce the cost associated assciated with retrieving the data from cloud fir store then you will learn how to build a serverless back end because we will need to create and deploy Some Cloud functions on Firebase and we will do that to process the backend logic to complete the transactions in the e-commerce in fact we will need to connect the application with our stripe account using the stripe API and this logic has to run on the server and not on the client and this is because we need to keep our stripe API key safe so you will also learn how to manage the API keys with different approaches so we will use MVD to manage the local environment variables but we will use secret manager on Google Cloud to keep the stripe secret key safe and to make it available to the cloud functions anyway before we get started let's take a look at a preview of some of the UI screens that we're going to develop so we're going to have a catalog where the user can see all the products for a category then we're going to have a cart where the user can keep track of the product he wants to purchase finally we're going to have the categories which is where the user can choose which category of product you want to visualize but obviously this is not all the UI screens that we're going to build and we're going to add more and more features to the app anyway before we start writing some code into Visual Studio code let's start to understand how we will be storing the data into Cloud fir store first of all let's take a look at this entity relationship diagram and this represent how we can store and connect different data in a SQL database obviously Cloud fire store is no SQL so we will need to denormalize this schema so that we can fit those data point into a few different collections so in our case the first collection is going to be the products collection this will store multiple documents each of these will represent a product that is available inside eCommerce then we're going to have another collection for the categories and each of the document here we present different categories of product so products and categories can be connected as well then we are not going to store the cart and the card items in the cloud fir store database so these are going to be the information about which are the products that the user add to the cart and we're only going to keep this locally on the user device so we will not send those data point and we will not write them inside the database in practice we will only know which are the product that the user has added to the the cart and we will only save those information into the database if the user finalized the order so in that case we will store them inside the document in the order collection so every time the user complete an order we will create a new document in the order collection and we will store the information about who is the user who completed the order we will store the status of the order and then we will have a list with all the different items that the user has purchased and finally we could also store the payment we could collect those data point from stripe and we could store them inside fter however we're not going to do it for now so the last collection that we're going to have is the user collection here we're going to have a document for each of the different users that are going to create an account on the application so we will store all the user information including an object that will represent the user address so again to summarize this we're going to have four different collection so the categories the products the users and the orders and we're going to store there all the data that we're going to save into our backand now let's move on and let's take a look at the payment flow inside our project so how we're going to process the payment from the client to the server side so first of all we're going to use the flatter stripe Library so that we're going to have the stripe flatter SDK available inside the application so this Library will provide us some out of the box widget that we can use in order to collect the card detail of the user so as you can see on the left hand side the first step is to collect the payment method using the stripe flat SDK so we will use the card form fi widget so that we allow the user to input this own information or the credit card in a safe way so we will not access or store in the database those information but the stripe flatter SDK will automatically create a new payment method every time we submit those information to stripe so again this will be processed and sent to stripe directly and we will not store them so we cannot leak the credit card details of the user in fact throughout the process we will not use the credit card detail directly but we just need to refer to the payment method ID and we will need to pass it to the strap API so that it will know which is the payment method that need to be used in order to process the payment then let's go go ahead and take a look at the next step and this will happen on the backend side so as I mentioned before we will create some serverless backend logic using Cloud functions so first of all the user will click on the pay now button and if he has completed the input of his own credit card information there will be a request sent from the client to the server so we will send a request to one or the two different endpoint that we will create using Cloud functions and this will start to create a new payment intent by using the information that we will pass from the client to the server so we will create the payment intent and we will try to process the payment as soon as we create the payment intent then if the payment is successful the stripe API will confirm the payment success and we will be able to take that information and we will be able to send the success message back to the client so that we can change the UI to inform the user that the payment was completed and so the order is completed as well anyway we will look at this more in depth later on as we keep developing the application for now let's get started let's open Visual Studio code and let's start by creating the code for this new project okay so let's go ahead with this new project and as you can see we have a stateless widget and this is creating the app that we are currently running in the simulator so far we just have the default application that you get every time you create a new flatter project so now in order to create our e-commerce app using Firebase and stripe we will start with this default project and we will change most of the code and the first step will be to create the category screen where the users can see all the different categories of the e-commerce and before we do that we will need to create the category model as well adding some sample data then we will display the list of categories using the flatter staged grid View library and this Library liary allow us to use different grid layouts overall you can refer to the documentation to see all of them but in our case we will use the mry grid layout and this will give us flexibility in defining how many columns we want and also we will be able to control the individual height of the different cards so we will create this grid with multiple cards of different height and it's going to be very simple to customize it we just need to use the mass reg grid View plus and then we Define how many columns we want and we can Define which is the height of each of the single card so now let's go ahead and add this library inside our project so we're just going to copy the name of the library and then we're going to add it from the terminal so we're going to run flatter Pub add staggered grid view after we run this the new dependency is going to be added to the ppec do yaml file so let's wait for it to be completed and let's go ahead and check if that that's been added then let's also remove all the different comments that we have here so we're going to keep this file a bit cleaner and as you can see the library has been added and we're currently using the latest version that is available at the moment I'm recording the video and by the way let's also add another dependency which is going to be equatable with this we're going to be able to perform value based equality so that we can compare multiple instances or the custom Dart classes they we're going to create for this project we're going to see a bit more how we use this in a while for now let's just close the ppec do yaml file and let's head over to the models folder because we can start to work on the category class anyway I've already added this class because this is very straightforward but let me show you step by step what I've added here so first of all we have the category class that extend equatable and again we will use equatable to compare multiple instances or the categories based on the value of the four different properties that we are adding keep in mind the category class will have four different properties including the ID the name the description and the image URL then remember that all of these properties are string and the description is the only one which can have a null value as you can see there is a question mark and it's not required inside the class Constructor then let's move on and let's take a look at the factory Constructor and we will use this to convert the data that we retrieve from cloud farore into instances or the category object remember the data will come in the form of a map and we will access the ID and the data separately so each document will have its own ID and we're going to retrieve this in a slightly different way than the rest of the data that are stored inside the document but we're going to see more about this later on during the tutorial anyway once we retrieve the data we will create an instance of the category object using the key value pairs that are stored inside the map and in Cloud fire store for this we will store the name the description and the image URL overall if we want to add more we can also store other relevant information then when we retrieve this data we can access the value by passing each key value pair to the corresponding property now for the image URL we also have a default value for now and this will be a random image that we can get from unsplash and that's pretty much it for the factory Constructor then below we have a list of props which is the list of properties that we use to compare different instances of the category class if we need to do that keep in mind if all of these four properties have the same values across two different instances of the category then they will be considered the same additionally I've also included a list of sample data that we can use to quickly create the category screener we will start by using this sample data and then then we will add the logic to retrieve the data from cloud fir store and finally we will display the data coming from the database however for now we will begin with the static list and you can find the code for this inside the project repository so that you don't have to manually type out all the different category class instances now let's close this and let's make sure to save everything before moving on and creating the category screen so let's proceed with that so we're going to start by opening the menu on the left hand side and we're going to create a new folder that will collect all the different screens of the flatter application then let's go ahead and let's start to create the category screen so this is the first screen that we're going to have for this project overall let's create a new stateful widget and let's call this category screen and for now we will use this as the initial screen or the application so we're going to make changes in the main do Dart and we will remove the default stateful widget with which is currently the homepage of the application and we will also import the new category screen and we're going to use it as a home so now at this point if we H restart or actually I will have to restart the application because it's currently turn it off once we do that you will see the newly created screen appearing inside the simulator but keep in mind that for now this is just a placeholder widget so we have not implemented any code yet inside the build method of this new state flu wiet so let's go ahead and let's make the changes here in the category screen because remember that we will need to use the mry grid that we have imported from the flatter STD grid View Library so let's go and remove the placeholder and let's use a scaffold then we need to implement the body of this scaffold and we're going to use the M grid view class similarly to the example that we have seen on pub. dev so we're going to use the count Constructor and we're going to set up the cross AIS count the item Builder and a few more properties for now we're going to set three different columns so we set cross AIS count equal to three then we need to pass the number of items that we're going to have and this is going to be equal to the number of categories that we have in the list before we do that let's just add a container as a return widget for the item Builder so we will build one container for each of the item that we're going to have in this mry grid The View then again as I mentioned before the item count is going to be equal to the length or the categories list that we have as a sample data in the category class and as you can see we have a few different container appearing in the UI each of these has a different colors which is what we have set up in row 25 now let's make a few changes inside the state object because we're going to save the list of categories over there for now we just map it to the list of categories that we have in the category class us but later we will change it and additionally we need to create a list of integers and we can refer to it as extents so these are going to be the height factors that we're going to use for each of the different cards I'm going to show you how to use them but for now we need to populate the list first and we're going to populate it randomly so we're going to use the random class to generate some random integers then we're going to use this inside the it state by calling the the load category private method so this is going to be an asynchronous function that we're going to have here inside the state object so with this we're going to retrieve the data for the categories and as well we're going to generate all the integers for the list of extends so we're going to have one integer for each of the different categories then that integer will represent the height factor that we will use to multiply an our coded integer to determine the height of each of the single cards now in the load category first of all we will need to fetch the categories from the database however we will add a too and we will do this later on for now let's just generate the list of extent by using list. generate and here is where we create one different integer for each of the different categories so we create as many entries for this list as the length of the categories list then we use the random class and we use the next integer meth me in order to generate a number that goes from 0er to two then we're going to add an arbitrary number so in this case is going to be equal to two so that we are sure that the generated number so the generated extent is never equal to zero obviously you can also add plus one it will work the same now we're going to have the list of extent and every time we add the widget to the widget Tre the init state will be called and we will call call as well the load categories then once we generate the list of extent we need to update the list of extent that we have inside the state object so we're going to use set state to do that and later on we will do the same for the categories however we don't have that yet so we will need to come back to this now let's go ahead and let's utilize the list of extends that we have just created in order to set up the height of each the different containers that we have inside the mass reg grid view so let me show you how to do that so first of all we will go inside the item Builder and we will assign a specific height to each of the different containers so we will create the height as a variable and we're going to store this in row 50 then we take one value for the extend list basically we access one value at a specific index and we multiply that by 100 which is an arbitrary value then we pass the height to the container and as you can see after doing doing that all the different containers have a different height remember that every time you have to start the height will change because we are generated randomly if you don't like that you can also hardcode the list of heights so that it will work in the same way but you will always keep the same height okay perfect so now let's go ahead and continue customizing the meeson regrid view to add some spacing between all the different containers and we can do that by setting the property is main axis spacing and cross axis spacing we're going to set the value equal to four and this will create horizontal spacing and vertical spacing between all the different cards then we're going to make sure that we can put the widget behind the app bar as well by setting extend body behind up bar equal to true and then we're going to add some padding to the Mas grid view so we're going to add padding in the upper part so we're going to use Edge in set do only we set the top padding equal to 120 and then we set the left and right padding equal to four which is going to be essentially the same spacing that we have between the different cards so perfect now the layout is pretty much good to go we have the spacing and we have the settings of the different cards however we need to customize the container more so that we can actually display the image URL or the category instead of just having a color and also we will need to be able to click on each of the card and to navigate to the catalog screen so that we will be able to see all the products for that specific category to do so we can just wrap the container with an inqu widget and then we will implement the logic to set up the navigation inside the on top callback but we will come back to this later on because we have not set up anything for navigation yet and also we don't have the screen to which we have to navigate to now back to the container let's add a decoration and let's add some border radius then I'm going to remove the color because we're going to use the image URL in order to add a background image to each of the different containers to do so we can set the image property of the Box decoration and we're going to use the decoration image widget and in order to actually retrieve the image we need to use Network image because remember the image URL is an image that come from a splash then we use box fit. cover so that the image will take the whole space available inside the card and as you can see now we're displaying the images they are all the same because we're randomly generating an image from un Splash and all the different categories as you can see in the list of sample data are using the same URL so as we are generating them at the same time they are always going to display the same images but remember remember that every time we have to start this will generate a different image but don't worry later on as soon as we save the data into our Cloud firor database we're going to have different images for each of the different cards so we will come back to this later on for now let's continue customizing the UI or the c or the category screen so what we're going to do next is to add a transition between this screen and the next one so to do this we can wrap the container with a hero transition and we will see this in action later on as soon as we develop the next screen all we need to do here is to pass a tag which is going to be the ID or the category and then we will also pass the same tag on the other screen so the one that we will navigate to now whenever we navigate from this screen to the next one you will see a transition between this image and the image that we're going to have in the next screen anyway let's keep this on hold for now and we will come back to this as soon as we create the catalog screen so that we can see whether this transition works fine now let's go ahead and as a next step we need to set up Firebase so that we can retrieve and update the data from the database so first of all let's take a look at the setup process because you will need to create an account on firebase.com then once you arrive there simply go ahead and click on get started and register if you don't have an account yet then we can kickart by creating a new project and we can do that through the terminal so to do that we need to install the Firebase CLI and the installation process is quite simple so it depends on the platform that you're using whether you are on Windows Mac OS or Linux you can just go ahead and follow the suggested installation method for your specific computer in my case I can just go ahead and use the auto install script and I'm just going to copy that inside the terminal then is your on Mac as well once you paste that the fireb CLI will be automatically installed for you inside your computer if you're on Windows the process is slightly different but you can simply go ahead and download the Firebase CLI binary and access it from there anyway after you install the Firebase CLI we can go ahead to The Next Step which is to add Firebase to the flatter project so let's go and take a look at the required steps so first of all we're going to use Firebase login to log in from the terminal into Firebase so in my case I've already logged in so it will just confirm that I'm already logged in otherwise it will ask you to go ahead click on a link and to log in anyway once you do that let's move on to the next step which is to use the flatter fire CLI and first of all we will need to initialize it if we don't have it available yet so just go ahead and run Dart Pub Global activate flatter fire CLI then we can use this tool in order to add Firebase to our current flatter project it's very simple we just need to run flatter fire configure so let's go ahead back in the terminal and let's run that as soon as we do that it will prompt us to enter some information so first of all it will fetch all the projects that are available for the loged in user then we can go ahead and create a new project but obviously you have the option to reuse one of the existing project that you have already available so for this tutorial I will create a new one and I'm going to call it atom boox Ecom then you can choose any name but just make sure to use dashes instead of underscores because underscores will not be accepted then I will start creating the new Firebase project and initially this will register the app for Android and iOS but potentially you can register the app for Mac OS and as well web application in in our case we will only do IOS and Android since we only have the IOS and Android directories now remember you should not see this message but I'm only getting this message because I've already registered the application before so this is asking me if I want to override a file that has already been created in this project so I'm going to click on yes since I've already done it and it will just replace it but it will not change anything anyway if this is the first time that you're running this terminal command in this project don't worry about this you won't have to do it and again it's the same for the Google services dojon this will be created again if you run the command twice but anyway we can just override it and continue now perfect the terminal is telling us that everything has been successful so we have successfully connected the flatter project with Firebase both for Android and iOS and currently in the terminal you can see the app ID both for Android and iOS then we have this new file which is called Firebase uncore options and here is where we are storing all the different keys and information that we need to connect the application with the Firebase project as you can see they are here at the bottom then we have a few errors and this is simply because we have not imported the required dependencies yet so if you scroll up you can see that the this requires the Firebase score library and we have not added this yet so let's go ahead and add it then after this is installed we can also go ahead and write flatter Pub ad Cloud firestore so that we have also the library that we can use to interact with database so perfect after these two different libraries have been installed we can close the app and restart so that we make sure that we don't get any error with these two different dependencies so we're going to restart the app and we make sure that this have been implemented inside the project then again just go and check the ppec yl file to see if these two have been correctly added so you will see fireb score and cloud firestore and remember just try to use the latest versions that are available whenever you're going to be watching this tutorial now let's go ahead and let's go to the project on Firebase so first of all we are here in the console and you should be able to see two different apps registered inside your project and you can find the information for both of them inside the settings if you scroll at the bottom including the package name the app ID and so on and so forth and potentially if you don't use them anymore you can also remove these two different application from the project now let's go ahead and let's start by enabling two of the services that we're going to use including authentication and the database so we're going to enable later on in tutorial authentication with email and password so we're going to enable that and we're going to add email and password as a provider for Firebase o in this way we can Implement later on the logic to create accounts and to login but for now that's all we're going to do we're going to come back to this later on now we're going to focus on the database we're going to click on first database and then we need to create the first version of our database so we're going to create the database and we will need to Define which is the location where we want to deploy the database keep in mind that this can be selected from the list and you should choose a location that is close to the user of the application so if you're developing an app for the European market just select Europe otherwise go ahead and choose whichever location it's the Clos closest to the user of the application then let's go ahead and set whether we want to start in production mode or test mode and keep in mind that the only difference is the default security rules that are going to be implemented so if you start in test mode every user can read or write data from the database as long as the request time is before than this specified date here obviously you will be able to update this and later on in the tutorial we're also going to Deep dive on security rules so that we make sure that the data that we're going to store in the cloud fire store database are safe now after a couple of seconds you can see that the database has been created and now it's completely empty so there are no collection at all and you can go and take a look at the rules these are just the rules that have been set up for the test mode of the database but again we will come back here and we we will Implement specific rules for each of the different collection that we're going to have so that users can only access the data that they are supposed to be able to access so for example the user data are going to be only available to the user itself in other users won't be able to access those while products and categories are going to be accessible by everybody now let's go ahead and create the first collection so we're going to create the categories collection and we also have to create one document so that's mandatory every time you create a collection from the Firebase console however we're going to remove the first document because we will not add the data manually but we're going to create a script that we can run directly from the application to load the data inside the categories collection so now let's keep this as it is and let's go back to our project to implement some logic in order to connect the application with the database B but oops it seems that the app crashed as it was trying to load so let's restart the application so that it will be up and running inside the simulator and then we're going to create the logic to connect with the database but we're going to have a separate package in order to store the logic so I'm going to create the packages directory inside the root level of our project and then we're going to create a new package that will store all the logic to perform crude operation to the firestore database so in order to create a new package we will need to run flatter create Das Das template equal to package and then we need to Define which is the package name that we want to create so in this case the package name is going to be DB client so let's go ahead clear the terminal and then let's move inside the packages directory so here is where we're going to create the new package so again just make sure that you call it dbor client but if you want you can also choose different naming then we're going to remove the test directory because we won't need it for now and we can start to develop this inside the Library folder now first of all we need to remove this default calculator class and in this file we're just going to create the library and then we're going to export all the files so that we can use them directly inside our project then inside the source folder we're going to create two different files the DB client and as well the DB record so now let's Deep dive on these two different files and first of all the DB client is where we will store the logic to perform the crude operations then the DB record is going to be a very simple class that will represent the data points that we can retrieve from cloud farer so for example every time we get a new document from cloud farer we're going to convert it into an instance of a DB record so this is just going to be the object that we will send back and forth every time we retrieve documents from fir store overall we're going to add two different properties to the DB cord the first is the ID that represent the unique identifier or the document and then we have a map that will include all the data that we have in that we have stored inside the document remember the ID is required because each document will have an ID while the data property can simply be an empty map because we could create a document with no data point saved inside perfect now we are good to go with the DB record let's just keep it as it is and let's move on to create the DB client this is a bit more complex and we're going to create multiple methods as we go on throughout the tutorial for now we will just create this class and remember to add the cloud fire store dependency to this package as well because we will need Cloud firester here inside the package so we can't use the instance at the import that we have added inside the root level Prospect yam so we need to make sure that inside the package the DB client we add the dependency Cloud fire store as well so after we do that we can start to use the different helper methods that we have from the library in order to add and retrieve data from the database so let's go ahead and let's create an instance of Firebase fire store first so we're going to declare this inside the class so this is going to be a variable that we have here available but then we're going to initialize this variable every time we create an instance of the DB client potentially we can pass an instance as an input but we're going to keep this as optional so you can simply initialize the DB client without passing any input then if the instance of fire store has not been passed as an input we're just going to create a new instance by using fireus fir store. instance so perfect we are good to go with the setup of the class now we can start to add two different methods the first one is going to be the add method that we can use in order to add a new document inside any fir store collection and then we're going to add the fetch all which will help us to retrieve a list with all the different documents from a specific collection and remember we're not going to send the documents directly as they are and as you can see from the return type of the method we're just going to return a list of theb cords so we will need to take all the documents that we successfully retrieve and we will need to convert them into DB records so every time we send data from this package to the client application we're always going to send that type of data we're going to come back to the fetch all later on let's start by implementing the ad method this will return a string once the future is resolved and this is because we're going to add a new document we're going to save it inside Cloud F store and we will take the document ID of the newly created document and we can send it back then we're going to take the collection and the data as an input because we need to specify which data we want to add in the new document and as well in which collection we want to add the new document the syntax is pretty simple so we just need to use the instance of farer with specify which is the collection and we use the add method again you can choose any collection and you can pass the collection name here and then we need to use the add method with this we can create a new document and we will return as well the document reference so again we just need to pass the map that will include all the data that we want to store inside the document and eventually this will return a document reference but in an asynchronous way anyway with the dock ref we can modify the document we can delete it or we can just get all the data from that specific document we will have access to the delete and to the get method then again we're not going to use this here we're just going to access the ID of the document and we're going to send it back as a response to the client whenever we're calling this method now let's complete the implementation just by drawing an exception if there is an error we're going to develop the catch part of this try catch block so perfect in that case we just pass the exception and the error message now let's continue and Implement as well the second method so we're going to use this after we add all the different categories so that we can retrieve them and display them on the category screen so here we just need to pass which is the collection from which we want to retrieve the data because as the method name suggest we're not going to apply any filter we're just going to return all the data from a specific collection then we're going to have a similar implementation as before so we just have a try catch block and if we catch an error we're just going to throw an exception with an error message and a bit more description then let's go ahead and implement the logic to retrieve the data so first of all we need to select the collection reference so we just need to use the instance of firester and we use the collection method to which we pass the collection this eventually will get the reference of the collection from which we want to retrieve the data remember that the collection reference will allow us to perform operations to retrieve data from it in this case we will simply return all the data without applying any filter so we will have a list of documents and we can just use the get method in order to get the list of documents remember this is a query but we're not passing any filter however we could potentially modify the query to filter the result based on the value of one of different properties that we are storing inside the document and we will explore this later on so that we can take a collection apply some filters and just return the documents that matches those filters now again let's go ahead and let's make a few changes because the query that we have retrieved is actually a snapshot of the different documents that are available for this specific query so we will need to access all the actual documents that are stored inside the query snapshot and then for each document we will need to map it and we will need to return it as a DB record so we just use the map method so that we iterate through the full list documents and then we will take one document at a time so that we can access the data and the ID in order to build an instance of the DB record class as you can see we just pass ID equal to doc. ID and then we can access the data or the document by using doc. data so perfect now we are taking all the data that are returned from the query snapshot and we just map each single document into an instance of the Deb record and finally we create a list of data so that we can return that as a response for this method because remember that's the response type that we are expecting from this method now we can keep the DB client as it is for now we will defin need to come back later on here to add more methods but for now let's just export the DB client and the DB record and let's start to use them inside our flatter application so I'm going to close this and I'm going to make sure that we add this inside the pect doy file so we need to import the local package among the dependencies so that we have access to the library so let's go ahead and specify which is the path at which we are saving the DB client so you will specify packages for/ DB client obviously if you put it in a different location you will need to change the path from which you are importing the DB client class now after we do that we will have available the two methods that we have created and we can use them directly inside our application but we need to make some changes to the main do Dart first because we will need to initialize Firebase so we need to change the main function to a synchronous and we will use widget flatter binding. ensure initialized after we do that we can run Firebase initialize app which is going to be a synchronous and we will need to pass the options so we simply pass the information to connect with the Firebase project and we use default Firebase option. current platform so that it will pass the information on the IOS app if we running the app on iOS or Android if you're running on Android now we can start to create an instance of the client and we're going to do that here in the main. dart so we're going to create this as a global variable but later on we're going to change our approach to have a better approach for dependency injection now we're just going to keep it simple and we're going to store it as a global variable so that we can use it everywhere we need it now in order to call those two methods that we have created and to retrieve data that are specifically for the category objects we're going to create a repository so we're going to create the CATE repository and this is going to be a new file then let's just go ahead and implement this class so what we need to do is essentially two different things so first of all we will need to upload the categories data and then we'll need to be able to retrieve them from the database keep in mind that we're going to add the logic to upload data here in this application for the sake of Simplicity however we're just going to use it once and then we're going to remove it because that logic belong to an admin panel and not directly inside e-commerce because obviously the user of eCommerce won't be able to create new categories this is something that has to be managed on the admin panel side anyway let's just go ahead and let's start to develop this new class as a first thing we need to declare the DB client because this is going to be an input required whenever we are creating an instance of the category repository because obviously we will need to use this in order to send the request to the datab base then we're going to create the fetch category method that will help us to fetch all the data of the categories so it will simply interact with the DB client it will pass the category collection as an input to the method for the DB client and it will eventually take the data so the list ODB records and will convert all those data into categories objects then we're going to come back to this in a while but first let's create the categories so we're going to have the create category ories method and this is going to be a synchronous but it will not eventually return anything now we will use this to take some sample data and to use them in order to create new documents in the categories collection into Fester so I'm going to show you first of all I'm going to paste here some sample data so these are the data or the categories that we're going to have available keep in mind you won't have to type this if you want this is just available inside the repository of this project so we're going to create this categories constant which is a list of maps and we're going to keep this at the bottom of the category repository file so this is outside of the category repository class then we're just going to access all the different maps that are stored inside this list and we're going to use them as data to pass them to the ad method of the DB client so that we can create one document for each of the maps that you see here inside this list list so again feel free to customize the list based on the needs of your application anyway let's go ahead and let's start to use it inside the create categories method so we're going to have a TR catch block so that we can handle any exception if any arises and then in the catch block we're just going to throw an exception and we're going to write the error message together with some description after doing that let's start to implement the actual logic to create the categories so we're going to take our DB client and we need to use the add method we're going to use it multiple times because we will create a loop so that we will iterate through each of the categories that are in the list of categories that we just created and then we're going to take that as an input for the ad method remember you could also pass the categories as an input to the method but as we are doing it right now just to upload our coded data we don't need to pass any input to this and we can can just access to the data that are are coded in row 25 then again for each category we're going to add a new document inside the categories collection so we just need to specify which is the collection name and then which is the map with all the data that we want to load and that's pretty much it this is how we can iterate through the list of categories and use rdb client in order to upload those data now before we run this let's also implement the f categories method so we're going to have a similar approach with a tri catch block and in case of any error we're just going to throw an exception then let's modify the tri block so what we're going to do here is first of all we will fetch the categories data which is going to be the list of DB records that we can retrieve from the DB client so we just use the fetch all method and we specify that we want to retrieve all the documents from the categories list then we're going to iterate through this list of Deb cards and we need to make sure that we use the category fromon Factory Constructor in order to take the data that we are storing inside the DB card and we convert those data so we convert the full list into a list of categories object so as you can see we're also specifying how we are expecting to receive the data after we iterate through the map method so essentially we take each single entry each single DB record we access the data and the ID and we pass those as an input for the category. fromjson factory Constructor that's how we are storing the data in the DB record so we have all the inputs that we need for the from jaason Factory Constructor so we can just pass the data and the ID separately which is what we are expecting in the factory Constructor now if everything goes well we should be able to have a list of categories and we can simply take that list of categories and return it as an output for this function and we can even shorten this we can just return whatever we're creating after iterating with the map method and converting the iterable into a list so perfect for now the category repository is pretty much good to go we can start to create an instance of it as a global variable inside the main. dart and then obviously this time we need to pass the DB client as a input because it will be required in order to create an instance of the category repository so as you can see this is going to be available now and we can access the category repository everywhere in the application because we are still keeping it as a global variable now let's go inside the main function and let's use the category repository in order to upload the data of the categories so we're just going to call the create categories method and as you can see everything loaded successfully so we can assume the this has run without any errors now let's comment this out so that we won't create the categories again and as you can see we have multiple documents each of these representing one of the different categories that we had in the list before so perfect again if you want feel free to add more documents or feel free to change the structure or the categories depending on the needs of your application now we can go ahead and we can start to use those categories on the category screen so we can just go inside the state object and we can remove the import of the categories from the static list that we have in the category class so we're not going to use any more this list that you see here so we can also comment it out and from now on we're going to start to use the category repository inside the load category method to populate the list of categories that we have in row 15 so we're just just going to save the categories into a variable by using the category repository fetch categories method so we're going to wait for this future to be resolved and once this return the list of categories we are just going to use that list of categories to generate the list of extends and then we're going to take this list inside the load category method which remember loads every time we add the category screen to the widget tree and finally we save all the categories inside this variable in the state object and we use the set State function in order to do that and at this point we can also replace the data source in the UI so from now on we're just going to use the underscore category variable in order to access the categories data and we're going to do the same inside the item Builder so we're just going to take one category at the time and then we're going to use that specific category that we are retrieving from cloud file fter in order to build the card for the categories and as you can see now if we UT to start we will be able to see that we are successfully retrieving the data from cloud Fester because each of the different card is now using a different image URL so perfect now every time you start remember that the size of the card will still change because our list of extends will be randomly generated every time We Run The init State function of this widget however the images won't change anymore because we are simply using the different images coming from firestore okay so currently to retrieve the categories from cloud fir store we're using the fetch category method or the category repository then we use rdb client and we use the fetch all method to get all the data from the categories collection and with this approach every time we we need to load the categories we're going to send a request from the client to the database and we're going to read all the different documents that are stored inside the categories collection however there is another approach that we can use to access the categories data in a more efficient way so with Cloud fir store we can optimize for efficiency and reduce number of database reads and also potentially lower the cost and at the same time increase the data retrieval speed by using Cloud fire data bundle so this is an advanced tool that we can use for our application for the categories and here how it work so we can create a static data file from the Firebase document so from the list of categories then we can publish this file and we can serve the file by using a cloud function and in practice we can send a request to a cloud function in order to retrieve the data now remember every time we retrieve the data from the file file we are not going to run the queries against the database again but we will just access the data from the static file then we will read the data from cloud fer every time we need to rebuild a file and we can decide how often we want to do that so for example if we do that once every hour we will read all the categories document 24 times every day and most importantly if the number of users of the application grows the number of will not grow because again they will all read from the static file and will not send any query to the database anyway that's what we're going to do in this part of the tutorial we're going to implement this solution using data bundles anyway before we get started if you want to learn more about data bundles I definitely suggest you to go ahead and read this article directly inside the documentation of Firebase as it contains a lot of information that can be useful and help you to understand and better this tool now keep in mind that you won't be able to follow this tutorial here in the Firebase documentation step by step because for the data bundle we will need to take a slightly different approach in order to build and use the tool in fact this tutorial that you see here is using no JS and we're going to use flatter and art in order to build our project anyway it's time to go back to our project and enable a new extension inside the Firebase console so simply go ahead and open the build section and click on the extension tab from here we can search for the extension that we're looking for so in practice we will need to add the fir store bundle Builder extension and we can find this here inside the extension Hub now this extension has been developed directly from the Firebase team so you can trust its relability then it will assist us in creating all the different data bundles that we need for the application and it will also allow us to catch publicy available document in a very simple way then by using this extension we will be able to serve the data from the Firebase hosting CDN or from Firebase storage we have the option to enable either one in this tutorial we will just send a request to a cloud function in order to retrieve the data of each data bundle now before we get started with this remember that in order to access the data with this approach we need to make them publicly available which means that it's important that you remember that if certain data is only accessible to a specific group of user within the application you cannot use this method because the data will be publicly available and in this project we will use the data bundles specifically to share the categories list or e-commerce app however we won't use it for other use cases for example we won't use it for the user data as they will only be accessible by the user itself and we will not use it for the list of products and this is because in our data structure we will store the number of available units for each of the different products inside the product document and the data bundles are not very suitable for these because they will not be automatically updated when the available product quantity changes so we will need to listen to the stream of data from the product collection anyway now let's move on and let's install the extension inside the project so as you can see here there are a few prerequisites so for example we need to set up Cloud fir store Cloud functions and as well cloud storage so we have done the first one we will need to enable the function and the storage and then we will also need to change the building plan or the project so we need to be on the blaze pay as you go plan because we can't use this extension on the free plan anyway keep in mind that you will not incur in a very high bill just if you're testing this out so probably you will only be charged up to a couple of cents now let's go ahead let's click on extension and let's select which is the project in which we want to add the extension then you will be prompted with this menu and you just need to go ahead and upgrade the project if it's not on the blaze plan yet so in order to do that you will need to set up your billing account into Google Cloud then you can click on purchase but keep in mind that you won't be charged unless you incur in any usage cost anyway now that this is done we can go ahead and review all the apis they need to be enabled for this extension remember that one of these is the cloud function API because the extension will need to be able to create and deploy a cloud function which we will use in order to generate the bundles and to retrieve the data for those bundles by simply sending an HTP request to the function that will be automatically deployed by this Firebase extension so this is going to be the serve function that you see over there let's go ahead and click on enable for all the different apis and we will need to stop on the Firebase storage for a second because it will take a bit to set up the Firebase storage bucket so it will create a new bucket and keep in mind that for now we're just starting this in test mode okay so after a few seconds the Firebase storage is ready and we can just go ahead and enable as well Cloud functions then once this is done can just go to the next step and we just need to review the access that we need to Grant to this extension so this will simply generate a new service account which will have these two different roles so it's going to be a cloud data store user so that he can read the configuration and as well build bundles based on the configuration that we will store in Cloud fir store and then we can also Grant the storage object admin role to the extension so that it can potentially save files into cloud storage then in the next step the most important point is to specify which is the collection in which we want to store the bundle specification so essentially every time we create a new bundle we will set all the details of the bundles and this will be store inside the document that will be created inside the bundles collection then we can keep all the other information as they are and potentially we can also configure some Advanced parameters but we will just keep them as they are right now so now we can just go ahead and click on install extension so that we add this extension to our project keep in mind it might take a few minutes for the extension to be ready so just wait for that and then we can continue with the tutorial so now let's go ahead and let's create our first bundle specification however before we do that we will need to clone a project from the Firebase GitHub so we can follow step by step the documentation here so we will need to run G clone and we will need to clone that specific project that you see in the URL this will just give us success to an admin dashboard that has been created by the Firebase team and we will connect that admin dashboard with our Firebase project and essentially through the dashboard we will be able to create all the bundle specification that we need in our project because keep in mind that as on now we have not created any bundle specification and if you go to the fir store database you will see that the collection that will store all the bundle definition is not there yet so let's go ahead and clone this project so let's copy the terminal command and then let's go back to visual studio code so it's up to you you can decide whether to clone this inside the Commerce project or if you want you can keep it separate because remember the code for this admin dashboard will not be part of your final application so this is just a tool that we will use in order to create the bundle specification anyway this project here is built with remix and typescript so if you want you can go ahead and take a look at the code if you want to understand how it's built better but overall you should not make any change to this project let's just keep it as it is and let's use it in order to build our bundle specification and the only thing that we need to change is to actually connect this project with our Firebase project so as you can see here in the redm it explained that we need to create the EMV file to store an environment variable this is going to be the project ID of our Firebase project so let's go ahead and create this inside the admin dashboard directory so let's go and create this new EMV file then we will just have this key value pair here so we're going to have the project ID and we will need to retrieve this from the Firebase console so you can see it's going to be inside the URL or you can simply get it from the menu in the left upper part of the screen so just go ahead and copy the ID of your project and just paste it here inside this environment file now we can open the terminal and we need to start the admin dashboard and before we do that let's go back to Firebase to check the instruction on the extension page so as you can see first of all we will need to move inside the admin dashboard directory so we will need to type CD firestore bundle Builder SL admin dashboard so let's copy that and let's move inside the admin dashboard directory now as a next step we will need to install all the dependencies and remember the admin dashboard uses remix and typescript so we can use mpm install to add all the dependencies that are needed for this project then it will take a few minutes to install all of them so let's just go back to the Firebase extension and let's check the next step so as you can see the next step is simply to run the project so we can just go ahead with npm randev and this will start the application on the Local Host anyway before we go back to the project and we start admin dashboard let's go and take a look at which are the information that we can add whenever we create a new bundle specification so we will be able to create them via the admin dashboard and let's take a look at which are the field that we can add so first of all we will need to define the bundle ID this is the unique ID of the bundle and we will use it whenever we send a request to the cloud function that will serve the bundle so we pass the bundle ID so that we can retrieve the data of that specific bundle then we need to specify which is the client cache and the server cach so these are optional but we will use them so with the first one we specify how long we want to keep the bundle in the client cache in practice when the user download the data or the bundle inside the local application these are going to be saved inside the client cache of far store which is going to be stored locally inside the application and they will be there for as much time as we specify here in the client cache then we're going to have the server cach and this will Define how often we want to update the values stored inside the bundle on the server side so the lower the value for the server cache the more often we will read the data from the firestore database in order to update the server cach then we're going to have the documents the params and the queries so with the documents we can specify a list of documents that we want to include in the bundle so we can cherry pick the document that we want to add then we can pass some parameters that we can use inside the queries and in practice we can pass these as query parameter to the HTP request that we will send to retrieve the data and we can then use them inside the query so for example we can filter a collection based on the key value pairs that we will pass as query parameters finally we can add the queries and we can add one or more query in each of the bundles so for example for the categories we're going to have one query to retrieve all the different categories in the collection then if you scroll down you will see which is the actual URL to to which you will serve the bundle and to which you will need to send the request anyway just make sure not to use the dollar sign between the region so you will need to remove that and also you will need to update the bundle ID because you will need to pass the actual bundle ID that you will Define when you create the bundle specification then if inside your queries you will pass parameters you can just follow the format here so you simply need to append the parameters as query parameters after the bundle ID now let's go back to the project the dependencies are already and let's run mpm runev to start the admin dashboard so you can see if you have the local OST link over there you can just click on it and you can open it and this is going to be the admin dashboard that is connected with our Firebase project and can help us to create a new bundle so let's go ahead and create the first bundle that we need for the project so this is going to be the categories bundle so we will need to store all the data of the categories inside the file generated for this bundle so we're going to call it categories in term of ID and then we're going to pass a client cache and a server cache of 3,600 second so essentially we're going to set the cache equal to 1 hour then we will not add any file cache or list of document and for this we will also not add any parameter so we will just create one query and we can pass the name of the query so this is going to be all categories and then we just need to pass the collection part so we do not pass any parameter because we do not need to add any condition to filter The Collection essentially we just need to take the full list of categories from the database and that's pretty much it this is the bundle that we need in order to create a static file for the list of categories so now after creating the bundle we can go back to cloud fter and you will see that there is a new colle ction that will include the bundle specification for the categories bundle anyway we just need to refresh the page and then you will see the newly created collection appearing here inside the database if you click there's only going to be one document and the ID will match the bundle ID that you have added before inside the admin dashboard and then you will find all the information about the bundle itself potentially if you want you can just modify it directly in the document then you can already check which is the data stored inside the bundle so you just need to take the URL or the cloud function that has been deployed by the extension and you will need to paste it inside the browser so remember these data are public so you just paste it inside the browser and you will get the data so as you can see I've added the URL here in the browser and I've also appended the bundle ID remember to change that then you will see that all the data are going to be displayed inside the browser so as you can see we have the time at which the bundle has been created and we have all the information about the document that have been added inside the bundle overall it's quite difficult to read the document here from the browser so let's just go back to our project and let's start to modify our DB client package so that we can retrieve the data from cloud farer but we can also retrieve them directly from the bundle so we will need to go inside the DB client class and we will need to add a new method to fetch the data from the bundle so let's go ahead and scroll at the bottom so we add the fetch all and we will use this whenever we want to retrieve the data by sending a query to the database however if we want to retrieve the data from a bundle we will use the fetch all from bundle method so this will return a list of theb record just as the other method and potentially if you try to use this for the categories you will return exactly the same list as you can retrieve by using the fetch all method so let's go ahead and implement this so that we can Implement all the logic that we need to send a request over there however before we do that we need to add a new dependency inside rdb client package so we need to add the HTTP library because we need to be able to send HTP get request so we add the cloud function and to retrieve the data we need to send a get request as we mentioned before so now let's just make sure to import the HTP package here inside the DB client file and let's rename it as HTTP then we will also import the cloud firestore platform interface library but don't worry you don't need to add any dependency for this you will be able to import it because this is part of the cloud fir store library that we have added before now let's scroll down and let's implement the fetch all from bundle method so here we will pass two different inputs so the first one is going be the name or the bundle and the second one is going to be the bundle URL and I'm calling the first input collection because we will use this to retrieve the full list of categories so we will just get the categories collection however if you prefer you can change this to bundle ID and then the second one is going to be the bundle URL which represent the URL or the function that we are using to serve the bundle now let's go ahead and implement the method and first of all we need to send an HTP request to a specific URI so we're going to use http.get and then we're going to build the URI by combining the bundle URL and as well the bundle ID and anyway for the SE of clarity probably it's better to rename the collection input that we're passing here to bundle ID so feel free to go ahead and to change the name because essentially what we're combining is the bundle URL and the bundle ID anyway moving on once we get a response from the server we're going to store that inside the response object then we will need to convert the response into a list of U in8 so essentially to load the data or the response into the fir store local cach we need to have binary data so that's why we need to convert it into a list of U in8 then what we are going to convert is the code units coming from the body of the response and keep in mind that with the code unit we are just converting the string or the body of the response into a list of integers which we can use to create the uint 8 list now after we have converted the data and we have this list of uint8 we can load the bundle into the local cach so we can use the load bundle method and this will take the binary data so the uint 8 list and you can load it inside the fir Store Cache or the application so that we are going to have all the data available locally directly in the client's app now with the load bundle we will return an object which is a type load bundle task and this is an object that represent ongoing process of loading the data of a data bundle so we can use it in order to monitor the state of the bundle loading process so essentially we can check whether we have successfully loaded the data or the bundle into the firestore local cache and we can check when the process is completed so we can just use a stream from this load bundle task and we can listen to all the events emitted by the stream then when the task state is equal to load bundle task state. success we can just bring that the bundle has been loaded successfully anyway as an alternative we can also just wait for the last event to be emitted by the streamer so we don't really necessarily have to listen to the streamer so once that is done so the stream will be closed and we know that the process of loading the data from the bundle to the Firebase local cache is completed so we can actually run a query to get the data from the local cache and to do that we just need to run a normal query so we just use farer do collection we specify the collection that we want to retrieve so in this case the categories and then we use the get method the difference is that now we're going to specify the get option parameter and we will pass SCE equal to cach so that we specify that we want to retrieve the data from the fir store local cach then we can wait for the query snapshot to be ready and we can access all the data from the query snapshot so we can access all the different documents and then we just need to perform the usual logic so we need to take each single document snapshot and we need to take the ID and the data of the document in order to build an instance of a DB card so that we can return a list of theb card and basically we will return exactly the same data structure as the fetch all method now we can try to use this fetch all from the bundle method inside the fetch categories method or the category repository so we just need to change which is the method that we are calling from the DB client and then we need to pass the bundle URL so we will need to go back to the cloud fire store console and we can just copy the URL or the function that is currently serving the data for the bundles so let's just copy this and let's pass the bundle URL remember here we do not include the bundle ID so the bundle ID will be added directly inside the DB client so now we are not fetching the data anymore from the database directly but we're sending a request to retrieve the data that have been published on this bundle so if we outut to start you can see that the app will load again just as before and this mean that our bundle is working fine and we are essentially retrieving all the data loading them into the local fire store cach and we are just accessing those data directly from the local cach so it means that we can successfully fetch the data from the bundle and overall there's nothing else that we need to change so it's going to be exactly the same process to convert the list of theb card into categories and that's pretty much it for this basic implementation of the data bundle but we will come back to this later on because once we load the data into the local cach there's no need to send another request unless the local cash is expired so we will need to come back and add a more complex logic later on in the tutorial but for now let's just move on to the next step okay so we can keep the category screen as it is right now and we will come back later on to make a few changes to the UI but for now there's nothing that we need to change so we can go ahead and we can create another screen for the flatter application so this is going to be the cut Cog screen which will contain the list of products for each of the different categories before we create the UI for this let's go ahead and let's review the product class so first of all this extend equatable so that we can Implement value based equality and compare multiple instance of the product then here we're going to have an ID the name the description which is going to be optional a price which is going to be a double and then as well an image URL and as well a category keep in mind that we're only going to pass the category name but if you want you can pass more information about the category object potentially you can also Pass the full category object as an input here inside the product class then finally we also have an integer which represent the amount of products that are available inside the stock okay so let's go ahead and take a look at the rest of the class so as you can see we have the class Constructor in which all the different properties are required except for the description then we have the list of props which are the property that we use to compare multiple classes and finally we have the copy WID and a few more methods now the copy WID can help us to create new version of the instance of the product class after modifying one or more properties however we will not actually use it so we can just remove it finally we have the factory Constructor product. from jaason this will help us to take the the data that we retrieve from cloud farest and to convert them into an instance of the product object and it's going to be the same step as we have done before for the categories we will just access all the different key value pairs that are available inside the map and we will assign them to the property of the product class now keep in mind that for the prize we first cast it as a number and then we convert the number to a double in this way we avoid any error that could arise if the price stored into Cloud fir store for the product is an integer then again we do something similar for the stock but now we don't cast it as a number what we just specified that that will be an integer because remember it won't be possible to have a decimal number as a stock then finally we have the image URL as well and similarly to what we did before if there is no image URL into Cloud firestore we will just use the hardcoded image from unsplash now perfect this is the product class so we're going to keep it inside the models folder now let's go ahead and make a few changes to the repositories so we're going to create the product repository that will help us to handle the data for the different products of our recommerce so here we're going to have a kind of a different approach compared to the category repository and this is because we will not use Futures to retrieve the data of the products but we will use streams because we will need to keep listening to the value of the stock to see if the product is still available or not because remember the value of the stock so the amount that is available for each product will change every time somebody else will purchase one of those products now let's go and implement this class so first of all we will add the DB client as a variable here and we will make this required inside the class Constructor then we can start by creating the stream products method this will take the category ID as an input and we will use that in order to filter the products collection then keep in mind with this method we're going to be able to listen to a stream of products so essentially we can take the data from the product collection and every time there is an update in one of the documents that we are listening to this method will emit a new data event so it will emit a new list of products and we're going to be able to use those data in order to update the UI in this way if the stock of one of the different product changes we can also update that in the UI so that if the product is out of stock the user will not be able to purchase it now let's go and implement this so we're going to use the DB client and we're going to use the stream all by method which by the way we have not created yet then to this method we will need to pass the collection so that we specify which is the collection that we want to listen to and then we need to pass the field and the value this is going to be essentially a key value pair that we can use to filter the collection in practice we specify a field and the value for that field and then we will only return the products for which that field will match the value now let's go and implement the stream all bu inside the DB client class so again we need to go inside the DB client file which is inside our local package then we can scroll at the bottom and we can start to add the new method so again this is going to be the stream all by Method so this will take the three different inputs that we passed before so the collection the field and the value and these are all going to be strings then to complete the function signature here we're going to return a stream but the data are going to be list or the be cord because remember remember that's what we send from the DB client to the application then we will convert the DB record inside the product repository now let's go and implement this so first of all we need to define the collection reference then we need to create the query so we take the collection reference and we apply the filters on top in this case we will filter The Collection based on the value of our field so if the value of the field matches the value that we pass as an input to the method we will include that document inside the list of document that we return inside the query snapshot and in this way for the products we're going to be able to filter the list of products based on the category then let's take the query snapshot and every time there is a new snapshot so every time one of the documents get updated we're going to emit a new value then before emitting the value we need to map the documents so we need to take each single snapshot that is going to be emitted by the stream and we will need to iterate through the full list of documents that is available inside the snapshot and we will need to convert the document data into an instance of the DB record class so we simply access the document and we use the document ID to pass the ID to the Deb card and then we use data equal to doc. data in order to also pass the data finally we need to cast this itable to a list and we can just return it so every time we start to listen to this method we will retrieve the first snapshot and then we will listen to all upcoming updates every time there is a change in the data now if we want we can also implement the stream all method which is very similar to The Stream all by the only difference is that we don't need to apply the filters so perfect let's keep it here and maybe we might use it later on in the project but for now we just need to use the stream all by and as you can see we don't have the error anymore so we take the stream and we save it inside the product stream variable but obviously we can't return this because this is a stream that will emit list of theb cards so we need to change that because we need to take the list of the be cards so every time that is emitted by the Stream and we need to cast those DB cord into product object so we will need to convert them using the factory Constructor that we have inside the product class so let's go ahead and do that in practice every time the product stream emit a new value so a new list of the buau card we're going to take the list so we're going to take this snapshot that we're getting from cloud farer and we're going to take each single DB record and we're just going to use the data coming from that in order to pass them to the product. fromjson factory Constructor and remember here it's important to keep the correct order so first you need to pass the data and then you need to pass the ID because the ID is a named parameter and will go after the positional one so again we will pass the data and the ID to the from jaason Factory Constructor and with this approach we will convert all the DB records into instances or the product class so that when Whenever there is a new snapshot coming from firer we can take those data convert them into product objects and we can emit the value as well from the stream product method from the product repository and keep in mind that these are going to be the data that we will use inside the UI to display the list of products for each of the different categories anyway before we can read any data from the product collection we will need to add some inside the database because so far we don't have the product collection yet so we're going to use this list of sample data that include multiple Maps where each of the different map represent all the data that we need to build a product object so we're going to take this list and we're going to load this data into our Cloud fir store database inside the products collection to do so we're going to have the same approach as we did before with the categories so we're going to create this create product method inside the repository and we're just going to use it once and then we will remove it so we will just add it here now for the sake of Simplicity and to create the data in the database however we will not allow users of the application to create new products for the e-commerce so let's go and implement this so in the tri block we just need to have a loop so that we iterate through the full list of products and for each of them we will just use rdb client to add a new document inside the product collection so we pass products as a collection and then we just pass the product as data so we will take the map from the list now we can go ahead and we can create an instance of the product repository inside the main do Dart so we will just create it as a global variable and obviously we will change this later on but for now let's keep this here and then let's use the product repository inside the main do Dart we're going to Simply call the create products method and we're going to hot to start the application be careful and do this just once not to have duplicate products inside your database now if we refresh the page into Cloud firester you will see that there is the products collection and there's going to be multiple documents and each of these will represent one of the maps that we have in the static list of data inside the product repository so perfect now we can use those data to populate the different catalog screens and to create the UI where the user can see all the products for each of the different categories so let's go back to the main do Dart let's remove the create products method because we don't need to run this again and now we can access the product repository from the newly created screen so we will need to go ahead and create the catalog screen so that every time the user go and click on one of the different categories it can navigate to the catalog screen where you can see all the products for that specific category so we will need to implement some basic navigation and for now we will just create a top level routing table directly inside the material app so we're going to use on generate routes and here we will add a list of different routes that are available inside the project so in practice if we use the Navigator and we try to navigate to the forward slash categories we're going to return the material page route that you see in row 36 and with this we will just create the category screen then we also have a fullback scenario and we will also return the category screen if that's the case then what we need to add here is the catalog screen so if we try to navigate to the forward SL catalog route we will just return a material page route and this time we will build the catalog screen keep in mind that we need to pass an argument to the catalog screen which is the category ID then we will use this category ID in order to filter the list of products now we can pass an argument and I'm going to show you now inside the category screen how we can pass the argument it's going to be very straightforward so in the on top called backod the inal we can just use Navigator do push named and then we're going to specify which is the route that we want to navigate to in this case it's going to be forward SL catalog and then we can pass the arguments in this case we just pass the category ID as an argument then once we pass the argument we can fetch it by using settings. arguments so perfect now let's go ahead and create the catalog screen so we're going to develop it as a stateful widget and we're going to just add the logic to retrieve the products here now we have the widget so we can import it in the main do Dart and then in order to fix that error we need to declare the category ID as a variable required to build this wiet okay perfect so now let's just remove the Conant keyword before the catalog screen and the main do Dart will not have any issue anymore and if we want we can also try to navigate from the category screen to the catalog screen so the navigation should work but if we press on any of the different categories we will navigate to the catalog screen but you can see as of now we'll just return a place holder so you will not see a scaffold you will not see the full screen but you will just see the placeholder so overall you can see that is working fine obviously we need to develop the UI so that we can actually use and see the different products here but now let's go ahead and initialize the logic to load the products so first of all we will store the product inside a list in the state object by default whenever we add this widget to the widget three the list is going to be an empty list but obviously we will change this because we will create a stream subscription and we will listen to the updates from the stream products method every time that emit a new data event we will have a new list of products and we will take that list of product to update the products of our state object so let's go ahead and initialize the stream subscription inside the init state so we need to import the product repository and then we use use the stream product method and we're going to pass the category ID as an input but actually in our database we have the category name so we can't filter the list of products by passing the category ID so let's go and make a change and let's replace the category ID with the category name so whenever we navigate we pass the name as an argument and this is because if you take a look at the product collection we don't have the category ID so we can't use that to filter so we need to use the category key value perir so perfect once we do that we will be able to use that to filter the list of products so now let's go ahead and continue with the implementation of the catalog screen so as you can see we will use the stream product we will use the category to filter the list and then we will listen to all the data event emitted by the stream so every time this will emit a new list of products we will take the list and we will update the list that we have have in row 22 so we will use set state in order to do that and keep in mind that we will initialize the product subscription inside in its dat so that this is going to be always available as soon as we create the instance of the widget and we add it to the widget 3 then don't forget to cancel the subscription so that we can close it and we can clear the resources every time we remove the catalog screen from the widget 3E now let's go ahead and implement the build method so we're going to replace the placeholder with a scaffold and then we can pass a very simple app bar so here we will just add a title to specify that we are on the catalog screen then we can customize this a bit by passing the team and by using the text team and we're just going to use a headline small for the style of the text widget then let's go ahead and Implement a very simple body for this catalog screen I'm just going to use the suggestion coming from tab copilot so that we can create a list view where each list style is a different products so we take the product from the list of products that we have in the state object and for each of those products we're just going to create a list style in which we pass a title a subtitle which is going to represent the category of the product and finally we can add the trailing which is going to be a text widget to display the product price then we also have the product image URL so let's add an image as a leading so that we can also display an image or the product we can customize this a bit more so we can pass WID equal to 100 and fit equal to box fit. cover so that the image will take the whole space that it's available inside the box now let's aut start and let's try to navigate to the catalog screen again and you can see that it seems that there is an error because we don't don't see any product and this is because we are still filtering based on the category ID so we need to make a few fixes because we need to filter based on the category name which is the property that we have available inside the product document so let's go and Implement those fixes so first of all here we're good to go because we are passing the category name whenever we are navigate from one screen to the other so what we need to do is to make sure that we apply this logic as well inside the product repository so as you can see here we still have field equal to category ID which is not even included inside the product document so let's just change that to category and let's also change the input from category ID to category so perfect now the product repository should be good to go we are passing the category name and now we should be able to take the category inside the catalog screen and then to use that inside the stream products method so now once we make all of these changes we should be able to stream the correct list of products for each of the different categories so we're just going to filter the list and as you can see here we are retrieving all the products for that specific category obviously there are still screens in which there is no product at all and that's simply because we don't have product for that specific category inside our database so perfect it's seems that the implementation of the catalog screen is working fine so we have a very simple UI but let's focus first on implementing all the features for this e-commerce so in this section of the tutorial we're going to focus on creating the card screen and to set up the logic for the cart of this application now before we get started with that let's just fix the error in the main do Dart and then let's art or actually my app shut down so I need to open it again anyway now all the issues that we had before with the catalog screen should be fine and we should be able to fetch all the different product for that specific category now let's move on and let's focus on creating the cart and as a first thing I want to show you two new models that we need to add inside the project in order to hold all the different cart items and the cart itself so the cart item class extend equatable and then here we're going to St all the different products that we add into the cart so essentially whenever you add a product to the cart we're going to create a new cart item for that specific product and we're also going to store the quantity of that specific product that we have added inside the cart so that if you add twice the same product to the cart we will not add two instances of the product object but we're going to Simply increase the quantity that we are storing inside the cart item then each of this this card item will have a unique ID as well and we're going to create it using The UU ID Library anyway let's go ahead and take a look at the rest of the class here we also have a subtotal GA so that we can calculate the total price for that card item based on the price and the quantity then we have the copy with method that we will use whenever we need to update the quantity so we will take the existing instance of the cart item and we will create a new one by changing one of the properties and Fin fin we have the two jaon method that we will use when we need to convert the cart item instance into a map so that we can send the data to our backhand server remember we will create a backhand server to process the payment and so we will need to be able to send the cart items to the server so that we can calculate what's the cost that user has to pay now let's go ahead and take a look at the next model we're going to take a look at the cart and here we will store the user ID and a list that will contain all the different card items at first we won't use the user ID because we will only use it when we need to store the data in our Cloud farore database and at first we're only going to keep the cart locally then here we have the copy with method because we will need to update the list of cart items every time the user add or remove a product from the cart then we have two more gathers and the first one help us to calculate the total quantity of product that have been added to the cart so we will just iterate through the full list of cart items and calculate the quantity and then we will do exactly the same but for the price so in this time we just calculate the total price of all the items that have been added to the cart and then we have the two Jon method as well here because we will need to take the cart and send it to our backend server so essentially whenever we need to pay we send the full card to the server the server will calculate What's the total price and then we will charge the user and that's pretty much it for the models that we need to create to handle the cart now let's go in the main Dart and let's create an instance of the cart model in this way we're going to store the cart item inside This Global instance so we're going to create it here in the main. dart for now and then every time we need to update the list of items that have been added to this specific instance of the cart we're just going to modif f it and update the value of the cart variable now we need to pass the user ID and we are not authenticated yet so let's also create an our coded variable for the user ID then at first the cart item list is going to be empty and obviously it will change as soon as the user add any product to the cart later on we will change our approach in order to create and hold the value of the cart and we're going to have a more sophisticated approach with State Management but for now let's keep it simple and let's create it like this as a global variable so let's go ahead and let's also create the cart repository so with this we're going to have a few different functions that can help us to modify the cart essentially we're going to have one method to add a product to the cart then we're going to have another one to remove the product from the cart and finally we're going to have as well one method to clear the cart so that we can remove all the products and all the cart items that have been added to the cart by the user for now we're going to start by developing the add product to cart so that we can try whether we can add products and modify the card so let's go ahead and let's implement this so first of all this won't return anything because we will just modify the cart that we have created in the main do Dart then here we're going to pass a product and the quantity so let's say we need to Define which is the product that we want to add and how much of the product we will add then let's Implement some logic so first of all we need to check whether the product is already in the cart because if the product is already inside one of the existing cart item we're just going to modify the quantity so we're going to create the existing cart item variable and we just check whether there is already One cart item for this product so we just take the full list of cart items and we use the where method to see if there is a product with the same product ID or the product that we pass as an input and we take the first or null because remember that we might not find that product then let's add an if else statement so we need to apply a different logic depending on whether the product is already in the cart or not so if it's already in the cart we will modify the quantity otherwise we will just create a new cart item and we will add that cart item to the cart so first of all let's try to extract which is the initial quantity so we take the existing card item and we take the quantity then we're going to use this in order to update the value so in order to update the value for this existing cart item what we can do is we take the cart object we use the copy with method and we modify the list of cart items so first we need to remove the existing cart item and then we will add it again with an increasing quantity so let's add this and actually we will need to use the Cascade operator so we need to add two different dots because first we need to process the remove method and then we will process the add method so again we use the copy withd method we take the existing list of the product that are already in the cart and then we remove the initial version of the cart item that contain the product that we're trying to increase in term of quantity and we will add that again now when we add it we use the copy withd method to increase the initial quantity by a number that is equal to the quantity that we pass here as an input to the method okay and that's pretty much it if we find the product to be already in the cart otherwise let's go ahead and implement the logic in order to add the product to the list of cart items if there is not a cart item yet with that specific product so if that's the case we will just need to create a new card item and we will pass the ID using The UU ID Library so just make sure to add this so let's add flatter Pub add uu ID so we're going to import this dependency and once this is done we can use this dependency in order to Generate random unique IDs so perfect we can import this then we can pass the product and we will pass the quantity so these are just the input that we will use to this add product cart method and that's pretty much it so this is how we can implement the add product to cart so now if we call the cart repository we can use this add product to cart and if we pass the product and the quantity we will be able to add them to the list of cart items that you see in row number 20 so let's add the cart repository inside the main of Dart so we're going to create an instance of this and it's going to be a global instance so we can use it wherever we need it now let's go ahead and let's start to create the screen for the cart because obviously in order to visualize all the product that we will add to the cart we will need to develop the cart screen so this is going to be a key feature for this e-commerce so we're going to create this as a stateful widget and then obviously we need to replace the placeholder with a scaffold we're going to have a very simple appp bar and then let's also customize the style of the text so we're going to save the text team in row 13 so that it's going to be easier to customize the style of all the text widget that we're going to create inside this widget now before we continue developing this let's add a new route inside the on generate route so if we try to navigate to a route for which the name is equal to forward SLC card we're just going to return the card screen now let's also set the card screen as the Home Route so it's going to be easier to develop it so we're going to always see the card screen whenever we start so we don't need to navigate there every time now obviously we don't see anything because the screen is empty and there's no product added to the cart now let's go ahead and implement the body of the scaffold and first of all we're going to use a single child scroll view because remember if there are a lot of products added to the cart we might need to scroll to see all of them because there might not be enough space on the screen to fit all of them then we're going to pass a column as a child of the single child scroll View and we also add some padding to create some spacing then let's add a container for now so that we can add a bigr color to the column but this is just going to be there as we develop it to make it easier to see which is the size of the column then the first widget that we will add here is a text widget and here we will specify how many products have been added to the cart so we will count the total quantity of items so we can use items in the cart and and then we can check which is the total quantity of items that has been added to the cart by using cart the total quantity so we can access the ga from the cart model and of course for now it's going to tell us that there's no product added inside the cart then after this text widget we're going to add some conditional logic because we want to display different UI based on whether there our product added to the cart or not so first of all we can add this other text widget in in which we tell the user of the application that there's no items inside the cart but let's add the conditional logic so first we check whether the list of cart items is empty or not if it's empty we return the text widget otherwise we will create a list view Builder so that we can display multiple cards one for each of the different card items so the item count is going to be equal to the length of the list that include the cart items then we set physics equal to never scrollable scroll physics because we can already scroll this view thanks to the single child scroll View and you should not create views with multiple scrollable nested inside each other then let's go ahead and let's prepare the item Builder so that we can Define which is the widget that we return for each the different cart items that we have in the cart so here we will just return the cart item card which is a widget that we will need to develop soon so this will include all the information of the cart item including an image of the product the quantity that has been added inside the cart for that specific produ and it will also include some buttons that the user can press to add more or remove the quantity so let's go ahead and let's create the stateless widget this take the cart item as a required input because obviously we need the information about that in order to create the UI for this and then you will return a placeholder and for now we're going to keep it like this we're going to come back later on to modify it because obviously it's better to create it as soon as we have some product inside the cart so we will need to create and add some cart item first now let's just add another element to the UI the cart screen before we move to the next step and this is going to be the bottom up bar over here we're going to have some information including the total price of the cart and as well we're going to have one button that will allow us to navigate to the next screen which is going to be the checkout screen and which is where the user can complete and pay for disorder now let's go ahead and add a row inside the bottom up bar as a child so that we can add the total price on the left hand side and then we will add a button on the right hand side so we display the total price with a text widget and we can just access the value of the total price for the current cart by using the total price GA then we're just going to create a filled button and for now the onpress is going to be empty and the text widget is going to tell pay now so that the user know that you can press on that button to navigate to the checkout screen and complete the payment then we're going to wrap both the widget with an expanded widget so that they will share equally all the space that is inside the row so perfect let's just add some space between the two different expanded widget wiget so that we are sure that there is some buffer space between the two widget and then we are pretty much good to go with the bottom up bar for now so we will implement the logic to navigate to the next screen later on for now let's just go in the main. dart and let's modify again the initial screen that we see inside the material app so that we can go again to the category screen and then we can modify the catalog screen so that whenever we Press On any of the different products that are inside the catalog we can actually add them to the cart so we're going to have a very simple implementation of this feature so that inside the catalog screen we have the list style and we can pass the logic to add the product to the cart inside the on top call back or the list style later on we're going to change this a bit but for now whenever you press on it we're going to call the add product to cart we take that specific product for that list style and we're going to add one in termal quantity for that product to the cart now let's also add some actions inside the app bar of the catalog screen so that we have an icon that represent the cart in the upper right side then we're also going to add a badge and the badge will tell us how many products have been added to the cart so we're going to have is label visible equal to true if there is at least one product inside the cart so if we have created at least one cart item and then we're going to use the shopping cart icon as a child then we need to take the badge and we need to wrap it with an icon button because we want to be able to press on it and to navigate to the card screen so again let's add the icon button let's develop the on press as well and here is where we're going to use the navigator to push a name route so we are essentially going to navigate to the card screen and now we are very close to test this new feature of the application let's just make sure to add the label so that we can display the number of items inside the cart inside the badge and then before we can actually add a product to the cart let's modify the on top callback because we will need to call a set state after we add the product to the cart because we are inside a stateful widget and we will need to build the state object in order to change the value that we're passing to the badge so in order to see the total quantity inside the badge we will need to call the set State now let's try to add products and you can see that every time you click the quantity of product added to the cart increases and also these products are now added to the cart and you can see them inside the cart screen you can also see the total and one different placeholder for each of the different cart items keep in mind there are three different press holders and five items inside the cart and this is simply because some of the products have been added multiple times and when you add a product multiple time you just increase the quantity of the cart item now let's move on and let's implement the cart item card so that we won't see any more these three different placeholders but we will see three different cards with the actual information of the products that have been added to the cart so let's go ahead we are going to use a card and then we're going to pass a padding widget as a child in this way we're going to add some spacing on all the direction of the card so perfect now we have the card let's go ahead and pass the child as well so we're going to add a row so that we can add multiple widget inside the row so on the left hand side we're going to add an image or the product so we're going to use also the clip wck widget so that we can clip the image corner and we will be able ble to have rounded corner for the image then we pass the image using the image Network widget and we just need to access the image URL or the product that is stored inside the cart item then let's also set the height and the width to 90 and let's set fit equal to box fit. cover and now the shape is going to be a square so perfect now the image is appearing on the screen and later we're going to modify it a bit because we're going to display the quantity added for the product on top of the image with a badge but for now we're just going to keep it as it is and we're going to add more information including the name and the category of the product and as well the price of the products so now let's go on and let's add a column here we're going to add the name of the product so we're going to add a text widget so we're going to take the product from the cart item and we're going to access the name property so as you can see now that disappearing ing in the column but let's make sure that this is aligned on the left hand side so let's use cross axis alignment equal to start then let's also add some space between the image and the column then let's modify as well the style of the widget so that we can make the font bold so I'm going to create the text theme variable inside the build method so that we can reuse this and then we're going to use a body large text style and with the copy withd method we can change the font weight to bold okay perfect this is working for now let's just make sure that if the product name is very long we do not write more than two lines now I'm going to paste another text widget inside a row and with this text widget we are going to display the total price for that product so if you have one product it's going to be the price of the product but the quantity could be greater than one and if that's the case we're going to display the total cost for that cart item then let's remove the style and let's remove also MAX Line equal to two so we don't need that and here as you can see we are simply multiplying the price for the quantity and we convert it to a string but actually we don't need to make the calculation here because if you remember inside the cart item we have the subtotal GA that is already doing this calculation so we can just access that and use it so perfect now we're displaying the cost let's also add a couple of different different icon buttons on the bottom right side so that we can modify the quantity of the product that we are storing inside the cart item so as you can see we have the first icon button that help us to increase the quantity and then we're going to have the second one they will remove the product from the cart now we also have a text widget here and this display the total quantity of the product that has been added to the cart then keep in mind that we will remove this later on because we're going to add the total quantity as a badge on top of the image but for now let's just keep the text widget is going to give us the same results then in the second icon button let's comment out the code because we have not implemented yet the remove product from cart so we're going to keep the onpressed but if you click on it nothing happens now let's go ahead and let's make a few changes in thmo style for the icon buttons given that we don't have much space we can use visual density. Compact and this will make the icon button a bit smaller and here we're pretty much good to go we just need to make sure that there is some space between the prize and the icon buttons so perfect now the layout is pretty much good to go we can just add a bit more spacing between the title so the name of the product and the other elements and finally let's just take this cart item card and let's move it into a separate file so we're going to create the widget folder and we can store all the complex widgets that we're going to create for this project here also if you have a widget that you can reuse across multiple screens this can be created here so that you can reuse the same code over and over again now let's just make sure to import all the different import that are required for the cart item card and then we can save this and we can go and fix the cart screen now we will need to import the widget from the newly created file and then we are pretty much good to go here let's just go ahead and remove the color that we are using as a background and let's just fix the alignment inside the column so we will need to align the column on the left hand side so we can use cross axis alignment equal to start and after doing so we are pretty much good to go with the UI or the card screen you can try to add more more products and you will see that the quantity will be increased and as well the total price will change accordingly then remember that if you press on the minus button nothing happens because we have not implemented that functionality yet and we will come back to this later on then remember that every time you aut to start or you close the app the cart data are going to be lost so it's going to be reseted to an empty cart and if you want to store the cart locally so that it persist across multiple session we will need to make a few changes and we will need to set up some local storage anyway for now we're going to keep it as it is so we are going to modify this a bit later on but for now let's just move ahead to the next part of the tutorial to implement the payments okay so the card screen is pretty much good to go for now and we need to move ahead to the next step of the tutorial because we need to start to create the checkout and we will need to implement stripe both on the client side and on the server side and obviously we will need to create a new screen where we can let the user input his credit card information in order to complete the payment this is going to be the checkout screen so let's start by adding a new route inside the on generate route property so whenever we try to navigate to the forward slash checkout route we're going to return the checkout screen and we can do that by pressing on the pay now button on the cart screen now let's go ahead and create as well a new file so we're going to have the checkout screen. Dart and here we will create the stateful widget that will display the UI for the checkout screen here we're going to use the flatter stripe library in order to create a card form field where the user can input his own card information but remember we won't have access to that now let's go ahead and let's Implement stripe so first of all we will use the flatter stripe library and with this Library we will have access to the stripe flatter SDK that will provide a some widget that allow us to ask for the card detail of the user without recording any information in this way we can use an outof thebox widget coming from this library to let the user input the information of the card send them to stripe without having any risk in term of security so we will use this library in order to do that and with this Library we will also be able to connect the application with the stripe project that we will create so we will initialize the stripe SDK by passing the publishable key now you can read more about this Library here but for now let's just go in the terminal and let's import it by running flatter Pub ad flatter stripe so we will add this inside the root level pect yaml file now we can start to use this inside the checkout screen so we can start to implement a simple UI here inside the build method but keep in mind that if we add any widget from the flatter stripe Library it will not work yet because we do not have a stripe account however let's create a very simple UI here for the checkout screen and then I'm going to show you the error that we're going to get and then we can go ahead create a stripe account and debug the issue now let's go ahead we have a single child scroll view we have a column and then we're going to wrap the column with a padding widget so that we can add some spacing on all the edges of the column so we're going to have Edge in set equal to all and we pass padding equal to 16 then here we can add a text widget inside the column to inform the user that he will need to add his own card details then we can add some spacing with a sized box and finally we can use a wig from the flatter stripe Library so the card form fied so these come from the flatter stripe Library so make sure to import this and you should also close the application and restart it to make sure that the library has been initialized then this widget can be customized a bit so we will pass enable postal code equal to false and then we will pass on card changed this is a callback that will execute every time there is a change inside one of different text form field inside this wiet now what we're going to do is the following we will have a controller for the card and we will update the value every time the user modify any of the information inside the card form field so we will also need to initialize this controller and we will do it inside the state object or the widget this is going to be the card controller and it's going to be the card filled input details widget then we're we're also going to add the Boolean loading and we set it equal to false we will use this when the user try to submit the payment now we can try to navigate from the card screen to the checkout screen so let's set the logic for navigation inside the card screen file so we can just use the Navigator and we use the push named method we will pass the route name and we will try to navigate over there so now let's s start and let's see if this is working now as soon as we try to navigate from the cart to the checkout screen we're going to have an error you can see that the app is now stuck so let's go ahead and take a look at the theb back console to understand which is the error that we've just gotten so as you can see the stripe library has not been initialized yet so essentially we have not connected our stripe project with the library so we will need to mix some changes in the main do Dart and we will need to use the publishable key that we can get from a stripe project in order to connect the app with the project itself so let's go ahead and let's go to stripe.com because we will need to create an account I've already created the account but overall you will need to go ahead and sign up with a new account or use an existing account if you already have one so just go ahead add all of your information and anyway as soon as your account is cre created you should be redirected to the dashboard and you should be in test mode if you're not in test mode just change the switch on the upper right part then you can go to the developer section and here is where you're going to find the publishable key and the secret key so we're going to use the publishable key inside the flatter client so inside the application because this can be shared and then we're going to use the secret key inside our backend server using Cloud function because we need to be sure not to share the secret key because whoever has access to the secret key can interact with the API of stripe and make changes to your payments so you need to be very careful with that however keep in mind that you can take the publishable key and use it inside the client also remember the client is never safe but don't worry it's not a problem to put this key inside the client so let's go ahead and take that so we're going to go inside the main Dart and we're going to make a few changes here so we can use the stripe library and we're going to set up which is the value for the publishable key for now we're just going to AR code the publishable key here inside the main doart so make sure to import the flatter stripe package inside the main doart and then you can use stripe. publishable key this is a Setter so you can pass a value and this is going to be set as publishable key for your application so we're going to just our code the key that we can copy from on the dashboard and we can paste it here later on we will change the approach so we will use envied in order to manage all environment barable and we will move this string inside the list of environment variables but for now we keep it here are coded then we're going to create an instance of stripe and we can use apply settings to pass some information we could pass the value of the publishable key the merchant identifier and a few more information including the URL scheme if you have but for now let's just keep it empty so we're just going to call this and we're going to wait for that and that's pretty much it for the changes in the main do Dart however we can't use the flatter stripe Library yet so we have a few errors so let's shut down the application and then we need to go ahead and make changes inside the iOS directory and as well inside the Android directory because there are platform specific changes that need to be done in order to imp implement this package so we're going to open the iOS directory and here we need to make sure that the iOS version that we're using is at least 13 so this is the only requirement that we need to change so the library is not compatible with older version of iOS so we will need to make changes inside the Pod file so we will uncomment the line in row number two and we're going to change the platform to 13 then we can make the changes as well the runner XC workspace so we will reveal it in the finder and we can open this with xcode then we can change the version of iOS across different places inside xcode so let's wait for this to load and then you can see that you're going to be inside the runner file so just click on that and change the minimum deployment of iOS to 13 then you can click on Runner inside the project section and you can make sure that that the iOS deployment Target is 13 even there then let's open the pod file and we're going to change the version of iOS as well here so we're going to set it equal to 13 so that we make sure that everywhere we have set it up equal to 13 so that the library will be compatible now if we start the app again it will work fine on iOS so we can test that and we can see how the checkout screen look like so as you can see the app successfully started which means that at least we successfully set the publishable key because we are doing this in the main Dart then let's go ahead and try to take a look at the checkout screen so let's add some product to the cart and let's move on to the checkout screen and as you can see we have the pro-built UI that let the user input his own credit card information now keep in mind that the card that I'm using is a stripe test card so you can just use the same in order to test it and remember this will only work in test mode now let's go ahead and let's make changes as well to the Android implementation because we need to make sure that the application and the flatter stripe Library work as well on Android so I opened the Android simulator and now we can go inside Android directory to make a few changes so let's go and take a look which are all the changes that we need to make so first of all we need to make sure that we use Android 5 so we need to change the Android API to 21 or above then we need to check which is the cotl version that we're using so we need to make sure that it's 1.5 or above and then we need to make a change inside the team of the application so Android as a default team and we need to make sure that we use the same that is required for this library because we need to make sure that the widget that come from the library are compatible with the team or the application so we will need to change this and we will need to use a descendant of team. app Compact and keep in mind that AC ity is just a screen inside an Android project anyway as a next step we will also need to check which is the version of the Gradle build tool that we're using so we need to make sure that we use an upto-date version and finally we also need to use the flatter fragment activity inside the main activity. cotlin file so again some of these changes are because we use the Android strap SDK and these require a specific theme for their UI component and then we need the flatter fragment activity this is needed for the payment sheet however we're not going to use that approach but we will still need to make the change so now let's go in the build of griddle and we can keep the current cutl version 1.8.10 is going to be fine for this project then we can keep as well the current Android tools so row number nine does not need any changes so pretty much we are good to go with the build Gradle so there's no changes that we need to make here so we can move on to the next step and we can open the build grd file inside the app directory so here we can scroll at the bottom we can make sure that the compile SDK version is equal to 34 then in the compile option we need to have Java version and we need to have version 17 so just make sure to have the same version installed here and then you need to make sure that the minimum SDK is equal to 21 older version of Android won't work for this then let's go ahead and let's save the buildo Gradle we are good to go here so we can open the main folder and then we need to go inside the main activity so here we will need to add an import the flatter fragment activity keep in mind in row six we were using flatter activity before and I've already changed it to flatter fragment activity in this way the library will be compatible and we can use the payment sheet if we want now let's go ahead and fix the theme or the application so inside the main directory we need to open the rest directory and we will need to go inside the values so we will need to modify two different files one is for the light mode of the team and one is for the dark mode so let's start with the styles.xml so here I've already made the changes and you can just copy it from the project that you can download from myab repository however you just need to make sure that here you use team. app compact. light do no action bar and you need to make the changes as well in row 15 so you will need to use name equal to normal team and the parent need to be team. material component now you can type this or you can just take the changes directly from my gab so just go ahead and take a look at the starter project then the same changes that we have here inside the style XML need to be applied as well in the the other directory so that we have the same changes available for the dark mode or the team so as you can see you just need to modify r four and row number 15 now we can try to launch this on the Android simulator as well and you can see that everything will work just fine so the app is started we can see the different categories and now we can try to go ahead and see what's happening on the checkout screen so we can see how the checkout UI coming from the flatter SDK looks like even on Android so let's go ahead let's open the catalog let's select some of the product let's go to the cart and finally let's navigate to the checkout screen again the UI is slightly different from what we had on iOS and this is because this is a native Android implementation versus the native iOS implementation that we had before but overall the functionality is the same you can input the information and then you can can use the information that the user has input in order to create a payment method on stripe so that's pretty much it so it's working on Android side so let's go ahead and let's go back to the iOS simulator in order to implement the functionalities of the checkout screen and here we are let's continue developing the application and as you can see we are back on the iOS simulator so let's go ahead to The Next Step which is to develop some backend logic to process the payment using stripe to do this we're going to use cloud function for Firebase and this is a serverless framework that will allow us to execute any logic that we need on the back end without the need to deploy any resources so it's serverless and we just need to write the logic then we will need to write the logic in JavaScript typescript or Python and for this example we're going to use Python and anyway in this part of the tutorial we will create one function that will help us to process the payment so we will send a request requ EST from the client to that specific function and this will help us to create a payment intent on stripe that will record the intent to collect a payment from the customer who's trying to complete the purchase and in some cases we will be able to collect the payment straight on so as soon as the user send a request we create a payment intent and then we process automatically the payment however in some other cases there will be the need of confirming the payment so that's why we will be creating another function as well but we will do this later on in the tutorial and also keep in mind that for this project we're going to use the second generation of cloud function there's two generation and the first one does not allow us to use Python to deploy the function so in this case we're going to use the second generation and you can refer to this article here in the Firebase documentation to see all the differences but keep in mind that the newest generation is built on cloud run and event Arc and this allow the function to have longer request processing time so for example if you invoke a function with an HTTP request it can run up to 60 minutes while in the older version it was only able to run for 9 minutes then another difference is that there is a native support for some event Arc triggers and this means that for example we could invoke a function if there is a change in a document into Cloud fun fire store and this is something that we might add later on inside our project so let's say that a user complete an order we will need to check which are the products that he purchased and we will need to reduce the stock for those products and this is something that we can do invoking a cloud function by using one of these event Arc integration anyway let's go ahead and let's go back to our Firebase project let's click on get started and let's follow the step that the console will tell us so first first of all if you don't have the Firebase tools you can go ahead and run this terminal command so you can just run mpm install DG Firebase tools and obviously if you run this you will need to have npm available inside your terminal in my case I've already added this so I'm just going to continue to The Next Step so in order to initiate our project we will need to run fire Bas in it so we're going to open the terminal and we're going to type Firebase in it but also we're going to add functions so we're going to tell this that we only want to enable the functions because keep in mind that we have already added FES to our project and the only thing that we need is to add the functions then we're going to choose the same project that we're currently using so just go ahead and choose the same project that you added before and then we're going to choose python feel free to use any of the other languages if you prefer but overall the logic will be the same you will just need to write it in a different language if you just want to follow step by step just select Python and then wait for this to be ready let's also install all the dependencies and then we will need to wait for a couple of minutes in order for this to be ready okay perfect and as you can see the initialization is completed So within our project there is a new directory that contain all the newly created code and here is where we're going to add all the functions that we will create for the project so we're going to add the logic in the main dop so let's go ahead and let's start to take a look at this and you will see that there is already some code inside this file so there are two initial Imports that we can take a look at so the first One Import the HTTP function and this will allow us to trigger functions by sending HTTP request and so we can simply send an HTTP request to an endpoint and we will activate the function that will then run then as a first thing we need to initialize the app so that's why we have the second import and we will do this this by setting app equal to initialize app and keep in mind that we need to run this to set up the necessary context for the function so if we won't do this we won't be able to interact with other Firebase services like we won't be able to read data from the database then let's go ahead and let's handle the first function so here we're using a decorator the htps function.on request and with this we can specify that whenever we send an HTP request to the endpoint we can call this method and this is where we will send a post request with the payment method ID and a few more information so along this for example we will pass a list of item and optionally as well the currency here we will send a request and eventually we will receive a response from the function so let's go ahead and implement the logic so first of all we're going to print the request method and then we're going to use request. getjson this will take the input that were're passing through the post request as a body and he will print them keep in mind we will print these inside the server logs and not in the terminal so we will need to check that in the server logs then we can add some logic to make sure that this function will only process post request so if the request method is not post we are just going to return a response in which we specify that there is an error so we will set the status to 43 and the response to forbidden because again you cannot use a get request with this point because with this Cloud function we are not trying to retrieve data but we are posting the data that we need to send to our server to initiate the payment so now let's move on and let's continue implementing this method so as a first thing we will need to fetch the input that we're passing through the body or the post request so we will need to fetch the payment method ID so that we can identify which is the payment method that the user has added inside the application and we can access the body by using request. getjson so we're going to save that into the data and this is going to be a map then we can access the key value purse with data. getet then inside the get method we just need to specify which is the key for which we want to access the value and we're going to do this for the payment method ID the items so the list of products that the user is trying to pchas then the currency and then as well the use stripe SDK parameter so perfect we're going to store all of this information into these different variables then as a next step we need to calculate the total price again we do not calculate the total price on the client because somebody who has bad intention could manipulate the amount on the client so we need to make sure that we just take the list of items that the user is trying to purchase and we calculate the total on the server obviously we can calculate the total on the UI directly in the client that's what we're doing to display the total to the user however we will need to calculate it again on the server anyway we're going to add a too for this and we're just going to AR code a price for now so we're going to come back to this later on in the tutorial for now we're just going to set the total to an arbitrary price anyway let's go ahead and let's start to create a payment intent so we're going to use a try accept block so that we can handle any error that might arise during the process so we're going to handle the exception first and overall we're going to have a very simple implementation of this so if there is an exception throughout the process we're just going to return a response that will inform the client that there was an error during the process so we're going to have status equal to 500 and this is going to be an internal server error now let's go back to the try block and first of all we check if we can successfully get a payment method ID from the request body if we can't we won't be able to identify which is the payment method that the user want to use for the transaction and we won't continue otherwise once we get the payment method ID we're going to have a map with all the parameters that we need to create a payment intent and actually to create a payment intent we will need to import the stripe Library so let's just add that as a dependency then then we're going to have the create method from the payment intent class and this will take the API key and then we can pass a map with all the parameters that we want to use to create the payment intent so we can just spread the map that we have created in R number 30 keep in mind this is still empty but we will change it in a few seconds for now let's just make sure that we return our response after creating the payment intent because we want to send the information of the payment intent back to the class Cent now let's go ahead and let's add all the parameters that we need inside the params dictionary so let's go ahead and let's use the suggestion from GitHub copilot so we're going to add all of these parameters and we're going to take a look at them so first of all we need the payment method in this way by passing the payment method ID we're going to be able to use the strap API in order to retrieve the payment method that the user is added through the UI so with the flatter stripe SDK we will input the payment method and we will create the payment method ID then we will pass the total and this is going to be the amount that the user will have to pay so this is going to be added to the payment intent as well then we will set confirm equal to true and this means that as soon as we try to create the payment intent we will confirm it and we will try to retrieve the fund from the user so that we can complete the payment instantly as soon as we create the payment intent then we can actually remove confirmation method we don't need this and we can add one more parameter and this will help us to specify which are the payment method that can be processed automatically so without any additional action or confirmation required from the client here we need to specify enable equal to true and then we will need to pass another parameter which is allow redirects and we will specify never keep in mind there could be some payment method that will need the user to be redirected on a different website to confirm the transaction and that's something that we will not enable in this project okay now let's go ahead and let's find our API key that we need to use inside the function so this is going to be the secret key and it will allows the function to have full access to our stripe platform so again you can find this inside the developer section and you need to go inside the API key tab then you will need to reveal your secret key and you will need to copy and paste it inside your Cloud function however keep keep in mind that you need to be very careful with this API key because if somebody get access to it it can access and modify all the payments inside your stripe dashboard anyway for now we're going to hardcode it here inside the main dopy but later on in the tutorial we're going to have a more sophisticated approach so we're going to use this tool called secret manager on Google Cloud so that we can store safely the API key inside secret manager and we can use secret manager here inside the function to import it but again for now we're just going to hardcode it here inside the stripe secret key variable that we have in row number six then inside the create function we can just pass that as the API key and at this point we are pretty much good to go we can almost deploy the function and test if this work fine however before we do that remember that that we have imported the stripe library and as you can see this cannot be resolved so it means that we don't have it available in our project yet so what we need to do is we need to modify the list of requirements inside the virtual environment so this virtual environment has being created automatically when we created the function directory with the fireb CLI and it has a list of dependencies this come by default whenever you create the function folder anyway we need to activate the virtual environment and we need to install the missing dependency which is the stripe Library so we're just going to open the terminal we're going to clear it first and then we're going to move inside the function directory so here as you can see you have the virtual environment and you can use Source virtual environment being activate in order to start the virtual environment so once this is ready we can modify the dependencies that are added here so we can use pip install stripe to add the python dependency then once we have it we can just take the full list of dependency and we can add it inside the requirement. txt so that later on we can upload this file together with the rest of the function to Firebase and it will know which are the dependency that we need in this project and this time it will include also the stripe library and anyway to do that you will just need to type pip free is greater than requirement. txt this will create the list of dependencies inside the requirement. txt file so I'm going to do it again because there was a typo in the name of the file and after that you can open the requirement of txt and you can see there is a list with all the different packages that we need to import so just make sure to scroll at the bottom and to see if the stripe package has been added as well so as you can see the stripe Library has been added and now we can use it inside our function so in order to deploy the function we need to open the terminal again and let's close the virtual environment then we're going to use Firebase deploy Das Dash only functions so that we're going to deploy only the function to our Firebase project in this case we will deploy all the function that we have in the main Pi but if you want if you have multiple functions and you want to deploy only one you can specify which is the name of the function that you want to deploy then it will take a few minutes for the function to be deployed especially the first time you do it it will take a bit longer because the CLI command will need to also enable a few different apis and finally once you receive the success message it means that your function has been deployed so you can go back to the Firebase console and you can see if you can find your function inside the function menu and once the page loads we should be able to see two different functions so the first one has been created before in the tutorial by the extension that we're using for the bundle Builder and with this we can create the bundles for the categories and we can serve the data for that specific bundle then the new one is the stripe pay and point method ID and this is the one that we can use in order to process the payment on stripe so as you can see here you will find a URL and this is the endpoint to which you can send an HTP request so you can send the HTTP request over there and this will trigger an execution of the function then here you can notice as well that the version is equal to version number two and this means that for this we're using the second generation of cloud functions now remember that inside the application we are not authenticated at all so we have not implemented authentication so far yet so what we need to make sure is that this function can be invoked by unauthenticated users otherwise we won't be able to call this function and execute the logic so we need to go ahead inside Cloud run and to check whether the settings are correct for that because remember each function that we create with the second generation of cloud function will be connected to a cloud run service so you can just go inside your Google Cloud project which is the one that the same name of the Firebase project and get created automatically once you create the Firebase project then you can open the menu and look for cloud run then here you should be able to see one service being deployed and active so this will have the same name of the function that we have deployed so strip pay and point method ID then as you can see this already allow unauthenticated invocation but let's go ahead and let me show you where you can change that that so first of all you can verify that this is connected with the cloud function then you can go inside the security menu and you will see that these currently allow an authenticated invocation which means that if you're not authenticated and you just send a request to this end point you can still call the function and trigger an execution of it so that's fine for now because we need to be able to do that from the application without being authenticated so now let's go ahead inside our library and let's make sure that we can invoke the function from the application so whenever the user go to the checkout page we need to send an HTP request to the function and to do that we're going to create a new package inside the packages directory so this is going to be the payment client and with this new package we will manage all the HTTP request they will send from the application to our Cloud function specifically for the payment so we're going to call this payment client then we're going to have this new local package and we need to develop it so we're going to start by removing the test folder and then we need to create the seource directory and the payment client. Dart file so this is where we're going to have all the logic to send the HTP request but before we do that let's move inside the payment client and let's add the dependencies that we need to do that so we will need the HTTP library in order to send request to our Cloud function so with the HTTP Library we can send the post request to the endpoint that we just created so let's implement the code to do that inside the payment client. Dart file now keep in mind that the outer file the one that is directly inside the lib folder is the one that we use to create the library and to export the files of the library in this way we're going to be able to import all the code that we develop here inside our main project now here we're going to do the following so we're going to create this class called payment client and as a first thing we need to define the HTTP client so we're going to import the HTTP library and we're going to call this HTTP then let's have a constant outside of the class and this is where we can store the endpoint or the function so we will just copy and paste the URL of the function and we're going to store it there you can copy either from the cloud run menu or directly into filebase so you just go into the function section and you can take the HTTP so it's going to be exactly the same so just go ahead copy that and save it inside this constant that we have here inside the payment client so that we will have a function that can send a request to this specific endo and you can do the same if you create multiple function and you have multiple end points then in the payment client we're going to specify that we can pass the HTTP client as an input but this is optional and if that's not passed as an input we're just going to create a new instance of the HTTP client then we're going to have the process payment method and as well the confirm payment method for now we're just going to use the process payment method to send a request to the endpoint method ID URL and ideally as soon as we send the request we will create the payment intent and we will confirm and complete the payment automatically however in some cases we will need to use another method which is the confirm payment method because once we send the first request the payment will require an additional level of Confirmation and that's when we will call the second method however we will need to develop this later on because this will require another import point to process the logic and that means that we will need to create another Cloud function in our backend server so for now we're just going to throw the unimplemented error in the confirm payment method and then we're going to focus on developing the process payment so here first of all we're going to define the function signature so this will be asynchronous and it will eventually return a map that will contain the response that we receive from the server so this map will just take the response in term of whether the payment was successful or not then the input that are required for this method are the payment method ID and the list of items that the user has to pay for so all the different cart items that you have added to the cart for this current order then we're going to have as well the currency and the stripe SDC but we will pass them as optional values because we will our code a default value Val for them so for example here we will remove the required keyword and we will set it equal to Euro obviously you can pass other values depending on the currency accepted on your e-commerce then we're going to set use stripe SDK equal to True with this default value now let's go and implement the method first of all we will need to create the URL so we need to create a URI by using u. par and we can just pass in point method ID URL then we can start to send the request using the client so we will use HTTP client. post so with the post method we need to pass three different parameters so first of all we need to specify the URI to which we send the post method then we need to pass the body so all the information that we want to send together with the post method and finally we will need to specify some headers as well now as you can see in the body we just have a map with all the information that are required for the function so these are the same key value Pur that we are parsing inside the function that we have created before so as you can see with the data we access the map and then we take all of them one by one so perfect just make sure to pass all the required information that we're currently using inside the cloud function then let's develop the headers so that we can complete the implementation of this post method so we're just going to add a header for now which is content type equal to application Json and with this we just tell the server that we're going to pass the body as a Json then we're good to go here let's just wait for the method to be completed and we're going to receive a response from the server we're going to access the body of the response and this is going to be a string and we will need to use the dart convert Library to convert the string into a map so that we can respect the function signature of our process payment method and we can return a map and we will use this map later on inside the client application now we can save the payment client for now and we can use it inside the application so let's go ahead in the root level Prospect DOL file and let's add the new dependency so we will just need to specify which is the path and which the new package has been stored so perfect at this point we can use it inside our application so we can use it to send a request and to Kickstart the payment process now let's go back in the checkout screen because we will need to change some of the UI here in order to send a request let's go ahead and take a look at the UI so if you go to the category screen you select one category and then you can add some to the cart then you reach the checkout screen where you can add your cart details however we have not implemented yet the bottom up bar which is where we're going to have the button that the user can press to send a request to complete the payment so we need to implement that before we can try whether our implementation is been correct or not so let's go ahead and let's set the bottom navigation bar so we're going to have a bottom navigation bar that is very similar to what we have inside the card screen so we will just use the bottom up bar widget and then we will have a row so the row will have two different child the one on the left hand side will specify how much the user has to pay and then on the other side we will have the button that the user can click in order to complete the payment so this will send the request using the on press call back so as you can see the button is showing up however we have developed something very similar in the card screen so so let's just copy the same implementation of the bottom up bar that we have inside the card screen and let's reuse it now later on we can create a custom widget and we can just use the same widget for the bottom navigation bar property for now I'm just going to paste it here and then obviously we need to customize this a bit the only difference now is that we need to change the onpress call back that we're storing inside the field button because obviously we're not navigating anymore to the check out whenever we press this but we need to send a request to process the payment so let's remove that and let's implement the new version of this so we will need to use our payment client and in order to do that we will need to go back to the main doart so that we can create a global instance or data then we will also Implement some logic because as soon as the user press we need to change the value of the loading Boolean so that the user won't be able to press the button twice in a row so we don't process the payment twice so here in the onpress we're going to check which is the value of loading if loading is equal to true we return null which basically deactivate the button otherwise we will have our callback where we're going to process the logic to send the request now we're going to break down this into two different pieces the handle payment and the process payment so with the handle payment we will just check whether we have a valid card detail inside the form because if we don't have valid card details we can't send the request so if we have a valid card and all the information are completed the handle payment will then call the process payment method which is the one that will interact with our stripe API and it will send the request for the payment so again in the field button we're going to call the handle payment method if loading is equal to false which means that we are not currently processing the payment yet now let's start by developing this so we're going to have an asynchronous function here and first of all we will check which is the status of the C details so if the user has successfully completed the input of all of its card details we're going to proceed with the next step otherwise if the card has not been added to the form we're just going to show a knack bar so that the user know that you will first have to complete the details of the card in the form and as you can see it works fine so when the details are not there we will see the snack bar bar otherwise we will move on to the next step and the next step is simply to change the value of loading equal to true because we have verified that the car details are have been added and now we can start to process the payment so we don't want the user to press this again so we change loading equal to true and we update the value inside the state object by using set State then we're going to have a try catch block so we will try to call the process payment method and if that doesn't go true we're just going to throw an exception in the exception we can print the error message as well and then let's also add a final statement so we will execute this as soon as we complete the TR catch block so essentially whether we successfully process the payment or withraw an exception so if that's the case we complete the execution and we will change the value of the loading Boolean to false again in practice we just reset the value to the original state we need to check also if the widget is still on the widget tree because after the execution is completed we might have removed the checkout screen from the widget tree so we're going to have if mounted equal to true and if that's the case we can execute the set State now again we process the payment and we will call the next method so here we need to do the following so first of all we need to use the information that the user is added inside the card form and we will need to use them in order to create a payment method inside our stripe account and we can do that without accessing the data or the card details at all so this will all be managed by the stripe SDK so we will just use stripe. instance and we will use the create payment method function from the stripe library and to this we just need to set the params property so we will use payment method params do card and then we will pass the payment method data so we will just need to create an instance or the payment method data class and this will simply access all the information that the user has added inside the card form field and you will use them to create and store the payment method into stripe but again we will not touch those data point at all so that we don't compromise the security or the card of the user of the application now if we print the payment method you will see that we successfully can create it by using the strap SDK so I'm going to show you that now if we go back to the cart and we go to the checkout screen we can input some information and this is going to be the test credit card for stripe we can click on pay now and then we're going to print the payment method which means that we can successfully create the payment method on stripe as you can see each of these is going to be connected with a specific ID and you can check the last four digit of the card to see whether it matches the one that you have added as a test card and in this case it's perfectly matching now obviously after we create the payment method we need to also send a request to our backend server to start to process the payment so we will need to implement the logic by using our payment client because we can have the payment method ID now and we need to pass it through the post request now as usual we're going to create an instance of the payment client in the main. dart as a global variable so we're going to create it here and now we can use it everywhere inside the application so let's just go ahead let's go back to the process payment method and let's use the process payment function that we have created inside the class so so we will need to pass the items and the payment method ID and we have both of these available so we can take the payment method ID from the payment method variable in R 95 and then we can access the list of cart items from the cart which is a global variable so we just access the cart from the main do Dart we take the list of cart items and remember we need to convert this into a list of map and we can use the two jaon method from the cart item model in order to do that so we will just iterate through each of the element of the list we're going to convert them into a map and then we're going to cast everything as a list again then remember the process payment is a synchronous let's add the await keyword then we're going to await for the response coming from the payment client and we're going to save it and we're going to print it inside the terminal to see what we get now we can try this but I can already tell you that there's going to be a couple of issues that we need to fix so try and go to add some product to the card then let's go to the checkout out screen and now let's go ahead and test this now we're going to input all the credit card information using again the test card and then we're going to click on pay now now before we do that let's recap so first of all we will call the handle payment method here we will check whether the card information are completed and if that's the case we set loading equal to true and we're going to kickart the process page payment uh function then here first of all we create a payment method on stripe using directly the data from the form and remember we won't have access to those data it's all handled by the strip SDK then we will store the payment method into the variable so that we have access to the payment method ID and we need it as an input for the process payment method from the payment client now again remember we use this to send a request to trigger an invocation of the cloud function so we will send a post request we will call the method by using the payment client and we pass the pay method ID and as well the list of cart items so that we have all the cart items that the user want to purchase so that we can calculate the total cost to pay on the server then again remember that this method is asynchronous so we'll need to wait for a bit for the logic of the function to be executed and to return a response now if if we test this you are going to get an error and there are a few reasons and a few things that we need to fix so first of all there is an issue with the way we are passing the data through the post request so if we open the package again you can see inside the payment client we are adding a map as a body of the post request but we need to change that so we can't pass the map directly as it is right now but we will need to convert this into a string so we will use Jason in code and then we pass the value in this way we can convert the dart map into a string and now we can actually pass this along the post request so this is the format that it's expected then there are a couple of more issues that we need to fix and one of these is the way we are calling the different key value purs so the keys here inside the body are using the snake case so there is an underscore between each of the different words and actually we need to check what we are using inside the function so if you look at the main do PI we are expecting camel casing so you can see there's no underscores here so when we try to access that specific key value pair we won't find it because there is no key that is payment method ID without the underscores so we need to make sure that we pass the correct Keys along with the post request so again just make sure to use snake case or camel case but you need to use the same across the two different parts of the application so in this case we're just going to use camel case everywhere so let's just update and remove all the different underscores that we have here inside the payment client and finally there is one more thing that we will have to fix inside the main dopy so as of now we are using the stripe library to create a payment intent and then we again again we use all the params that we have we pass them to the create method and then we get a response so this will be a payment intent and we have a full object with all information of a payment intent again this is what it's returned here and we can't use it directly as a response because we can't pass the payment intent class as a response but we will need to take some information from the payment intent and we will need to build a response that is be a string so now we're going to use the generate response method this is a private method that we can use as a helper function inside this Cloud function that we have developed here so here what we do is the following so we pass the payment intent that we have just created and we check the value that we are storing for the status then depending on the status of the payment intent we will return a different map with different information so for example if the status is equal to require action we will pass the client secret and a few more information along that and we will return this as a response and as you can see from now on we will not return the payment intent object anymore but we will return a map with key value purse and obviously the actual response will depend on which is the status of the payment intent so if it's required action we will return the first option otherwise if the status is equal to requires payment meth me we will return the map telling that there is an error so potentially the card was denied and we need to inform the client about that so that we can display an error message to the user then if the status is equal to succeeded we can inform the user that the payment was successful so we will send the client secret and the status back from the server to the client and finally we have a fullback scenario in case any other error will happen and in that case we just pass an error message now let's take the function and let's make sure that we deploy the latest version that we just created so in the terminal we will need to use Firebase deploy Das Das only function now it will run and it will upload the latest version of the function that we have created so that he will update the function that we have on the server and finally once this is ready we can send a request in order to test whether everything work fine so now let's go back and UT to start the application and let's see what happened as soon as we send the request so as you can see we are printing the response that we receive from the payment client so let's go on the checkout screen and let's add the test credit card from stripe so let's add all the information and let's click on pay now then as you can see it loaded and finally it completed the payment so if now we take a look at the the back console you can see that we have this dictionary with two different key value purs so so the status is equal to succeeded which means that the payment intent was created and the payment was collected and then we also have the client secret and we can use it to verify that the payment has been recorded as well on stripe so if you go to the list of payments you can open the latest payment that is available here and this will be the one that you just created by testing the application then you will see the payment amount and for now this is 14 and this is because we are our coding the value inside the cloud function we are not calculating yet on the Fly once the payment is processed then you can scroll down and you can see the event data so here you will have all the information of the transaction and the payment intent object so for example if you scroll down you will be able to find one field which is going to be the client secret then you can just copy the value and you can verify that it matches the one that you have in the debac console by filtering the list of messages in the Deb console and as you can see after we filter we are still seeing the client secret that we just received from our request which means that we successfully process the payment and receive a response in the client and that's pretty much it for this part of the tutorial but keep in mind that we need to add more features to this application we will need to add more functionalities to the stripe integration and then we will need to improve the UI and add State Management and we're going to do this in the next part of this tutorial
Info
Channel: Max on Flutter
Views: 7,144
Rating: undefined out of 5
Keywords: flutter, flutter project, firebase flutter, flutter firebase, flutter stripe, flutter_stripe, stripe_flutter, Flutter app, flutter ecommerce app, ecommerce app flutter, flutter tutorial, flutter stripe app, flutter ecommerce with stripe, flutter full-stack project, flutter cloud firestore
Id: xYgIY_1ulhw
Channel Id: undefined
Length: 178min 17sec (10697 seconds)
Published: Wed Jan 17 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.