#119 - March Meetup

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everyone good morning thanks for joining us on saturday morning [Music] so today uh for our talk we have pratik with us he's gonna walk us through the compost uh like if you have an existing app and if you want to convert it to compost then how to go about it so he'll walk us through that uh and uh so compose is like currently trending in android developer community like everybody's talking about compose so if you're not check it out like it's an it's in declarative way of writing ui it's gonna completely change how we write android app uis and uh it's still in beta uh so it's a good time to check out and play around with it so you know you're like is aware of the philosophy uh it's it's very different like uh the way we write xml and we are gonna write compose so now we'll also drop a link to android's uh compose pathway course in the description below so you can check that out and uh that will help you getting in the hang of like how to write ui and how to maintain states of the ui components and everything and today any hypothetic is also gonna walk us through how to convert your existing apps ui and if you have any questions during the talk just drop it in the comment box below we can highlight those during end of the session and pratik can help you uh understand your daughter like help you clarify your doubts and understand the concepts so without any further a view let me add it to the stream say hello good morning hey everyone can you hear me yeah all good okay cool um let me just turn on the volume is this better yeah it's good cool um thanks for joining me uh for this session today uh before we get into the live coating i just have like a couple of slides to get through so let's get started with that um so what i'll do is i will if you can add my screen here i'll just play the slides cool um so this is going to be a hands-on session on refactoring an existing app to jetpack compose quick introduction about me i'm pratik i'm a part of the mobile engineering team at buffer you can find me on twitter at hackatronics um slight disclaimer so the amount of compose i'll cover is probably scratching the surface it's just like 20 of the total api surface but at the same time it should cover 80 percent of the most common use cases that you might come across when building an app so it should be enough to get you up to speed with the apis um there are some ideal and like software requirements uh you should have like a working familiarity with kotlin uh you should have worked with mvvm architecture but any sort of reactive architecture like uh mvi should also be fine uh some familiarity with architecture components mainly the view model and live data would come in handy and then basic com basic familiarity with some composables would also be um an added benefit but uh it's it's not a problem if you don't uh don't know compose i'll try to make it as uh as introductory as possible cool so what is jetpack compose so this is like copy wasted from their website so it's a modern ui toolkit for build for building uh native uis it simplifies and accelerates ui development and you end up writing way less code and work with intuitive kotlin apis but like in in simple terms for things like building a list you no longer need like 100 lines to do it you can do it in a much more idiomatic and expressive way like this so before we get into the live coding let's just jump through a couple of you know quick primers around compose first ui state so if you look at an xml based ui this is what our typical structure looks like so we've got like um our view model which is typically the state owner and then the activity or fragment observes the state and then based on any change in the state uh we manually call setter functions uh for updating our xml based widgets um now this this is a lot of manual work and this dance to basically synchronize our xml ui with our state is quite error-prone as well um declarative ui takes a different approach to this all together and it's much more simple so any time our state changes um our composable will basically re-emit itself and it directly observes that state and compose internally will differ tree to make sure that it's only invoking the reinvoking decomposables that have changed so it basically skips anything that has not changed since the last dimension um second part modifiers so modifiers basically determine the behavior layout and the visual appeal of your composer but there's a quick gotcha there uh you have to be aware about the order in which you apply the modifiers because because it ends up uh impacting the way its final look uh so if we take a look at building this graphic out which is like a square of side 200 dp and then we've got another hollow square inside at a margin of 24 dp and then we have like a red circle the the like logical sequence in which you would think about building this would be something as follows right so you create a you know yellow square you give it a modifier of size 200 dp and a background of yellow then you will add some padding for the internal square and then do a border stroke there for two dips uh with the color white and then finally you would add another padding for the circle and and give it a background red and clip the circle see but if if you if you run this the result will not be what you would have intended you'll get a square here instead of a circle and and the reason is because the ordering in this case matters and and the way in which modifiers basically work is um so top to bottom uh a modifier basically applies an effect to a composable and then it needs to be consumed by another modifier uh in order for that effect to be visible so in this case if you look at the background um the color red is applied which is consumed by the clip modifier but when the clip modifier actually clips the circle nothing is actually consuming it which is why you get the circle sorry the square instead of a circle so a quick fix for that will be just flipping around the order so you just clip the circle and then the clip circle will be consumed by the background modifier which will be rendered accordingly that's for the containers so compose ships with most of the common uh ui components that you might need when building uis like text button cards and all of these components come out of the box with material theming and styling support but the way in which we lay out these components is slightly different so in xml uh we lay things out on the z-axis using frame layout for anything that we need to do on the x or y axis we typically use a linear layout and then for laying things out relatively we use a constraint layout on compose things change slightly um so for for stacking things on top of each other we use a box for a horizontal alignment we use a row and for a vertical alignment we use uh columns there's there's a few nuances to how these containers work and i'll get to that in in the live code right so having said that we can now get started building the app um you'll be able to find the project at this link later uh after the after the live stream so let's get started so if you can see my screen and tell me if the font is good i'll go ahead a bridge is the phone good yeah you can increase a bit okay maybe a couple of steps how about now yeah this works cool so i just um pause the video and and just set up the phone to share the shelby um app with you so cool so let's let's get started with the with the app i'll first give you like a quick walkthrough so this is the app in its current form it's it's basically two screens um there's a popular movie section and there's a favorite section and you can go inside of a office of a movie mark it as favorite and it'll show up in the favorite sections you can remove it from there now in its current form it's not particularly pretty but maybe we can take a look at you know um cleaning this up and and making a little nicer once we move this over to compose so let's take a look at the project at the at this point so we've got like two fragments here um yeah so this is the popular movies fragment and the favorite fragment is also the same it's just that the data source is different so it's just using the standard setup we've got view binding we've got view models and we're essentially observing um the uh the live data from the view model to render out the list right and we've got navigation components to basically uh navigate from the list screen to the details so if you if you look at one of the let's say view models so popular right um what it does is when it's initialized it fetches things from the repository and the repository structure is also fairly straightforward so it fetches the movies from the api caches it and then presents it from the cache now i'm not doing anything fancy here in terms of error handling or testing as such just to keep the scope of this project um small so let's get started building this uh ui so i'll just close all of these files and the one of the ideas behind uh compose is that you build your layouts from bottom up so that means you build out the most the smallest component first and then use those components to compose your screen see where the naming comes from right so what we'll do is we'll first try to build this um list item here right so i've got a package here and i'll create a new file let's call this movie card and this is going to be a composable movie card that takes in a movie for now we'll also generate a preview for this so preview composable fun movie card video and inside of the preview we will just invoke this movie card component and i've got a fake data factory that just hardcodes some values and builds a movie so we'll use this for the preview here um cool so now if you if you look at this um list item um it's basically built out of um three components so on the lower most side we have the image and then we have this uh gradient scrim uh this blackish gradient screen that will just make the text a little visible and then on top of the text we have these um three uh widgets which is like a text view sorry two text views and then a rating component so so let's try to figure out how to build this first right so first up we want to have some sort of a container right so in order to as i had shown in the slides in order to build a vertical stack we use a box right so a box will accept a modifier so we want this boxes width to be the screen bit so we'll give it a modifier of fill map switch okay now we want to constraint its height so we'll give it a height of let's say 200 db and and the dp is basically an uh a property that's already defined on float as an extension so you don't need to worry about converting things um and then inside of this box we'll have let's let's write it down so we'll have the background and then we'll have the scrim and then we'll have the content which is basically the you know title the info and the rating let's start off with the background so the movie comes with a url for loading the image but like if you look at the image component that comes with compose it doesn't natively support loading images from from a url so what i've done is i've added the accompanist library by chris payne's uh that gives us access to the coil image so coil image is basically able to take a url so i give it a backdrop url now i want to define a couple of properties for um for this right so first let's remove this and what we want this um this image to do is first of all it needs to be uh let's define like a content scale so that fills the entire available space so that'll be a content scale drop and then we'll use uh i think it's not visible properly let me just reduce the size here yeah and then what we'll do is we'll use the um request builder property within a coil to essentially enable a cross fade here so what this will do is as as and when the image loads it will cross fade in now the reason why this is not working is because it image needs a content description so we'll simply use the movie title itself as the content description for this image for accessibility purposes and uh and let's also give this a modifier of fill max size so this will basically take up the entire width and height of its parent now so that it uh it's visible you know in the available uh space allotted to it here cool so we've done the background now let's take care of the scrim so if i show you the recyclerview item you can see that the the scrim is essentially like a drawable that that has a gradient via an xml drawable here but um again um right at this point in time um xml drawables are not supported apart from like vector assets as such so what we'll do is we'll simply create a column here which is like a vertical linear layout and we use this to basically define the the scrim for this um for this card so this will take a modifier of fill max size because if we wanted to you know match the parent on both x and y we will then give this uh background and the background here would be a vertical gradient now if you look at what a vertical gradient takes it takes like a list of colors and it takes a start coordinate and an end coordinate so basically you want the scrim to go from here to somewhere in the middle roughly in the ballpark of the middle of this view so let's first give it the colors so list off so will be a color dot transparent and then color dot black because if you look at it here it basically is darker on the bottom and then as it goes up it gets lighter so that and then we have the start start coordinate so start y you will start at zero and then end it roughly at let's say 500 f right um the preview is a little finicky so it sometimes works sometimes that doesn't what i have found it to like what i found reliable is at times just closing the file and reopening it should render this movie card right yeah anyways we'll deploy it later to check it out if this works or not cool um next up we want the we want the content of this uh of this particular card so the content is again a column which has two text components and then a rating bar so let's first define a column um and this column will basically be um a fill matched with because we wanted to expand the entire width of the parent so fill max width and then if you look at it here it is at an offset from the top and the left side so we give this a padding and padding has a couple of variations so you can either define like a horizontal vertical padding or a padding on all directions or specifically apply padding on either of the four sides so now it started showing up so let's just reduce the size a bit right cool um so we want the padding top to be around 32 db 348 yeah and then on the start around 16 db cool um and now we get started with the content here so first we'll add a text here so text composable basically accepts our text so the text will be the movie title so movie dot title and then what we want to do is if you look at this screen right there are some movies that are like exceeding the uh the width of the screen so we want to somehow wrap it so first of all we let's give let's give this a max line of one and then we wanted to wrap so so we want to give it like an overflow modifier or overflow property of ellipse size so any time it goes beyond the uh recommended width it will ellipsise the end we also want to align this to the start so text line is going to be x line dot start and then for now let's just hard code and give it a color of white so things cool and this is going to be a bold so if you look at the cards here they are a little bold so let's give it a font weight of on trade dot bold and then a font size of um roughly 24 24 sp and this is also an extension so let's import this cool so let's try to build this and see if this renders out meanwhile i'll go ahead and just duplicate this text composable here for the movie card sorry movie info and we'll change a couple of properties for it so this will be movie info this does not need a max lines we also don't need an overflow or a board and this can be like 18 sp so let's build and refresh this again i'll just close this and we can maybe deploy it later on so after these two we have like a rating card component and so compose basically doesn't come natively with a rating card component so this is like a good enough opportunity to [Music] um to show how we can build a a custom composable here cool so what we can yeah okay cool so this showed up so what we'll do is we'll build a rating card component so and we'll pass in the movie dot rating to it right let's create a new composable and what we'll do is we'll also generate a preview for this so preview at composable one and let's give it like a rating of 3.5 f and start also so the first part of this um of this rating component is this text which actually shows the rating so and and if and then if you notice um the rating bar in in like in its entirety this is this is like a horizontal linear layout of a text and then a star and then a star and so on right so what we'll do is we need to horizon horizontally lay things out so for that we'll use a row and now this row will need to be um at a padding of like eight from the top we'll also add the paddings to the text because right now it's hugging each other a lot so let's give it a padding of top 8 and then here we have a text component we can basically copy this text component from here and change a couple of the properties so let's give it a rating dot full string does not need the max line overflow property color will still be white font will still be bold but the size will be around 32 db let's try to build this now so once the rating text is done we need to handle how the stars will look like so we basically have to uh handle the logic of rendering the stars in their form so if you look at this movie right so it has a rating of 3.6 right um and so what that basically means is the first three stars are full and then since the next uh rating is uh in the range of 0.5 to 0.9 that's the reason why it is half filled and then the last one is completely unfilled so let's let's build that out now again this the stars are also like a row so i'll go ahead and build those as a row so i'll create a row and so first we need like a range for this to loop over so this is a five-star range so i'll create a range here so val range is equal to one through five right now i'll loop through that range so it'll be a range dot um and so for this value what we want to produce is a rating item so this will take the value and the rating and based on where the value falls within that rating it will generate the star in like either filled or half filled or unfilled so let's create that right so to for us to be able to determine what this middle star um will be whether or not it will be fully filled or half filled we need to get a hold of the fraction and the whole number from this rating and then determine if the fraction falls within that range so what we can do is we can split the the rating into a fraction and a whole so let's call it whole fraction which is equal to rating dot split to whole interaction now this is like an extent this is like an extension function that i had built to just split out the rating and which returns a pair which is what i'm receiving here so now what we want to do is we will be emitting an icon here and an icon takes an image vector a content description and and also a tint which will determine the color of the um icon so for the icon we will need first the image vector so let's assign it an image so uh image factor is equal to so if um if our value is so value.2 float yeah if our value is equal to whole plus one which is which means like in a case of 3.6 if our value is the fourth star here which is the four um in this range um the whole plus one and if the fraction is in the range of 0.5 to 0.9 then in that case what we want to do is emit an icon dot star uh no i can go default i can start default dot star half right because why isn't this importing okay we need an else plus uh or else we need the icons dot default dot star right so let me just quickly explain what i did here so let's import this yeah so if in the um if in the fourth uh value if the rating falls between 0.5 and 0.9 then that would mean that our star needs to be half else it will be this fulfilled fully filled which is like you know the three stars before it or the next star that comes after similarly for the color we have like a val color and this will also be dependent on the values here so if our value is less than or equal to the whole right so the whole number is basically in this case 3.6 so the whole number is 3.0 so anything between one two three will be fully filled um or if our value dot to float basically the condition above we can basically copy this out right if our value falls in here right so if this is the case then we want the star to be yellow in color so we will do a color dot yellow else it will be a gray color color dot okay cool so let's assign these now so image vector will be image vector content description we can keep it null for now uh but don't do this this is bad for accessibility i'm just doing it because of the time back right here and then for retain we can pass it back right cool now let's see how this looks or if this works or not meanwhile what we can do is go back to the text and give it some padding so give this a modifier okay studio just so modifier equal to modifier dot padding top is equal to a dot since this is not working in the preview what i'll do is i'll just directly deploy this so movie card component let's run this okay studios seems to be taking a toll here let's see if we can fit a few things um quick time hypnotic um i guess it's studios [Music] oh let me see if my internet is crapping out no we are on the 5g anyway let me quickly launch it first and then see what's going on what's taking so much memory okay let's try launching studio again me actually stop the screen share once and try it because my screen is lagging a lot okay cool now give me a minute if this is breeding right now i'm just showing you a lot yeah i'll just i'll just check share the screen once this is loaded if there are any questions till now i can probably answer them right now while this is everything again yeah if anybody has any questions just uh put it up like a military thing yeah sorry about the technical difficulties seems to be some microphone issue because like there's a lot of disturbance you're speaking is this better i don't think i have anything running for the mic do i no i can switch back to my airpods if you want like this is an external mic still if this is still choppy just let me know and i'll switch to the airpods mic in that case yeah just try it out once because yeah let me see so can mike how is this is this better um wow this is is this is it possible for us to reduce the stream quality yeah let me uh listen yeah if you can reduce it to maybe 720 that's why it's lagging so much um yeah i'm just checking meanwhile your studio is starting with a few questions so maybe we can start with those right now it's working on the arctic fox canary probably that's the reason why it's so unstable but yeah it's it does like the tooling support isn't there in the regular studio right now yeah in the first modified example you showed the ordering of modifier isn't that error you could possibly be difficult to deposit good question um so it isn't error prone it takes a while to get used to it uh once you understand the ordering in which these modifiers are applied there's only like a certain combination that you you can use to basically build a ui in terms of like getting errors as such it will probably be because of you know in the incorrect ordering or um or the incorrect sizing as such right um there shouldn't be an issue like once you get a hang of uh how the modified like in a chain of modifiers how the application process works right a good way to basically learn about this and get get familiar because it takes a while to actually for it to click for you you can like try building like a box and you know within that box try having like two more boxes and a circle and then try to align it and apply a few modifiers and see what you were expecting if that is the thing that you're getting as the result or not and if it does not then mostly it's the ordering issue it's almost like rx where you know you have to think about the operators and the order in which you apply them like the when you are starting out the most obvious way which you would probably try to apply might not be the correct way to do so right so it gets it just it takes a while to understand the application ordering and then it's it's it's just fine so studio has loaded so let me see if this is performant enough one sec on cool i'm going to try and share the scene again okay because this side is fine now it's much better okay let me try again how is this good let me try sharing the screen again okay cool um sorry for the pickup yeah i'll just try to launch the phone um cool so this was where we last left so we had the what did i do oh crap one sec uh i could straight up okay cool i'll minimize this and not close it cool so this is where we last left right and so now it pretty much looks like what we had earlier but there's some margin and padding issues first of all the value needs to be aligned to the stars so what we can do here is go into the rating item sorry rating bar and so row has this property called vertical alignment wow which is still hanging a lot um cool so this will take an align dot center vertical vertically and we'll just simply deploy this and test it because autocomplete is now not working no not this one okay it's an alignment so if we try building and running this wow this is being super slow um okay anyways while this builds um we can plan out what we want to do about the list ui uh cool this is decent enough right so this looks fine for now uh now what we want to do is maybe we can like add a little bit of padding for the star items as well so let's give it a modifier dot padding and this will take a start padding of 8 bb okay cool let's screen this up and uh deploy this once again and now if you look at our popular movies fragment which is what the list was right um this is basically using a recycler view right now so one of the good things about composers if you decide on adopting compose you don't have to go all in so it's not like you have to migrate your entire app you can do it incrementally and it also interrupts very well with the existing view system so what we'll do here is basically within this fragment host like a list of composables right so first of all to do that what we'll need is we need um like a like a recycler view of sorts and a recycler view equivalent of compose it's called lazy column and lay zero depending on which direction you want that in so let's create a new package here let's call this screen and this will be popular movies now this is again going to be a composable which takes um so it will be a popular movie it's composable and what we want to do for the recycler view is we want it to directly observe the list that's coming in from the view model so we'll pass in the view model which is the popular view model here right and now what we can do is we can create a list and this is the view models movie and then there's a special operator within the live data which is called observe estate so couple of things to unpack here right um first up um so observe as state basically returns like a state wrapped object or a list to you you can of course use um an equal to here but then what that will mean is you can't directly use the movies object because now this is a state of list right so you'll have to do a value so what a by delegation lets us do is it automatically unwraps the movie so we don't have to do a get value there so we can directly consume the movie okay now what we want to do is we want to render out a recycler so let's create a lazy column [Music] okay and lady lazy column has this property called items where we can supply our list right now this list as you can see is nullable because when the um property is initialized initially the movie might not have loaded so by default it will be available so to be safe in this case what we can do is supply a default value of an empty list so that this doesn't crash okay um cool now why is this complaining and this should work one sec remodel dot movie dot observe as state yeah this is the one initial value will be an empty list of movie yeah and then for each of these movies right because this is essentially a for loop here we want to pass this and generate a movie card so we'll pass the movie here required in found any one second uh why is this complaining so this is a lazy column let's just run this probably this is the ide that's bonkers yeah let's let's try to run this so for this what we want to do is we want to host this inside of the fragment right so for for doing so there are a couple of ways in which you can take care of that one would be you can essentially add a composed view here itself right and then do a you know binding dot get compose view and inflate the composable there but if your screen entire screen itself is a composable then instead of doing that you can just directly return a compose view from here so to do that we'll just get rid of the binding we'll get rid of the adapter and then we'll get rid of all of this code here for the observation and instead what we'll do is we'll return a compose view which takes context and then from this we invoke the set content method and our set content basically takes a composable and they it it renders it out to the screen [Music] cool so here we will do a popular movies and we'll pass in the view model oh okay i got it yeah we imported the wrong view model here so this should be a popular view movie model and so this will be movies and now we can pass it an empty list cool um cool this should work now let's run this instead of the preview let's deploy the app and if everything goes well you'll see the movie list um on the screen let's give it a while british if you have questions coming you can keep them coming to me because breeding is taking a while so it's no waiting you can get those answered sure okay cool um so we have like the movie list showing up and the way in which you can tell that this is a composable is uh if you look at this half star drawable it is different from the favorite screen right which is coming natively now there's a there's a couple of bugs here right one if you look at lights like the last item it's not fully visible and the second is if you click on any of the items it's not opening the detail screen so let's fix the card first of all um so right now if you look at like the box here right or let's say split this right uh this is not at the desired offset like the the correct one the uh xml version is much lower than what we have over here so to fix that so box has this property called content alignment let me give it a comma first content alignment and this will be an alignment dot center start so if i give this a center start what will what it will essentially do is align the item from the center of the uh available like center of the space and then produce decomposables from there on so let's try to run this again uh and in the meantime we can also fix the last item getting cut off from from here cool so this looks much better now so in the lazy column it takes a property called a content padding and like if you work with recyclerviews you know this is like a pain to do manually so in this case what we'll define is we want this padding to be applied only to the bottom so that this last list item can be correctly visible so we'll just give it like a bottom padding of let's say um 64 db just a demonstration purposes uh this will be a content padding not content cool let's run this and that part should also be fixed now cool uh yeah this is totally visible now cool now what we'll do is we'll um plug in this screen to the detail screen by navigating to it so i'll show you how it currently works so in the what's that favorites movie fragment yeah so what happens is um we have the adapter and we've got a listener on the adapter which basically passes back a movie that was clicked on and then we use the nav controller to with uh nav directions to navigate to the details so this entire setup we can essentially reuse so for doing that what we'll do is again open the movie card and first add this lambda there so this will be a lambda that takes a movie and returns unit right um and now we want this entire box itself to be clickable of course this will show up now um so what we can do is we can apply a modifier called clickable on this and whenever this movie is clicked we basically want to invoke this lambda that key of path with the movie object okay now we will also have to pass this down so this will be another lambda here movie unit and pass this back down to the card component and then where we are creating the uh popular movie screen we can essentially handle the navigation here so this is a movie and so here we can basically do what we did before which is find nav controller dot dot navigate and we have a directions argument here so it's a popular movies fragment direction right dot action popular movies fragment two movies detail fragments so if you look at this this is already defined in my nav graph so that takes a that stake that takes an int id and goes to the movie detail screen so we'll pass in the movie dot id and now if we run this the this screen will essentially redirect us into the into the detail screen uh oh yeah so of course our preview will break now because uh we'll just pass this in empty lambda cool let's try clicking on this cool this works so now what we want to do is we will start building out this detail screen and then later on try to prettify it so before we get into that bridges do we have any questions that i can answer up till now yeah we have to do we need to maintain all resolution images in drawable folders like in the old way uh good question i haven't actually tried using static assets yet but i i would guess so because the way in which we handle resources has not changed um so for loading strings we have the string resource and i think there's an equivalent image resource so you'll have to specify that drawable's name and then the system will take care of picking up the right one um so i guess yes but yeah for most uh static assets like icons and stuff you can use an svg and that should take care of the scaling but for yeah the static images that you ship with your app you'll probably have to still do it the old way i'm not entirely sure but probably you still have to do it the old way any image vector you use in the code is it available in drawable oh yes yeah so the image component that i'm using here right uh sorry the uh where's the movie card uh rating item here right so this is an icon composable that comes from the material icon class right and this image vector is essentially coming this is basically using like the material icons and this also comes from the material icons uh compose library so this is just a dependency that i have on the material compose components and i'm basically getting that out of the box so essentially it does ship with it these are all those default icons that yes so there's like the default there's uh there's the sharp there's dual tone i don't remember the naming but essentially every material icon is available uh as a named qualifier once you add the gradle dependency good any other questions yeah we have a few i'll just quickly does com support kotlin multi platform no it doesn't uh there is jetbrains has taken up the uh the the composed desktop project so you can essentially use the same knowledge that you gain while learning compose on android and build desktop applications i remember there was a kotlin talking kotlin episode where they had talked about hopeful future plans of bringing jetbrains composed to kotlin native but that's nowhere in the works right now so no this is android exclusive in at the time cool i'll take the other questions after building the screen out okay cool so now for this screen um again if you look at uh look at it from from top to bottom in a layered approach we have this blurred backdrop sort of a thing going on in the back background then we have like a card i think it'll be better if i show this as a light theme yeah then we have this card which has all of this content and then on the top we have this poster right so let's let's get started building this now since this particular screen does not have any like components to break it out down to we'll just build it out as a whole screen entirely so let's create a kotlin class called movie details now this will be a composable let me close this up composable fun moving details um and this will take in a movie for now let's generate a preview for this which will again not work but one details preview and let's call the movie details and pass in the data factory dot make movie cool um let's let's think about what we have to do here right so first of all we need a box right because again things are stacked on top of each other so box and this box will be match parent match parent on both sides so modifier dot fill max size okay and we also want the content alignment to be from the top center yeah so content alignment top center basically means that it will start producing items somewhere roughly from here because if you look at this things are fairly centered here for now right cool now there are three things we want to build here one is the backdrop the other is the content and then the third is that poster on top which is this image okay so let's let's get to it one by one so if you look at the the content it's simply a an image so we'll just use like a coil image here and this will be movie dot backdrop url and then let's give this a modifier modifier dot dot fill max width we'll give it a height of around 300 dp let's import the dp cool um we also want a couple of more additional properties for this so we want this image to basically fade in so coil has this property called fade in we'll put a true there and then we also want to add a content scale which will be a content scale of crop we don't want this to be filled entirely and let's add a content description first so that it stops complaining content description will be i'll give it a movie name itself okay and now to blur the image uh oil has these transformations that we can use so that go inside of the request builder lambda so first we'll crossfade this actually since we're fading this crossfade is not required so let's just do a transformation here so transformation has this blur transformation and what the blur transformation takes is a context and a blur radius now we don't have like in compose we don't have a function called get context right but each compose has like a local uh composition context that you can use to you know fetch your resources and do things like these so that comes from your local context dot current but this will not work because this needs to be invoked from a composable scope which this is not so what we'll do is we like create a context that'll be a local context.current pass in the context here and then give it a radius of like 25 um so i think that's a radius right 25 f cool let's see if this shows up it probably won't show up because um network images are not rendered um cool so that takes care of the image part right now what we want to do is we want to have the card here right now if you look at this card the white card that we have it's at like a distance from the top right um and the analogous component for a card in compose is a surface right so for the surface what we want to do is first of all you want to offset it from the top so let's give it a top padding of 24 db um actually it will be way more than 20 40 it should be like 250 or something yeah 250 dp and we then wanted to fill the max height now let's give this an elevation because cards need to be at a distance like an offset from the bottom so let's give it like an elevation of 8bp and then let's trim out the corners to be rounded so for doing that we need to give it a shape of a rounded corner and then here we can specify the rounded like corner radius for each of the four corners so start will be 24.bp and end will be top end will be also 24.33 let's try to um deploy the preview and see if things are okay so far [Music] um cool so we do get the blurry image but we are not getting the card oh yeah so for that i guess we need like a content first so let's give it a column um where we put in the content which will be uh modifier dot fill max width because i wanted to expand the entire width of the parent and this will also have like a default height right so we want to give it a minimum height of let's say 650 db and the reason why i'm giving it a minimum height is because the the summary that you saw earlier for the movies that showed up here they are of different lengths so we don't want the card to shrink or extend based off of it so a minimum height will help um offset that um we also want to give this like a top um padding so padding on top because it we have like a poster here so we want the content to be unblocked so let's give it like a 120 dp padding on top cool um and for the for the title we can simply like copy this text we have and change the property so we have a text movie title max lines one color will color we can remove because that will take from the surface modifier itself cool now we can run this and see if it works more defier typo cool so we got the card we got the text which is misaligned right now we can we can quickly fix that up right so what we can do for that is let's give it a um vertical arrangement so we want what what we basically want to do is so if this is the column we want to start producing item from the top so let's give it an arrangement dot top and then align everything horizontally so horizontal alignment would be alignment dot centered horizontally now if we run this it should work meanwhile we'll also add the rating bar here that we had already uh created so passing the movie dot rating i think this would work now hopefully [Music] cool it does look like it is loading it this time okay cool so the title shows up and the rating shows up but the text is white now and the reason why the text is white is because okay cool this showed up so uh yeah so the reason why the text is white is because we've actually specified the text color itself to be white but it is a little tricky because um if you look at the list right let's deploy the actual app um this cannot be correctly themed right now because in the regular list this has to be white because it's above the images but inside of here it needs to be basically theme dependent and be black so what we'll do is let's pass this uh color and the default value would be a color dot white so this will now use the past color and then from the detail screen we can pass it um a color of material colors sorry material theme dot colors dot on surface so on surface will basically determine if the theme is light or dark and assign it um the appropriate color here cool now what we want to do is below the rating bar we get this again a text so we'll just copy copy this paste it below here remove the max lines overflow property this will be a text align dot center and font will be non bold this will be around 18 sp and then this will get uh so horizontal padding would be 16 dp and then the vertical padding will be let's say 32 dp so vertical would be 32 db cool let's build and refresh this again this will again not work um we can just use this itself for now for reliably getting the previews and just close this off cool so now that we have the content here what did we do oh so i should pass the overview right um so now that we have the content on the card the other two things that are required are the button and then the poster that we have seen on top um on top of it so let's add those two right so this looks fine um first up we'll add a button now i've like already created like a basic skeleton of what a button should look like here which is called add to favorite button and it takes a movie so i'll show you what this is right now but if i run this in the moment you'll see that it'll be broken it will not look uh look proper or fully fleshed out and the reason for that is um so first of all i've like used a column here to center it out um at like an offset of 32 from the top so you can see like this like an unfilled button and the reason why this is unfilled is because i have not supplied in the properties because these properties essentially depend on the movie's state right so let's flesh them out one by one so first of all if the if the text so for the text we basically want to determine if the movie is favored or not favorite or not so if the movie is favorite then we want to return a string resource called remove from favorite so that's r dot string dot uh remove from favorites i'll just remove this because it's taking on space and else what we want to do is load the string resource called r dot thing dot add to facebook cool let's format this same thing with the icon so the icon will also take in an icon composable and the image vector here would be determined by the favorite state so if the movie is favorite we want to have a finned heart so that will be on icons dot default dot favorite else we want to have icons dot default dot favorite border folder right cool content description let's keep it null but again don't follow me on this because this is bad for accessibility cool now if we run this it should show up the fully show us the fully fleshed out uh button and we'll add the on click property to this later so cool you can see that the button is showing up now and the reason why this is showing up as uh favorite is because i've hardcoded that value here so it's favorite it's true cool now the last bit here is the poster so we'll add the poster here and i've already built like a poster image which takes a movie and there's nothing fancy it's just a surface that wraps a coil image and loads it from the url that i've supplied so if i run this you should see the card show up on top cool so this works now there are two things that we want to do so first up we want to wire this together um with the movie details fragment right so the movie details fragment uh essentially passes this let me show you uh details so the movie details fragment basically passes it an argument uh from the navigation args right so this contains uh an int id that it uses to basically get the movie from the repository right and then it basically observes that movie and renders it out so what we will do is we will remove all of this render code from here we also remove the binding and inflating of this excellent now let's return a compose view and pass in the context set a content here and pass in the movie details now this is right now expecting a movie so first of all let's change that so this will now get a movie id which will be an in and we'll also give it a view model so view model with the movie details remodel because we want to load it here in this composable so let's go in here and the id will be available from the args and this is the navigation arts that i'm using here and then i'll simply pass in the view model here cool now to load this because now everything here is expecting the movie first of all what we need to do is call view model dot get movie details and pass in the movie id so that it can initiate the trigger loading the next thing is we need a movie from the view model as a state so view model dot movie dot observe as tape now the problem here is that this is not a list so we can't provide it like a default value right so if i if you simply go ahead and double bang every movie occurring this will cause a crash because initially this will be null so what we can do is first up let's clean this screen out right so we've got like three different distinct components here that are just all you know piled up against each other so we can um cut this out create a backdrop component that takes a movie create a compressible and this will be a non-null movie and paste this here so that takes care of the header sim similarly what we'll do is we'll cut everything here out and create like a movie detail details content that will also take a movie and let's create the composer again and this will be a normal pass this packet cool um now what we want to do is this is fairly clean for now um we basically want to render all of these out only if the movie is not null so there are two ways you can go about it one you do a movie check here movie is not equal to null um sorry if a movie is null you basically return from here so don't compose anything right so what this will do is this as soon as this hits here and the initial value is null this will not compose once this gets the value it will start composing but then for that we will have to again double bang everything which will still work but i really like that's not a clean clean looking piece of code there so what we'll do is just wrap it inside of a letter so movie dot let and then all of these would go inside here and now instead of passing the movie we will just pass the implicit it parameter here cool okay uh let's run this and see if things work as expected the details uh details okay so the preview will no longer work because we require a view model so we'll just remove that um the button will still not work because we've not hooked it up to with a click listener but let's see if the ui looks fine cool cool let's load one of the movies from here okay and there was one thing i noticed which is if you look at this button right this is taking up like this is not actually taking up this entire desired height the reason is because the height is static in this case so we want this screen to be scrollable so in order to do that we will go to the root component which is a box and add it uh add a vertical scroll probably and call this remember scroll state so now if we run this this particular movie should at least look fine cool yeah this loads now what we want to do is we want to actually wire up the button right again so we have the viewmodel instance and if you look at the viewmodel the viewmodel has a set and unset property that basic sorry function that marks the movie as favorite and resets it so what we can do is we can we can take the view model and just pass it to the details continent sorry content and then the details content can just pass it down to the add favorites button but then if you look at this composable right this doesn't really care or have to know about the view model right this this should only be aware about like a lambda uh that will trigger when when you click the button and then we can handle it from the root composable itself because that will make for a much nicer pattern so we will call this an on favorite button click which will be a lambda that takes a movie and then returns unit and then on click here we'll just invoke that lambda so on favorite button click with the move cool now what we want to do is i'll just copy this part and then here [Music] on this particular component we'll add this again and pass this down cool now what happens here is we get a trailing lambda so let's call this movie now if the movie is favorite we want to unset it as favorite so view model dot unset movie as favorite pass in the movie id movie dot id else we will mark it as favorite remodel up so if we run this now the favorite functionality should also essentially work so let's go to godzilla remove this from favorites uh let's go to cherry remove this from favorites to do this work yeah cool remove remove let's add something let's add godzilla back cool so this works now but it isn't particularly exciting right i mean this is just straightforward um stuff that we've retrofitted together to make it work um so for the next part of the talk what i want to focus on is okay i think i quickly not what i want to focus on is actually making this look nicer so i have like a couple of slides around teaming here that i'll just quickly run past um cool let me know if you can see the slides cool um so we've built like the first part of it but now let's focus on actually making it look nice so the detail screen this is what we want it to look like and we'll also add like a cast section right so material theming on compose basically depends on two things one your color palette and two your typographic scale right so for the colors what i went i did was just go to this material resource called the color tool and then you can pick a primary and a secondary color and it'll generate the palette for you so you can see i've got the primary and secondary swatches here and for the type scale i also like built this one out specifically for myself because this is what i wanted this to look like right so this was like the prep work to actually get theming to work but how does uh how does theming actually work in in compose so for that we'll take a look at building our theme out and then assigning the theme to our our ui so if you look at the files i've got two files here so one is called colors and what this is essentially doing is taking those same hex codes the first uh the first four characters in this case is for the alpha value it's taking those same hex codes and generating like a primary color primary dark color secondary and secondary dark color now like the convention is typically to name it um as per the shade like primary 700 primary 500 for the lighter one but i think naming it semantically makes more sense and that's how i prefer seeing the colors in so that's why i've named it this way and then once you've generated your swatches you generate two arrays and one is or two variations one is called a light color and then you pass in the primary primary variance secondary and secondary variant and similarly for the dark colors you pass in the primary secondary and and basically these this colors um function takes a bunch more properties right so if you want to leave don't want to leave this to the default you can specify those in here as well and you can see i've like specified like a background property right now same with the typography so i think i have the type file yeah so for types i already have a lot of the google fonts loaded for me so in this project i have like what five fonts so one is chevo bold regular and then rubik bold rubik one and rubik regular right i'll close all of these off and let's go into the type here so if you look at the type here what you basically do is create font families so we have two categories or two families of font which is chivo and rubik so i've created a rubik family which is uh like a font rubik with the acid rubik regular and font weight normal and then we have a rubik bowl with great bold and extra bold and similarly with chivo now when you create your typographic scale right so this is going to include all of your h1 h2 subtitle body and so on and so forth you can create a text style specify the font size then specify the family that it wants to be that it needs to be picked from and then depending on what weight you select that's the font you'll get so take for example this h4 scale so i've specified the family rubric right and then i've told it to pick the extra bold font so it'll pick up rubik one and give it a 36 sp size and a letter spacing of negative eight um similarly for h5 i've used chivo and i've given it a bold uh weight so it'll pick up the chivo font and give it like a 24 sp and so on and so forth for the other style now this was a lot of you know manual repetitive work that's why i did it beforehand but i'll show you how to build the theme out from scratch so we'll create a new composable called um cinematic theme because that's the name of the app so this will again be a composable called the cinematic theme and now what this will take is is like a content composable right so this will basically apply the theme on composable so this needs a parameter which will be a content which is of type composable function and this will emit a unit next we will use the material theme builder here so we'll pass in the colors so based on whether our system theme is dark or light we want to use the relevant color so if is system in dark theme which is basically a utility function that we come from that comes from the foundation class um so if the theme is dark we want to load the dark colors so let's call it dark colors else we want to load the light colors okay now let's specify the typography so typography is equal to typography and again if you look at this it is back reference from the type scale that we had created right and what would what we'll do is we'll use these two and apply it to the content that we have passed so comma content is equal to content and we can remove this here now we've created a theme but this will not work out of the box like you would um have in the you know xml where you just uh apply a theme to or yeah apply a theme via manifest and you start working you'll have to basically assign each screen its own theme so let's start off with the popular movies fragment where you've hosted the list right so we also want these stylings to work on this particular uh movie card component so what we'll do is set content right so before set content we want to wrap this with the theme that we created so we pass in the cinematic theme and paste everything back here now similarly with the movie details fragment we'll do the same thing which is wrap this behind the cinematic theme and put this up cool now if i run this it the theme will not just magically you know get applied on all of these composables we have to do a little bit of work uh but what you can still notice is at least like if i go into the detail screen and if i turn on dark mode the surface color and everything works fine right so let's let's now start applying the typographic scales to our different composables here so we'll open the movie card cool so first up what we want to do is style this rating bar text okay so so for the rating bar we will have i think the styling would be material h4 so first let's remove the font weight and the font size and give it a style and the styling will be material theme dot typography and i'll just copy onto my clipboard because you'll be using it a lot dot h4 now if i run this the rating text should at least be changed [Music] cool and you can see my h4 definition here is a rubik one which is this font okay cool so let's apply it to the other components now so for the text here again remove the font weight and font size and give this um i think this will be a h6 because this is lower than the cut so this will be style equal to dot h6 and then for my movie info this will be a subtitle font so remove this and then give it a style is equal to [Music] dot subtitle cool um let's run this and see cool looks good we can probably do with some padding here but uh let's let's try to fix that so movie card this is the movie info let's add it start some padding so modifier just go to modifier dot padding up is equal to 8 db and now while this runs let's go into the details card and modify those so again for the for the text here or the title we'll still use the same uh h6 if i'm not wrong so remove this from here player style is equal to 86 um yeah and then this should get a body style so style got body one let's run okay let's run this now expecting an element i just fixed that let's open a movie cool um so in order to style it uh according to the design i had shown you earlier we need to make a couple of changes here so this will be aligned to the start now and then we also had like a section header here so i've already created that component which is again just a text view uh sorry a text composable and i'll pass in the overview i'll just show you what this looks like so if you go into the section header this is just a text with a text alignment of start and using the subtitle uh property so this is what this looks like and we can probably change this to start and top because we don't need that the bottom padding which was unintentionally applied cool for the final bit we will add like a cast row for this um screen so if you look at the view model here the viewmodel has a cast property which gets like the cast details from the api and then image shipping to this live data so first up let's also fetch the cast from the from the api and then we will observe the cost as a state so cast and since this is a list of cards we can pass it an empty list okay now what we want to do is we want to show the cast before the button so if you look at the content here this is where we want to show the cast so first let's add a section header call it cast and then what this will do is we just will build a cast row using the cast that we have now we will need to pass the cast to it so let's do a cast which is a list of cast and then this will also need to be passed from here so let's go back and let's build this composable out now so this will be just of course so what this will do is we this is also going to be like a horizontal recycler view itself right so we saw a lazy column right now we will see a lazy row now lazy column and lazy door don't really have any difference as such it's it's mostly around the um orientation okay so what we will do is we want to uh we don't want to have any padding here or do we so i think i think it will probably take some padding later on uh anyways so what we will do is we'll just loop through this list so items and pass it cast and for each of these card um objects we will create a cast item so this will be a cast item that takes it now this is not created right now so let's just create a composer function required and found one sec am i observing the correct thing here cast is in fact a list i am passing that down there's a cast list and then we have a cast over here um probably some kind of oh yeah we were using a different con um variation so we'll just pass in the past and then create a cast item which takes not an end it will take a cast right so now what we want to do is uh we want to have like a uh a circular image for the cast item right so what we will do here is just use the coil image property and then give this a modifier so let's give it some padding first uh where did it go here right so modifier.padding and we wanted to have like a start padding of around 24 db and then this image size let's keep it 60 60 dp right and then what we want to do is we want to okay this is not this is the request so this will be a modifier that's right yeah uh and then we want to pass in the data so data will be cast dot poster url and then we want to use the request builder to crossfade this in through and we also want to have a transformation called the circle crop transformation that circle crop transformation um and what the circle crop transformation does is it takes an image makes it wrong that's it cool uh let's give this a content description as well so request builder ends here we can give this a content description which will be the cast name and that's it let's try to run this and see if this works hopefully it should cool and as you can see the cast load we can probably give it some padding because it's hugging the section header at this point so castro um give it some content padding again studios and let's run this for the final time and yep this looks much better now we can probably do one last uh one last thing here which is make the images fade in because right now if there is no image it just looks like a blank screen there um cool let's open this up yep this is looking good so awesome cool so that's that's it for the app um as you could see like you know i took an existing view based application and simply converted it to juice compose um it took me about like one and a half to two hours but depending on the scale of your application this might take a little longer you will also have to invest some time in state modeling within your view model and um taking care of things like um things like theming if you look at the colors and and the typography particularly this is probably very common in how designers do a handoff so whenever they build like a design system you can expect this as a spec here and so what you can essentially do is use the spec to map out your typographic scale and then build out your individual smaller components out now this was not an ideal approach by any means like i could have very well created you know specific test text components itself with the uh different and correct stylings applied to it and broken down the screens into much more smaller components but i wanted to just highlight how approachable compose is and how easy it is to get started once you get the hang of um the basic nuances so that being said i'm open for any questions that you might have so i'll just remove this and start up the video again yeah cool any questions that you might have yes let me just look at the comments areas is zip pack compose support hot reload hot race car if you're comparing it with flutter no they had talked about improving the tooling like you could see like the tooling is still not perfect and it still has the autocomplete is not as good but they are working on polishing things up with the um with i think the new ir back in in the kotlin compiler things should get a little more faster alongside kotlin symbol processing so that was one of the things that they they they have on the cards and they are investing in but right now it's not as good as flutter or react for that matter yeah we can apply jetpack now pagination um yes so any any architecture component library or jetpack library or anything that comes from the regular view system as long as you know how to make the data consumption work with compose it'll just work out of the box you don't have to worry about anything else um the interrupt story is i think the most important part i don't think for the for the near future people are just going to scrap off what they have and you know just start integrating compost from scratch so it was kind of important for me to show that with the existing fragment and activity setup you can still integrate compose within it right i could have like gone uh full blown on this and you know scrapped off everything built out the navigation structure itself with compose but that's like a lot more work and navigation in itself right now in compose is not that stable but um it should work like any legacy thing that you have or any uh jetpack library that that that you're using you should be able to make it using uh usable with compose unless there's like you know a restricted restriction as such this is a similar lines uh we know there's one question is jetpack's uh life cycle or is composed life cycle aware the reason why i removed those is because the composed view is just like a regular view that plugs into a composable so that compose view is still restricted by the life cycle of its parent which is the flag uh compose i think i had read it it still has life cycles but you don't have to take care of it it's i think uh on active on dispose and on commit you don't have to worry about these um state management is pretty good in composables um one of the things that i did not cover in this talk was uh composable scope specific memory with things like remember so you can essentially remember the state of a particular composed widget right so in case the orientation changes you won't end up in like a bark state where you know you have like an edit text with a lot of data and then all of that data goes away so you can you know do a hierarchy specific or compose node specific you know remember there and just remember the state of that particular widget to re-render it out in that particular state back when the you know orientation or configuration changes on migration should we keep the fragment or just use single activity with multiple composed components that totally depends on your use case if you have a project right now which uses uh fragments and you want to build a new feature out in compose you can just plug that fragment with the composable using compose view you can also set it directly in the activity activities come with that set content extension that you can use um or if you want to you know you want to like take that risk and move all into compose you can use that as well it does provide you the flexibility of having a lot of options so see what works for you components defining is internally using constraint layout i don't understand uh if you're talking about layout compose does have a version of constraint layout to handle complex layout so the screen the detail screen i showed you in the xml version i had used constraint layout for it and compose does have support for constraint layout but i wanted to cover the primitives here uh the box and rows and columns to build screens out to show that you know things are still possible with it of course compose comes with constrained layout so you can you know transfer that learning there the apis are slightly different uh in terms of defining constraint but internally no it doesn't uh constraintly out internally to lay things out it depends totally on your modifiers and what parent it belongs to for laying things out a couple of more questions how to handle landscape view using composers sometimes we can have landscape view different from portrait um you can have i mean if you want to have like a totally different variation you can have like a um a different composable altogether and you can just check the configuration of the screen and invoke that composable instead or in most cases you can just you know play around with the modifiers and adjust them to work with the uh landscape view so think about like this screen that i showed you the details this was not vertically scrollable and if i change the orientation and i want this to be scrollable i can just apply the vertical scroll modifier there so it's almost almost similar to how we take care of it right now in the view system if you want to have like a totally different layout file you as you create in the current system you can always do like a you know different composable altogether but you can also adjust the existing layout itself to work with the landscape layer so if at all it's a different view then in oncreate you check the configuration and then call out the different composition yeah i have not to be honest i have not actually worked with landscape layouts yet but it should be possible like compose does offer a lot of uh checks like you know you saw the dark theme check there are ambience uh similar to that where you can check the configuration and depending on that you know render things out differently so it should be possible it shouldn't be that difficult actually how can we add remove update modifiers dynamically after ui is drawn say uh text size value is dependent on api response would that be any ui performance implication on low end devices uh good question i so if if your text size is dependent on like a data source so as you saw there was like a text composable where i was defining the size right as long as you render it or sorry as long as you convert the response into the size it expects i think it's a text unit right and you convert that into an sp um it should be fine like just observe your data source get the result from it and convert it to nsp and it should take it just fine as far as the ui performance is concerned um so if if your data source changes it's not like the entire screen will be redrawn so what it will do is we'll take take a look at the node and you'll see which part of that state is being observed by which node and let's say if there are three different things in that state and only one has changed and only one particular compose node is accessing that so all the three won't be updated it's only that one that will be reinverted into it on so performance is taken care of before you intern so you don't have to worry about it yeah i think that's a wrap that's all we have thanks for having me this was this is good sorry for the um studio issues early on but um i think this was fun yeah that was that was fine like technical issues can come but like super useful it was nice to have built slowly slowly and we saw four there like listing and detail everything with the dark theme coming up in like one hour time and the answer yeah thanks thanks for having me and uh yeah if you have any more questions just feel free to reach out to me on twitter i will share these slides and the project later on on twitter itself we will talk so you can you know see his handle and you can contact him for any composite and doubts uh thank you so much cool take care bye thanks everyone for joining uh i hope you learned uh like a few bits about compost and do check it out it's gonna be the next big thing in writing ui so that's it from our site we can sign off now thanks everyone
Info
Channel: GDG BlrDroid
Views: 298
Rating: 4.8518519 out of 5
Keywords:
Id: jUIZMUzdTco
Channel Id: undefined
Length: 107min 34sec (6454 seconds)
Published: Sat Mar 27 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.