Building a BottomSheet from scratch in React Native

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what's up mobile devs today we are going to build from scratch this powerful bottom sheet animation in react native by using just the reanimated and the gesture handler package this video is part of a youtube series called what about dressers related to react native animations that heavily depends on gestures so if you like this kind of content don't forget to subscribe to the channel to be updated on my upcoming videos before moving on i sincerely want to thank yail pawel and someone a few months ago i've opened a buy me a coffee account without any expectations and out of nowhere i received support from them for my youtube content so thank you very much for your support that said we can finally move on on the code part here i've created a react native project with the expo cli and i'm using of course the typescript configuration in this project i've also included the gesture handler package and uv animated package both with the version above the 2.0 version and inside the bubble config.js i've i've added of course the rear animated plugin in order to completely reanimate the installation process so that said we can start to update a little bit this ui so let's use a dark background color and let's update this status bar with a style equal to light so uh right here uh in this project we are going to use of course the gesture detector so we are going to implement the pawn gesture and in order to enable the pawn gesture we need to wrap all the all the app with the gesture handler root view component so this component is from the react native gesture handler package so here let's specify flex one and here we go so nothing is changing but uh keep in mind that without this component the pan gesture will not work especially on android so that said we are going to place here the bottom sheet component and this component needs to be created so we are going to create this component inside this components folder so let's say bottom shell.tsx and inside this file we are going to use the react native snippets and we are going to declare our bottom sheet component so let's import it so we need to import it from components bottom shed file and here we go so it is right here so of course we need to fix a little bit the ui so for now uh we are just going to use a simple view and we are going to define here the styles dot bottom shed container so let's access inside this style the bottom sheet container and we are going to define a height equal to 500 a width equal to 100 percent and a background color equal to white so here it is so i believe that something is wrong so we we need to place this bottom sheet inside this container style so here it is so this component of course needs to have the same height of the screen so we need to access the screen height and in order to access to access it we need to use the dimensions api from react native so let's uh use dimensions and let's specify here get window so we are going to retrieve the height and we are going to name it screen height so we need to assign this screen height to the bottom sheet container height and here we go so right now of course we need to fix the position of this bottom sheet container and in order to do it let's say position equal to absolute with top equal to screen height so uh we are basically pushing down this uh bottom sheet right here in fact so if we check screen height divided by 1.5 we can see that it is right here so 1.2 and with the screen height divided by 1 the bottom sheet will be right here so for for simplicity for now let's just set the top equal to screen height divided by 1.5 and we are going to fix it in a while so uh let's also include the border radius parameter equal to 25 so here it is and the latest the actually the last ui feature that we need to implement is this simple gray line that we have usually right here so let's define here the styles dot line and let's say width equal to 75 height equal to 4 background color equal to gray so here it is we need to center it so let's say align itself equal to center and margin vertical equal to 15. so let's also include some border radius so let's say border rate is equal to 2 and here it is so finally we can start to deal with the pawn gesture in order to deal with the pawn gesture we are going to use the gesture detector component so let's uh import gesture detector from react native gesture handler and let's drop this bottom sheet container so in order to handle the gesture detector we always need to wrap an animated view so let's fix it right here and we need to um import animated from react native reanimated so we need also to assign to the gesture detector the gesture parameter so let's define here the gesture equal to gesture dot pan so here gesture is just imported from a react native gesture handler so we need to handle some callbacks for now we are just going to handle the on update callback and let's try to access the event.translation y so here something is missing no actually everything is working uh nicely so we can we can see here that we are able to visualize the event.translation y so the main goal right now is to store this translation y inside the shared value and to pass the shared value to this animated view in order to translate on the vertical axis this is a bottom sheet so let's define here the translate y shared value and let's store the even dot translation y inside this translate y value so in order to pass this translate y value to this animated view we are going to use we are going to create a reanimated bottom shed style by using the use animated style hook and we are just going to return a style that use transforms and we are going to assign to the translate y the translate y shared value so let's pass this reanimated bottom shader style right here and let's reload so here we can see that everything is working perfectly so of course if you are not familiar with this kind of concepts so shared value the use animator style and so on and so forth feel free to check out my other series called animate with your animated i will put of course the link in the video description so um that said we can we can see that everything seems to work perfectly but [Music] you probably know that by so by start to dragging a second time we can see that there is of course an issue and this issue is related to the context so basically we need to store the previous position inside an object and we need to account in this on update callback the previous translate y position otherwise the animation will always restart from the beginning so in order to do it we need to define the context object so the context object will be a shared value we are just going to handle the y-axis and we are going to store on the onstart callback inside the context dot value the previous translate y value and we are going to consider in the unupdate callback the context.value.y so the previous position so um if you're not familiar also with this context object uh i've talked about this uh this shared value this uh technique in my previous video related to the what about gestures a series i will of course put it in the video description if you are interested so if we reload we can see that everything is working perfectly so uh there is of course an issue so if you continue to start the drug behavior you can see that we are able to scroll a lot and that's not fine we need of course to clamp this uh this view and we need to set a max translate y value so first of all let's try to fix here the um let's actually try to log the translate y value and we can see that uh when we scroll uh till the top of the screen the translate y value is negative so we want to set a minimum value by using the math mean function equal to translate value and the max translate y value so let's actually see for now screen height minus screen height since it is negative and let's reload so here there is a mistake so actually let's say max and we can see that the everything seems to work fine but it isn't and that's because we are starting not from so in this position translate y is equal to zero so we are able to scroll from this position to screen height so uh in order to fix this behavior we need to we need to remove here screen height divided by 1.5 so the translate y will be right here so the translate y will be equal to zero in this position and we are going to be able to scroll until the screen height but of course we need to push we need to push up this bottom sheet and in order to do it let's just use the use effect and let's fix here translate y value equal to let's say screen height divided by 3 minus screen height divided by 3. so here let's also use with timing and here we go so at this point the translate y is uh at this point is equal to minus screen height divided by 3 and if we scroll we we are able to see that the clamping is working fine so i hope that this was clear basically in this position so in this condition the translate y value uh previously was equal to zero in this condition instead with the use effect hook it is equal to minus screen height divided by three so the uh clamping is working perfectly since we are accounting the minus screen height divided by three to the translate y value at the beginning of the animation so uh let's just fix this with timing function so you can see that it is working perfectly but i think it is much more interesting this spring animation for this context so let's also update the configuration we are going to use a damping equal to 50 just to reduce a little bit the bounce effect and here we go so um of course the clamp function is working finely so the mat max function is working perfectly but we want to reduce a little bit the max translate y value so let's say plus 50. and let's reload and we can see that everything is working so let's save this value inside a constant and this constant will be called max translate by so let's save it and let's say max translate y equal to minus screen height plus 50. so um another step that it is interesting to handle is related to this border radius so um in this animation we can see that the border radius is fixed to 25 so here we have set the border radius equal to 25 but it is actually interesting to update this border radius based on the translate y value so for instance let's say that we want to set in this condition so when the translate y reaches the max translate y value we want to set the border radius equal to 5 with a smooth animation so in order to achieve this behavior we can just use the interpolate function from reanimated so let's define here border radius equal to interpolate we are going to use interpolate from reanimated and we are going to pass the translate y value so here we need to set the input range the output range so um i will just write the input range and the output range and i will explain you uh why it is working so let's set here border rate is equal to 25 and 5 and here let's set max translate y plus 50 and max translate y let's assign the border radius to the reanimate style and let's set extrapolate dot clamp so let's reload and we can see that everything is working nicely but why it is working so basically what we are seeing with this interpolate function we are seeing that when the translate y value reaches the value a max translate y plus 50 the border radius needs to be equal to 25 otherwise when the border when the translate y reaches the value max translate y so this one the border radius needs to be equal to five so if the translate y value is between is a value between these two values the border radius should be nicely interpolated between 25 and 5. so the latest thing is of course this extrapolate clamp and basically with this extrapolate clamp we are seeing that if the translate y value is less than max translate y plus 50 the border radius needs to be equal to 25 otherwise it will be automatically interpolated to a value that is greater than 25 so for instance if i comment this extrapolate clamp you can see what is the behavior and it isn't fine at all so i hope that this interpolate explanation was clear but i've done other tutorials with this interpolate function so feel free to check out my previous tutorial related to this topic so that said we need to handle in the end the on end callback so dnn in the unend callback we need to check what is the current position of this bottom shed so for instance if the current position is a is a less than let's say screen height divided by 3. we want to we want to animate back the bottom sheet to value zero otherwise if the bottom shed is uh positioned here for instance so is a greater than screen the position the translate y position is greater than screen height divided by two we want to animate the bottom shed to the max translate y so uh let's see what i'm talking about so here we need to handle the on end callback and let's check the translate y value so if the translate y value is a greater than minus screen height divided by three we want to animate the translate y value to value zero so of course with the spring animation so let's say with spring 0 so let's reload and we can see that it is working perfectly otherwise if translate y value is greater than screen height divided by 2 actually if is less than screen height divided by 2 we want to animate till value max translate y so let's see so here we are between um screen height divided by 3 and screen height divided by 2 and we don't want to do nothing and here we are above a string height divided by 2. and we can see that the animation is working perfectly so for instance i think that is quite better to use one divided by five so here it is it isn't animating and at this point it will animate so um as you can see we are using this with spring function with this kind of configuration a lot of time so um of course we can refactor a little bit this code by creating the scroll to function and by using the use callback hook from react so here we are going to copy this code we are going to set a destination let's say destination of type number and let's assign here destination with spring so we are going to replace here scroll to value zero here scroll to value max translate y and uh here let's say scroll to scroll to minus screen height divided by three so uh we can see that everything is not working i hope so everything is not working and that's because uh we are missing the workload keyword inside the scroll to uh function so we can see that we are trying to call the uh to call synchronously a function that is a javascript function so we can solve this problem with this issue with the two different solutions the first solution is to mark the function as a worklet and the second solution is to execute the scroll to function with the run on js emitted from reanimated so in our case it is much more easy to use the workload keyword right here so let's see what's going on and we can see that everything is working perfectly so um i believe that at this point the missing step is to implement the the button that is on the screen and probably this is the most interesting part of this tutorial so first of all let's try to define here a button let's just use a detachable opacity so we are going to use touchable opacity from react native and let's implement here actually this style with styles equal to style start button so we are going to set and height equal to 60 aspect ratio equal to 1 background color equal to white so here it is actually let's say height equal to fi 50 and the border rate is equal to 25 with an opacity equal to 0.6 so here it is so here let's create the unpress a cool backup so let's use use callback from react and let's assign this on-press callback to the touchable opacity so the main goal right now is to is to access the scroll tool method from the bottom shelter from the unpressed codeback so the interesting part is that we are going to create a reference we are going to bind this reference to this bottom sheet and we are going to bind the scroll to method to the bottom sheet reference so first step is to define here the reference with this use ref hook so let's use let's import user f from react and we are going to bind the ref function the ref object to the bottom sheet so ref here it is so the purpose would be to call ref dot current scroll to and let's say minus 100 so in order to implement this behavior we need to update and to refactor a little bit the bottom sheet basically in order to use the reference outside we need to implement the bottom sheet as a react forward ref component so here let's drop all the component with react forward turf and the react forward ref component will have here the properties and here the reference so just to be precise we are going to define the bottom shed props and we are going to define the bottom sheet ref props so the ref will have the scroll to meet so let's say here a destination of type number and the return type void and we are going to specify here the bottom sheet ref props and the bottom sheet props so this is of course just for typescript so here we are just seeing that the reference has a property as a function that is called a scroll two the final step is just to bind this uh scroll to method that we have defined right here to this reference and in order to do we to do it we are going to use in my opinion the must underrate the hook from react that is called use imperative handle so let's import it and this is a hook takes as the initial parameter the reference and the second parameter is just a function that returns the bottom sheet reference properties so here we are just going to return the scroll to method inside an object so the third argument is just the dependencies and we need to pass this code to uh method so in my case uh i'm using expo and i haven't the eslin configuration activa so probably uh in your case you will see here a warning and you need to pass this code to method so here let's export the bottom sheet ref properties and we can declare here the reference with the user f of type bottom sheet ref props and we can see that we haven't the warning anymore so let's reload let's tap here and we can see that the onpress function is working perfectly so we can get rid finally of this use effect and we can say for instance here -200 so here it is so at this point we are almost done the final step is just to handle the toggle behavior so for instance what we want to do we want to handle if the bottom shed is active and if it is active we want to toggle it back to we want to scroll back the bottom shed to value to value zero otherwise if it isn't active we want to scroll uh we want to scroll the bottom sheet to value 200 so here it would be nice to access the is active parameter by calling ref.current is active and of course here we need to implement this is active function so as before we need to add here the is active function to the bottom sheet ref properties and let's just return a boolean so uh we need to define the is active function with the use callback and for now we are just going to return to let's pass here is active and let's pass it right here so how can we know if the component is active first of all we can create a shared value with the initial value equal to false and that we can we know that all the bottom sheet transition will pass from this scroll to uh method therefore here we can check the destination parameter and we know that if the destination parameter is equal to zero uh active should be false otherwise active should be true so basically if the destination parameter is set to to zero we are um just toggling back the the bottom shed to value zero to uh to the bottom of the screen otherwise uh the bottom sheet will be uh on every other position of the screen and it will be of course active so we can refactor a little bit this condition so we can just write active value equal to destination different from zero and here we go and inside this is active function we can just return active dot value so uh here finally we can access if it is active or if it isn't and if it is active we want to scroll back to value zero otherwise we want to keep the scroll to -200 so let's reload and we can see that this animation is working perfectly so i think that right now we are almost done an interesting part is that of course inside this bottom sheet component you would like to pass any kind of item so for instance here let's say that you want to pass a view with the style equal to flex 1 and let's say background color equal to orange so in order to implement this functionality here we need to pass we need to define the bottom sheet props we need to specify here the children property of type react react node so let's access the children property right here since here we have the bottom sheet props and let's call let's retrieve let's pass here the children so after the gray line and i believe that everything is working perfectly so um the animation is completed let me know uh in the comments if you liked this this video let me know if you have any kind of other ideas for my future videos and of course leave a like and subscribe to the channel if you like this kind of content so thanks a lot really thanks a lot for staying with me all this time
Info
Channel: Reactiive
Views: 88,212
Rating: undefined out of 5
Keywords: react native, reanimated, animations, react native animations, app development, gestures
Id: KvRqsRwpwhY
Channel Id: undefined
Length: 31min 18sec (1878 seconds)
Published: Fri Feb 18 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.