Composing an API the RIGHT Way: Essential Tips for Jetpack Compose! | Droidcon Italy 2023 Talk

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
I really want to emphasize that you don't have to really strictly follow all of these rules especially if you're on the app side of things you can be a lot more flexible about it but if you're building libraries internal or external or like something really core that a lot of others are going to use uh then please take a good look at these guidelines and try to follow them so that we can all have a nice time in compose and have common uh understanding and expectations of how these components should work I also don't expect you to remember all of these things after the talk so again the guidelin dos are always there you can reference them if you have a specific design problem but if you took a couple ideas away from this stock uh then I'm already happy with [Music] that all right uh hey everyone uh thank you for choosing this talk uh again it's lovely uh being here uh my name is Martin Brown I'm a veler Advocate at jet brains working on things like cly multiplatform uh come find me with questions later uh and I just want to very quickly highlight that it's the uh Friday the 13th which is just an excellent date uh to be speaking on uh it's no secret that I'm passionate about API design I've given a couple talks about it in the past and I've uh written uh some things about it as well uh and this is because I really do believe that everyone is an API designer uh regardless of what kind of development you do whether you're building like Frameworks libraries or just app code um you are designing apis uh like again even if you're building an application you're building apis for your teammates to use even if you're a solo developer you're going to be using your own apis later on where you have forgotten the times when you wrote them um so it's important to think about um how you build apis of course the importance uh is a scale here uh so it's a lot more important if you're right some kind of core code that a lot of developers are going to be calling and you can be a lot more flexible about it if you're more on the app side of the scale so this talk is going to cover these two documents uh which live on GitHub uh these are by Google and they describe uh API guidelines for Jetpack compose and API guidelines for composable components in Jetpack compose there's a bunch of overlap between these uh and they are very very long documents uh it's about 13,000 words uh some online tools estimat that it's about an hour or two to read all of this uh and it's dense technical writing so I hope to bring some of the highlights of this uh to you in this talk the idea behind these guidelines is to have some kind of common understanding of what idiomatic composed apis look like uh and then hopefully all of us will be building apis uh with this common understanding in mind which means that if you have some practice with like the built-in things from a foundation or material or some of the popular libraries then your skills are transferable because as you start using some new composable components they will behave similarly to how you've already used other composable components um if we designing them the same way so these documents do say jetpack compose on them which is an excellent Android Library uh but I really do have to highlight that if you learn these things about jetpack compos API design you can actually reuse all of that with compos multiplatform which if you haven't missed this morning's excellent keynote uh you already know about uh but it's a way of bringing the same apis to um not just Android but also iOS desktop and the web um and uh this is the current state of support uh for the uh for compos uh multiplatform uh so iOS is currently in Alpha Android and desktop are stable um you can already build uh Windows Mac OS Linux apps uh if you want um Jin toolbox is a great example of this uh and web is currently experimental but maybe hack around with it and give us feedback uh very quick overview of what we're going to cover so we're going to look at naming first start with some simple things then we'll explore a bunch of things around the parameters of components and finally we'll take a look at a couple things around State handling this is going to be a lot of code so I hope you've had your coffee um and we're get started so naming first uh starting with something simple uh this is how constants are supposed to look like in cotlin this is what the cotlin sty guide tells us to use it's the same that like shouting convention that Java has for static constants and compose actually doesn't use this uh convention uh instead compost chooses to use camel case for uh constants uh with an uppercase uh letter um up front uh which goes against the guidelines but the idea for compose is that they don't just use this for constant properties like this they also use it for other properties which are basically constants they are just not Primitives so they can't use the cons keyword and they also use the same naming convention for objects whether they're top level or maybe inside a seal class or some other class and they also use the same convention for enums in all of these cases the things that have the same uh naming convention are semantically the same they are a constant value you can safely reference them it's stable over time um and whether it's a property or an object or whatever else uh is really just an mentation detail so according to the compos guidelines you don't have to care about this all of these should be named the same way so uh that was easy uh what about functions can anyone in the audience like character shout uh how composable functions are supposed to be named I heard uppercase and any any Undertakers who agrees with uppercase let's let's do that first letter uppercase okay so of course the answer is that it depends as everything uh so there are two categories of uh composable functions uh we can sort every composable function hopefully into one of these categories uh they can either emit content or they can return a value and never the two shall meet uh so you should not have a composable function that does both of these things let's take a look at some examples so for emitting content uh here are the composable functions that you probably use a lot if you're writing compose uh so things like button and column these emit UI specifically so it's clear that they emit content and we have this uppercase naming convention for them um to indicate that when this function is called as part of the composition it is creating a thing when uh this function is called something will exist as a result of calling it so uh these functions kind of look like Constructors uh with this uppercase naming um and this doesn't just have to be UI of course uh so we also have launched effect and similar functions which are also a thing that will exist in the composition it's an entity of sort so we also use the same uppercase uh naming for these uh and it's important that the names of these should be nouns so it shouldn't be like you know create button or something like that we just create we just call Button um and that uh will manifest the button for us so in the other category are composable functions that return a value the purpose of these is not to emit something into the composition they don't produce UI for example instead they return a value and they do that uh having awareness of the context of compose so they might access um composition locals to produce that value they might rely on composable State and produce new values as state changes um but they their primary purpose is to return a value and examining these functions that do return value it's important that you should not follow this special case that com uh cotlin has in its sty Style Guidelines uh where you can create these Factory functions which mimic Constructors by just having the same name as the type that they're returning if this is a composable function you should not use this guideline uh again you should stick to the lowercase Ling uh to indicate that this is going to be a composable which just yields a value um and this especially applies uh if it's doing something more complicated for example you can have these uh functions that already remember the value for the caller so on the call site uh in order for you to know that you don't have to remember that value uh you should use the remember uh prefix and you should again keep to lowercase all right uh there are a bunch of things uh which these guidelines Define um rather a lot of different terms I'm going to condense some of those into a single one which I'm going to call component so a component is going to be a composable function that returns unit and emits exactly one compos UI tree node returns unit emits a node into the UI tree uh we're going to look at these uh in turn starting with why a component uh should emit exactly one node um so we have a bunch of like basic compost code here uh and when we're building compost we're always thinking in trees these function calls uh always have a tree structure so in this case for example we could represent this uh using this tree here uh where we have the root up top which is the column and then that has three children the text text and row and so on and so on for the rest of the nodes with those uh texts n nested in the buttons at the very bottom of the tree and the very cool thing about compose is all the code that you're writing is just codling code so things like extracting a function work really well so we can just look take a look at a bit of compose code uh like that piece right there and we can extract it to a separate function uh for example I'll extract those two text nodes um and call this function title and then where those used to be I can just call the title function and I'll have the same behavior as I had previously uh you can also do this with intention actions in the ID uh in an automated way but if we look at the tree that we've generated down here um it's rather odd because it doesn't have a root anymore so this title component doesn't produce a single UI tree node it's produces two different nodes which in the example above will be attached to the column that we called the title component in but this is a dependency of this component uh because it only it can only be called when you are inside a column and uh a healthy compos component should be like self-sufficient it shouldn't depend on the specific specific context where it's being called um so this is a great way to produce bugs like this uh where if you don't call this in a column um but you're in some other layout like a box or wherever you are um then you can really easily end up uh laying out these texts on top of each other so in order to not have the chance to make this mistake um this component should again emit just a single root node which we can fix in this case by just creating a column within the component wrapping the text with that and in that case we have like a proper tree with a single root and this can be used anywhere we want to I hope you also got the subliminal message on that previous slide um getting back to the definition uh the next thing I want to take a look at is why these components should return unit so uh why it is that whenever we have functions that produce UI they should not also return something uh there are a couple of reasons uh here's a bad example which you should not follow uh where we have an input field composable we can imagine that that displays something on the UI and it returns an input State object which um you know contains whatever the current input is uh like as a string or something like that and then we have this example code which creates an input field receives that state and then passes it on to a botton listener so that the state um can be cleared when we press that button um I'll highlight just two of the major issues here uh one of them is the ownership of this state so the input field has uh created that state object for us and then it returned it which means that it has full access to that State uh it can modify that state at any time but on the uh call site we also have full access to that State uh so the component uh is stateful it already contains its own State and can modify it but we can also modify it and these two sides don't know if the other one is doing anything with the state um so this problem of shared ownership um is the first problem and then the other one is is a bit more simple um this restricts us to only referencing this state after we've already called this function which is really inconvenient because what if we just want to reorder our UI and have the button on top uh now we no longer have access to that input State because it will only be created when the other piece of UI exists so uh in order to fix this um we would be much better off with a stateless component uh like this where instead of creating the state it receives the state as a parameter uh this way it's controllable externally whoever is on the call site is free to create that state any way they want uh and then they can pass that into the component uh to control it and uh of course we also don't have the ordering issue at this point uh because uh now we can just declare the state anywhere uh in the tree and then pass it on to any of the components that need to reference it um so this is how composable components should be driven uh they should ideally be stateless whenever you can manage it um so that they can be controllable externally and their purpose is uh this very classic thing if you've ever seen a compos talk you've probably seen this already uh but they have to take their inputs the data that they receive and turn it into the UI and so uh up next let's talk about those inputs uh the primary way of providing inputs to a composable is through the parameters as we've already seen but I actually want to take um side note here and talk about composition local a bit uh I will not show all of the syntax of how to use composition local uh if you want to learn more about it uh you can follow this link uh and as with all links uh this these are going to be on the final slide so you don't have to take a picture of each link along the way um so composition local is a mechanism where within your composer uh at some point you can provide a value for the rest of the subt tree that uh that uh node is in so for example we could provide some value there and then everything that's below that point in the tree would have access to that value in this uh ambient way if you want to say um so for example in that node over there we could reach out and grab a value uh that has been provided as a composition local without having an explicit parameter for it now this has its use cases uh mostly you should use this for styling which is really like a global concern as and it's really convenient if you can uh change that at the app level or at the screen level for example but you should really not use this just to pass data down like if you've extracted uh something from an API and you have like data to put on the screen please do not use composition locals to pass that down uh please prefer um using parameters instead uh which is composes General ethic itude so uh you should prefer explicit parameters over the implicit composition locals and really like only use that essentially for just styling purposes another thing that composes huge on is using default arguments uh so uh default arguments are great because uh we can have these really complex composes that offer a lot of customization with all of their parameters but they can be calable with just the required arguments so in the case of text for example you can just pass in a single string and you already get a word working uh piece of text on the screen and then you can go on to provide more of the optional parameters we can also see a great example in the text uh composable of using composition locals it's a really nice uh way of going about it to grab a composition local as a default value for uh one of your parameters because that way your component can pick up the composition value uh the composition local value by default but callers can always choose to override it if they prefer to do something else as a local decision something you should not be doing um is uh using null values to mark that a value has not been provided so default Arguments for composable components should be real values whenever possible and if you use null then null should really indicate the absence of value it should mean that something should not be Set uh but in this case uh in this example here we're using it to indicate that we should fall back on some default value and we could have just placed that default value um in the signature in the first place uh here's a nice example of using null uh so the button composable uses null as the default value for the border of the button and here null really means the absence of value in that um if we don't provide a value then the button is just not going to have a border on it something else that we can take a look at uh in this same signature is that if you have a lot of default values um that you provide it's a nice idea to collect them into a single object with the convention that uh it should be um you know component name defaults so you're using the default suffix for this object in the case of button um we can find this object this is a very very shorten version of it uh but as you can see it provides a bunch of things uh like the uh default sizing information uh as well as functions for example to produce um some of these button related types like button colors and so on um and if you make this accessible um to users then they can actually reference these default values so imagine a case where in some situations based on some kind of condition you have custom colors that you can provide to the button but in other cases you want to fall back on the default one uh if these defaults are nicely organized and made public then and on the call site we can use them in conditional logic and sometimes use our own things and sometime use those uh built-in default values but if you were to um decrease the visibility for example make these default values internal or private then you would take away this ability from the users of your components all right um next up I want to talk a bit about modifiers uh which are an excellent part of composes API design uh at least in my opinion um so modifiers give us this uh ability to only accept a single parameter in our function instead of having to uh add like dozens of parameters for customization of size and padding and uh background colors and cropping and all of these things we don't have to add all of these separate things uh to the signatures of our components instead we can just have a single modifier parameter and then we have this nice chaining API uh where we can uh customize a bunch of things uh in a single place uh this also means that if you're providing a modifier parameter you should not provide conflicting things as your parameters on the component so for example if you have a modifier parameter already it doesn't make sense to also have a padding parameter because that can already be covered by using modifiers uh an interesting question is whether you should have modifier parameters or not uh let's examine what happens if you don't provide a modifier parameter you might think that that means that your component will not be customizable but that's actually not the case because your component can always just be wrapped by something else which can accept a modifier our parameter and this essentially um achieves the exact same thing as if you just had a modifier parameter in the first place and the modifiers uh could have been passed to your component it just makes the call site really inconvenient and it introduces extra nodes in the tree uh so in general your components should have modifiers uh this is going to be one of the um one of the first rules uh that we can extract from these guidelines that composable components should accept a modifier parameter uh something great that I heard yesterday in a conversation and I'm going to look uh certainly at the person who came up with this is that if your component is a public component so if it's not like private in a file then you should provide a modifier this is not part of the guidelines but I think it's a really good way to go about it if you have something that's only used locally like it's just private in a file then maybe it's fine to drop it but really you should accept modifiers uh going on uh this should be named modifier by default um it should be the first optional parameter it should be applied to the root node of your component and its default value should be modifier we're going to take a look at all of these in turn um the first two are fairly obvious um this third one we're going to get back to and I want to look at why it should be applied to the root node so the idea behind modifiers is that whoever is calling into your component wants to customize that component and if you have a component like this one for example uh then looking at your component from the outside it essentially looks like a column right like because that's the root node that you're producing so your component behaves like a column so whenever you take a modifier as a parameter then it should be applied to the Colum column uh a counter example would be this so if you were to apply that modifier to just part of your hierarchy and not to the root inside your component um then if someone wants to change something like the background color for your component it would only change it partially uh so uh these are the reasons why you should always apply it to the root node uh now it's important to uh see that sometimes this root node in your component is dynamic so for example you might have some condition and in some cases have a column layout in other cases have a row layout or something like that uh in these cases you should make sure that the modifier ends up being applied to whatever is the root node after all of these conditions are evaluated uh so here you can see uh that in both conditions uh the component that's emitted as the root here into the composition uh will receive the modifier parameter it's also important that you only apply the modifier once because you should have just one root as we've already discussed and you should not take multiple modifier parameters if you find yourself doing it that then you probably need to change up the design of your component maybe split it into multiple components or something like that and uh then finally the last thing we had here was that it should have a default value of modifier uh in case you haven't looked into this yet uh this default modifier value that we use is actually referencing the companion object of the modifier type so modifiers in interface uh there's a companion object in there and that companion implements the interface so if you just write down the type name you get the comp companion which is already an implementation of modifier and it's an empty implementation of modifier which means that if you use this as a default value and apply to your root and the callers have not provided anything to override it then it will just have no effect at all and if they did provide some values then those uh would take effect um on your component now as you're applying this modifier uh you're allowed to add additional modifiers onto it but it's really important that you can only do that you should only do that uh by changing things on top of uh like changing things after U the original modifier like you can see on this slide so something you should not do is this to create a bunch of things uh with modifiers and then chain um the modifier parameter somewhere along the way like to the end or to the middle of that chain so always put the modifier at the beginning of the chain if you need to add additional things uh this is important because modifier order matters I'm not going to explain modifier ordering here uh but you can go to the docs and modifiers uh which do that very nicely again links are going to be at the end of the talk okay um next up I want to talk about slots um I think I like slots even more uh than modifiers uh they're an excellent piece of compose and they alleviate a lot of problems that you can have with the view system um so slots and compos are this idea that uh we can pass in uis parameters because we can express this as a type in cin types system uh by using a composable function type such as this one uh so for example if you're designing a button and you want to offer customization to the callers so that sometimes they can put text on it or sometimes they can put icons in it or an image or whatever else or all of these things at the same time then you don't have to try to create all of these overloads of your text function U your button function um to accept these things you can just accept arbitrary UI and then of course on the call site we get this really nice trailing Lambda uh syntax where um we can contain the UI um in like um in our original component so for example we could pass in a row here and an icon and a text next to each other and then that would show up on the button uh we can go further with using slots uh by adding a scope to the slot so for example in the case of our button if we know that in most cases users uh will want to lay out multiple components next to each other when they are calling our comp component we don't have to force them to create that row we can create that Row in the implementation of the button and then put the composable function into a row scope that way the callers see that whatever content they're emitting in the function that they've passed in will end up inside a row layout side by side um this also gives us access to things that only exist in rows so for example you cannot use this weight modifier in arbitrary places and compost code this is only available when um you are inside a row because it's the row layout that will interpret these weights and use it to uh size the uh contents that are inside it um so this is also something really nice uh we can provide here now you might be wondering uh isn't this now a component here which emits multiple nodes which we said we shouldn't do we said we should always have a single root for every composable component and the short answer is that it isn't um so this here is not really a component uh this function that we have down here um it's just a function that we're creating as a Lambda so it's a single use uh function we already see that we're inside a row scope so we know exactly that the nodes that we emit are going to be uh put into a layout correctly um and we're not going to call this from anywhere else because it's not like a top level function something interesting to think about when you're building apis that accept uis parameters is the life cycle of those uh slots so here here uh we have again an example where we have some kind of condition uh in the component and depending on that condition we either have a row or a column layout and whichever layout we're using in both of those we uh use the uh content parameter that we've received uh which is uh slot and the problem with this code here is that as time goes on uh the preference item is created at some point and it exists for um like um some amount of time but the content which is also are created at the same time in like one of the layouts will actually be dropped and then re-executed and added to the composition again anytime that the state changes here so as we're going from one of the conditional branches to the other one the existing content that we've produced by calling the parameter of the function the the content function here um that content gets dropped and we have to call that function again so that it produces UI again and we can reattach it to the new layout that we've created um so the guidelines say that whenever possible um your slots life cycles should match the life cycles of their hosting components um so essentially here we would want content to continuously exist and just be moved between different places uh between the row and the column but not be recreated every time and the solution to this is a very very specific compos API which is designed for exactly this use case uh so the API is called movable content off uh you can wrap uh composable function uh with movable content off then you can remember that at some point in your composition and this give you gives you a new composable function to call which well moves content as it's called multiple times so as the state changes between checked being true or false uh whenever movable content is invoked it will produce the UI if it doesn't exist yet or if it has already been produced it will just move those nodes to another place in the composer which is really convenient I again don't want to dive into the details of this but there's an excellent blog post uh about exploring movable content off and movable content with receiver off um which you can check out again on that link all right uh with that I want to wrap up the parameters part uh of the talk and so the last thing we'll look at here is how to order our parameters so here's the recommended ordering for parameters in compose uh required parameters should go first then we can have our optional parameters uh with modifier the first one and finally we should have um primary slot if we accept a slot parameter and we have a primary one if in case there's multiple uh then that should be called content and it should be at the end of the parameter list to take a look at these rules uh I've grabbed this random function from some open source project uh and we'll see how it doesn't comply with these rules and uh fix it up a bit so first uh we should have our required parameters um why is this a good idea uh because if required parameters are first then the component is callable by just providing those required parameters and we don't have to do name parameters or anything like that we can just pass in the required parameters and the component will already have a basic uh like working uh copy uh for example again in the case of the text function which is also being called here that only has a single required parameter so you can pass in just a single string and already have it working um in this case uh this would not work because the first parameter is a modifier um so um we couldn't just skip that parameter because we're providing things positionally so to fix that we would have to move these things to the top of the parameter list we could of course always name these parameters especially since we have a booon here which just says true on its own which is really not helpful it's always a good idea to name these parameters um but you know maybe you have inlane hints in your IDE um and then you don't need them so much then uh we should have optional parameters next so we have the basic required parameters and then optional parameters allow us to further customize the behavior if we start providing them and again the rule here is that the first one of these should be the modifier the reason why this is important is because if we all adhere to this convention and we all use modifier as the first optional parameter then if you take a look at any composable function call which already has its required parameters you can without thinking just add a modifier for the next parameter and it should just work um so in this case for example we could just grab a modifier put it on the next parameter slot and if we follow these guidelines then that will just work because that's the next parameter uh finally uh we should have uh the trailing Lambda if we do have it and in this case we do have it uh of course this allows the nice like visual containment syntax this like DSL like syntax in cotlin um which allows us to have the Lost Lambda parameter outside of the parameter list um and the only problem here is the naming of the parameter so by convention this should be called content uh so we can just fix this um a very quick note which I didn't add the slide for uh but within each of these categories you might have a lot of different parameters you might have you know like 10 15 uh optional parameters for example um So within those categories you should also strive to put the more important parameters first so that they're more discoverable uh just as a general rule of Thum uh next up let's talk a bit about State handling and I want to start this section uh by talking about the state type specifically uh so the question I want to answer first is is it a good idea to pass in mutable State uh into a component it's state we're passing it as a parameter it kind of makes sense uh this is something you should definitely not be doing um this again introduces an ownership problem for this state so this state has been created outside of the component so someone already owns and manages the state but if we pass it in as mutable state to the component then suddenly the component can change its own State without us being able to control it uh from the outside so you should avoid passing immutable State um and it's a lot better it's a much nicer idea uh to pass in just plain values instead and then have the component um use a call back to indicate the caller that it wishes to change the state so you might be wondering is it okay if we use the readon state then because then we don't have the ownership problem because the component cannot change it we can still have a call back where it can indicate that it wants to change the state uh and the answer is that this is safe to do but you should still avoid it um the problem with this one is that it's a very specific uh type of parameter so it forces callers to always provide a state object specifically but what if on the call site they already have a plane value from somewhere what if they already have a float and you're requiring them to pass in a state of float uh then you know you're forcing them to create a new state object just to put the float in there and then inside your component you're probably going to read the float from that state object um so we've just packaged it and unpackaged it for no reason so in general uh you should prefer accepting plane parameters like these as parameters to your components and in some cases uh you can also accept a function which will produce the value that you need um this gives a lot of flexibility on the call site uh because if you're accepting a function then that could be a constant it can be an existing plane value it could uh read existing composable state uh or it could read values from some State object which isn't just the state type but some other like State holder in compos which is uh backed by composable state um so this gives a lot of flexibility and what it also uh offers the uh implementation of the component is uh to defer the read of this state so you don't have to read this immediately as you've received it but you might be able to read it in like a later stage of composition or something like that which is great for optim imizing things uh here again I have an article to recommend um which talks about debugging recomposition and as a part of that talks about how you can differ reads to State objects and optimize a lot of things uh that way in compose the final thing I want to touch on is State holders so as you're adding your parameters uh to your components you might end up in a situation where you have a lot of State going in and a lot of callbacks coming out so that the component can request updates to the state uh I'm just going to do this with two different pieces of state but you can imagine this um scaling upwards so at some point you might decide that you don't want to have all of these uh split components which is you know just a lot of parameters to provide um you don't want to have all of these components in your component uh all of these parameters in your component parameter list uh and so in these cases uh you can extract this into a state holder um for example uh this one and then instead of providing all of those individual parameters you can provide just a single one now um there is a convention for this the stakeholder should have the name of the component plus the state suffix and it should be stable uh stable uh here means that whenever the values inside the state holder change uh it should notify the composition so this essentially means that the implementation should uh be based on composed mutable State uh but I will again not dive too deeply into stability but you can learn more about it on that link speaking speaking of the implementation there should be an implementation of this state which is quite important uh because if you require an interface as a parameter and your callers uh have to implement that every time that they want to use the component that's really inconvenient so you better provide an implementation that they can use uh so here's a very simple implementation of this one uh where we are just delegating both of the properties into a mutable stateof uh to make sure that we're uh complying with the stability requirement uh after this uh to make the API even nicer and what we can do is we can make the implementation class uh private detail so we can make that class private and we can have a factory function that can produce instances of this but only returning the interface type uh here because this is not a composable function it doesn't depend on anything in the composition uh this can actually use the regular like a cotlin like virtual Constructor or factory function or whatever you want to call this where the name matches the name of the type that's returned and then uh to go even further so that uh we are not forcing our clients to even call this function every time if they don't want to manage the state we can have a default value where remember can help us uh and if there's no state provided we can create our own State object remember it by default and then work with that state but of course one can always be passed in now all of this code uh maybe doesn't seem like the solution to the problem of having four parameters uh this is way more code than than what we started with um but if you're designing a library then this is a nice way of going about it of creating an interface providing a default implementation uh then clients can always provide their own implementation if they need even further customization and again there's a lot about this in the guidelin still um but what I what I really want to highlight at this point after Ben's done taking the picture thank you uh is that for app development where you don't need all of this flexibility and you're fine with just a single implementation you don't have to build up all of these things so if you're just working in an app and you want to introduce a stolder you can just have that be a class like this it doesn't have to be an interface and all that um as long as it's uh again stable and then of course as you're later on editing your code and maybe find yourself needing multiple implementations for this uh same stolder you can always just extract an interface out of this and of course there's someone providing tools for that as well in conclusion uh I really want to emphasize that you don't have to really strictly uh follow all of these rules especially if you're on the app side of things you can be a lot more flexible about it but if you're building libraries internal or external or like something really core that a lot of others are going to use uh then please take a good look at these guidelines and try to follow them so that we can all have a nice time in compose and have common uh understanding and expectations of how these components should work um and I also don't expect you to remember all of these things after the talk so again the guideline docss are always there you can reference them if you have a specific design problem but if you took a couple ideas away from this talk uh then I'm already happy with that so uh here are the links that I've promised you um during the talk uh you can check out all of those things uh to learn more about them um you can also find these and the slides uh on the link on the slide and you can find me on social media like Mastadon and also around the conference so please come ask questions uh about composing apis I hope everyone got that P before choosing this talk by the way um so ask questions about that compos multiplatform costly multiplatform uh anything like that I'm really happy to chat about um and I think that's it thank you Marton
Info
Channel: Android Heroes
Views: 799
Rating: undefined out of 5
Keywords: Jetpack Compose API, API Design Jetpack Compose, Compose API Best Practices, Marton Braun JetBrains, Droidcon Italy 2023, Jetpack Compose Guidelines, Android Development, Compose Multiplatform, Jetpack Compose Components, Jetpack Compose State Management, Jetpack Compose Modifiers, API Parameters Order, Jetpack Compose Naming Conventions, Designing Compose APIs, JetBrains Developer Advocate, Android API Development, Droidcon Talks 2023
Id: R7GZMhakSBY
Channel Id: undefined
Length: 39min 59sec (2399 seconds)
Published: Mon Jul 08 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.