Ep. 056 - How to create a custom animated drawer in Flutter | Flutter Processing

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what is up flutter devs today we're going to build a custom animated drawer in flutter that we're going to use in the flutter processing example app let's jump into it here we have the current example app or at least one of the entry points to it in flutter processing i've ended up creating a bunch of different entry points and there's no need for that for example in addition to what you just saw running here under the coding train all of these different files have their own main entry point that means you have to rerun the build every time for each one of those and there's no reason for that we want all of these demos visible in one place what we're going to do today is we're going to take we're going to start with this screen here and we're going to add a little overhang here on the left side of the screen and when you tap on an arrow a drawer is going to animate out with groups of options for demos that you can click on and then you can close the drawer to view the demo now the first thing we need to do is create a screen specifically for that purpose because everything that we see here is actually just content it's like a page within the screen and every demo has its own page but we need the screen with the drawer with the chrome the scaffold whatever and then we display this content inside of it let's get started with that let me show you what we've got to begin with first here's the normal main main file this is the typical example entry point you can see here here's our main function for this entry point we call run app like normal this is the app that we're running it's a material app it has a default theme and then it displays this home screen everything you see here is the home screen but it's not really a home screen it's more of a hacking demo and so that's what i'm going to call it i'm going to rename this to hacking demo and not only am i going to rename it i'm going to move it because again what we want to launch is a generic screen for all the demos and i want to collect the you know my own demos in a directory here that we will call demos so we already have the coding train and that i'm going to continue storing anything that i create that simulates or ports what dan creates over on the coding train that's going to go in here so everyone knows where it comes from and we give appropriate credit but a lot of other things that i create that are my own ideas or hacks or prototypes they're going to go under demos so i'm going to create a file here called hacking demo and then i'm going to come back here to the main.dart and i'm just going to grab all this stuff and cut it out and paste it over here under hacking demo i'm going to import material then i need to import some file system stuff import let's see import flutter processing sure import file selector just bringing back all the imports and let's see what's the problem here i think we have to hide so whenever we deal with images we get the image widget out of material but that's not the image we want we want image out of dart i o so you have to like hide one for the other you have to either hide the image in dart i o or you have to hide the image and material otherwise the names conflict and so now we have this complaint about image right here but oh sorry i i said dart io i meant dart ui so this image here comes from dart ui that image is a bitmap image whereas this image here from material is the image widget but they have the same name so they conflict let's go back to main for now i'll import hacking demo just to make sure everything's working i'm going to save that now we're already running this here so i'm going to do a hot restart and see if we get any errors okay everything seems to still be fine what we've done now is we've opened up a space right here where we can put any screen that we want hacking demo is out of our main.dart file it's sitting off in a different directory let's create a file called app screen and we're going to create a stateful widget called app screen we'll import material now we may or may not be able to get away with a stateless widget we are going to animate so there's going to be an animation controller it's possible that if we're careful we don't actually need to call set state but i think sooner or later this app screen that we're building here is going to want to call set state it's going to want to maintain internal configuration that changes over time so i'm just going to make it a stateful widget and assume that we'll eventually need it i'm also going to comment what this class is the term app screen is not particularly clear but there's no good name for this screen that i can come up with so this is a screen that displays one of many demos multiple demos are available in a drawer not a great description but it should tell you something about what it what app screen means and we can always improve that documentation comment later for now i'm going to take in a child widget nice i say for now because if we have a drawer the point of the drawer is to change our content right but just for the moment to keep things working we're going to take in a child and then in our build method we are going to return widget.child therefore this widget really does nothing right now but it allows us to come in here and say app screen pass and hacking demo let's organize imports save and just to be sure to do a hot restart everything remains the same okay so now we have a screen around this page it's not displaying anything but it gives us a place to create our drawer let's go back to app screen first we just want to get the visuals working here i don't really care about getting selection working yet um this is a common practice start with your visuals and once you get the visuals working then connect it to whatever other information that you need if we're going to have a drawer the drawer needs to sit on top of the content if one thing is sitting on top of another in the z index that's a pretty good indication that what you want is a stack now i'm going to generate a column because this list doesn't have a stack in there but i'll generate a column and then i'll just rename it to stack and now we have a stack and then on top of the content i'm going to say build drawer which we will return from a build drawer method so what do we want what is this drawer what is this widget that we're returning for the for the drawer well it's going to have kind of two pieces too it's going to be one thing but it's going to have two pieces first i want a little bit of overhang here this is not going to be an invisible or hidden drawer i want this drawer to show itself with a little overlap here on the left side and i want a little arrow icon that the user can tap on to open the drawer so let's make that happen first we're in a stack we want to be as tall as the screen and we want to be anchored to the left side of the screen we can accomplish that with a positioned widget so let's return positioned we want to be top zero bottom zero this makes us as tall as the stack and then left zero we'll eventually change that left parameter to animate the drawer but for now that'll do and then our child will be a i was going to say container i think we actually want material because the icon that we show is going to be an icon button so first let's make sure we have a piece of material so that doesn't complain and then we will have a sized box which is going to establish the width and for the width we want a drawer overhang width i'm going to make that a constant so let's i'm also go let's also deal with the color here color i want a drawer overhang color let's go make both of these things constants up here see drawer over hang color const color and let me check my notes here for what i actually want these colors to be okay i think for this color we're going to go with a all the colors we're going to be dealing with today are going to be dark grays this one in particular is going to be 3333 as a hex value and then we want the drawer overhang width and that let's go with i don't know 48 pixels see did i misspell that oh okay can't figure out how to make a double from an int a consistent issue that i wish somebody would fix and let's save that all right notice over here on the left that we have this gray overhang thing i now want a an arrow chevron here in the vertical center of this overhang i'll say child of the sized box we will use a center i will say child from there will be an icon button and then the icon will be icons dot chevron right on pressed for now i'm going to leave blank doesn't really matter let's save that all right you probably can't see it but it's here and it's black so we could just set the color directly but probably for this entire drawer we're going to be dealing with a dark theme and so we might as well just change the theme entirely so around this positioned actually i guess i'll leave the position where it is so that the position is the direct child of the stack but then around the material i'm going to create a theme widget and then for data we're going to say theme data dot dark let's save that and now the icon is white you can tap it nothing happens yet but we have the icon at least okay now let's deal with the other side of this drawer to the left of the overhang we want the actual drawer how do we get something to the left of the overhang well we can use a row so we'll take this sized box right here with the width and we will surround that with a row and we will put to the left of it another sized box with a width of drawer width i need a constant for that we're going to go with 250 pixels and then well see so here here the material that's not a great place now because this is going to apply the same background color maybe that background color is fine well let's we'll deal with the color in a second when we get there first let's deal with the fact that this is now going to look like an open drawer okay i guess we can go ahead and get the animation working before we even deal with what is in the drawer we want this drawer to begin mostly off-screen with just the overhang then when we tap the arrow we want it to animate out to this position and then when we tap anywhere outside the drawer or when we tap this arrow again we want it to go back to the left let's make that happen we're going to need an animation controller and that means we're also going to need a single ticker state provider mixin so that we can run the animation controller we will initialize the animation controller in init state we also need to always dispose of controllers and then down here for positioned we can make the left position equal to the animation controller dot value times the drawer width so when the value is zero our actual ssi is incorrect we need to start in negative territory so we need the value minus one so that initially it's actually negative one times the draw width let's save that oh gotta do a hot restart so we actually create the animation controller okay so now we're back to where we expected to be let's create a couple methods in here let's say open drawer and close drawer for open drawer we're going to say animation controller forward and to close drawer we're going to say animation controller reverse then we come down here and when we tap on this icon first let's let's switch the icon as needed so if the drawer is closed if the drawer is anything other than open i think technically there are four states there's closed opening open closing i don't really want to deal with two of those i would like to just switch the icon one or the other because i don't really care enough to worry about all that so what we will say is animationcontroller.value is greater than zero then we're going to show a chevron pointing to the left i sure wish my autocomplete would hurry up today chevron left otherwise chevron right now so we're going to switch the icon also in on pressed we're going to say if animation controller let's say let's say only if we're not animating then if animation controller is greater than zero uh we are going to say sorry the value of the value is greater than zero then we're going to say close drawer otherwise we're going to say open drawer and really this right here should be in a method called toggle drawer and since toggle drawer calls the other two let's declare that above the other two and there we go now technically that will run the animation but we will see that it does not solve our problems nothing happens and that's because we are not rebuilding our widget tree when the animation runs to solve that problem i don't know if this is going to work actually but i'm going to try to wrap positioned with a builder not a stream builder but an animated builder [Music] let's see if that complains because positioned is no longer a direct child seems to be all right hey there we go look at that [Music] so now we're rebuilding our drawer as the drawer animation changes which causes that left position to change 60 times per second now i don't like that it's a linear animation i would like for it to start and stop more slowly like it has some mass to it this is called ease in ease out what we want is a curve there are multiple ways that we could apply an animation curve i'm going to use something called a curved animation after we create the animation controller we will then create a curved wow my autocomplete is so slow today animation controller curve we want curves dot ease in out that's easing in both directions but that still isn't enough because remember we we go forward in the curve to open the drawer but we are reversing the curve on the way out so actually i take that back if this was just ease in or if it was just ease out we'd need two curves one curve going forward one curve going back but this curve might be symmetrical the same going forward and back so we actually might be fine but if you ever need the other direction reverse curve is available you could use a different curve in reverse let's save that now uh so draw animation we need to use it still so down here in animated builder we're going to pass animation drawer animation and we're going to use more importantly we're going to use drawer animation.value down here the animation controller works as a clock to run the animation but the drawer animation is what actually decides the exact pixel value that we have let's do a hot restart and let's see what it looks like now well it's still there's a there's a little bit of difference in the speed there i wonder if because we're in debug build and we're running this expensive processing sketch over here if we're just kind of a little janky on the animation doesn't matter we have a curve applied we can change the curve later to a different curve and change the timing now we pretty much have the drawer itself except one thing i want to be able to click out here and close the drawer so here's what we're going to do for that uh what would be a good name for that thing [Music] build drawer tap to close i don't know and here we're again going to need an animated builder [Music] because we only want this thing to intercept taps when the drawer is open we are going to return a gesture detector whose child is going to be a container whose width is going to be as wide as possible whose height is going to be as wide as possible and whose color is going to be transparent now in theory all we should need is a sized box but i seem to run into issues where the gesture detector won't detect a tap on a sized box i have to actually give a transparent color i've tried changing the behavior of the gesture detector it never seems to work i don't know if that's intended or if that's a bug or what but we're going to have a container with an explicit transparent color we are only going to have this though we're going to say if drawer animation dot value is greater than zero then we're going to have that otherwise we're just going to have a sized box that won't show anything and it won't interfere with gestures let's save that open oh we need to actually respond to the gesture on tap we're going to say close drawer [Music] save that close open close open close open close open close open close okay cool so we have a drawer we now need the content in the drawer for the time being the number of options that we have in this drawer is not going to fill the drawer vertically and i would like some decoration above i would like to vertically center the demos in the drawer and i would like some decoration above and below the options so that it's clear where they begin and end and for that i'm going to use a package that draws angled lines all down the drawer as kind of a background pattern so let me go find that package real quick and then i'll be right back okay here's the package it's called patterns canvas and it can draw a whole bunch of different patterns we're going to take advantage of diagonal stripes thick here's how this package works it's not necessarily how i would design the api but it is what it is so we're going to use it how we're going to use it we have to define a custom painter we have to so we extend custom painter we use this pattern thing and then we use a custom paint widget let's make that happen i'll just actually copy this as it is right here we'll come back into our code for the app screen we'll come down here and paste that code i will call this let's see drawer pattern painter i also actually forgot to add this to the pub spec so pub spec yaml and is this the correct one i think it is and then yes this is patterns canvas what's the version oh 035 patterns canvas 035 save come back here get dependencies whenever you're ready i don't know why this thing is not seeing that dependency okay now it's there all right so diagonal stripes thick this is actually already the pattern that we want this wrecked is incorrect we want to paint the entire size so uh we need to pass a rectangle apparently apparently passing size is not enough or i think it's going to paint everything so a quick way to create a rectangle is zero offset.0 and size and then we also need to implement should repaint and because we're not going to take any parameters in we don't ever need to repaint unless the size changes in which case flutter will repaint automatically now we're going to use this custom painter with a custom paint to create the background for the drawer here is build drawer and then here is the actual drawer part of the drawer we're going to surround this with a custom paint and we're going to use the drawer drawer pattern painter save and i guess let's hot restart just in case nope still not showing what is the problem is it possible that this has a height of zero we we can say row cross axis alignment cross axis alignment stretch make everything as tall as possible that was the problem now i do not want this pattern i want different colors and i want thinner lines so let me go check my notes here and see what i want for this okay let's come back in here let's come down to the custom painter for the background color what i want is a color of 2a 2a 2a and then the foreground i want a color of three zero three zero three zero and then there's this parameter called features count which in this case is i think going to be the number of lines and we're gonna go big and go a hundred save that there we go now we have what i'm looking for it um it may not be easy to see on your screen but there is there are a bunch of very thin light and dark gray lines which adds some texture and if you're running this in person it will be clear what constitutes a group of list items versus what constitutes this background now we're into menu territory which is how do we represent a menu we can model that ourselves pretty easily so in here we are going to define a class called drawer menu item every individual demo item will have an associated drawer menu item in the in a group we're going to allow a possible icon if you want to pass one we're going to require a title and we're going to have an optional subtitle we'll support all of those now we're going to add something else to this definition later but just to get the visuals working again this is all that we need we'll create a constructor make it const make these named that's not required that's required okay i don't just want a drawer filled with a bunch of the same kinds of items i want groups i want a group for the coding train i want a group for my work maybe groups for other things in the future and so that means that we also need a demo menu group and actually now that i see that let me rename this this is not a drawer menu item this should be a demo menu item the group is going to have a title and then the group is going to have a list of demo menu items we'll take both of those into a const constructor wouldn't it be cool if when you generated a constructor it generated above your your properties and with named parameters like almost everything ends up being at the end that'd be nice that'd save time over and over and over and over again all right so with that what is a menu well a menu is a list of demo menu groups our screen instead of taking in a child should take in a list of demo menu groups we'll call that menu and we are also going to require i'm trying to think if we want to offer control over the very first menu item that's active let's see how it works out okay and now in our screen we can't just show widget.child we now need um well to do this and i said we're going to do the visual stuff first didn't i so let's not break that let's let's put the child back where it was we'll break that in a minute first let's just render this thing uh what are you complaining about did i misspell something what's the problem here oh semicolon not a comma all right let's come back to main where we instantiate the app screen and notice that we're broken we're broken because we aren't providing a menu and we aren't providing the initial item to display i'm going to create a method here which is going to build a menu which is demo a list of demo menu groups the reason i'm putting this in a method is because if i were to define this as a const or a final property it wouldn't be recreated during hot reload it would only be recreated during hot restart which is more destructive i'd rather let it refresh every time in production there's no need to put this in a method if it's constant but for development ease of development i'm going to put it in a method so that the method gets called every time the widget tree is built we're going to return a list of demo menu group wow it's incredible how slow the autocomplete is today first we're going to have my stuff and then we're going to have the coding train and for my stuff let's see we will say demo menu item come on come on okay title will be hacking demo and then we'll just grab one of these coding train examples for now so let's say demo menu item title [Music] star field then up here in our build method we will say menu equals create menu then we will say menu is equal to menu and we will say initial active item is menu 0 items actually say we can say menu.first.items.first that's the first item in the first group save that okay i think everything is fine there we'll do a hot restart just in case everything seems to be okay so now we want to get the visuals for group headers and for individual items so let's start with the group headers come back here to app screen down here we build the drawer i'm going to create a method a build method for each header and for each list item because we're going to want to call them repeatedly so first we're going to say build menu group header and we're going to require a title we're going to return a container we're going to give it some padding just on the just on the vertical i'm going to go 8 pixels on the vertical we're going to give it a color to contrast a bit with the background this is going to be i think even darker than the background than the pattern background then we're going to actually show the title with text so put title in there and just in case it wraps which it probably won't but if it does we're going to go text align dot center and then we're going to give it a text style with a color that is a medium gray for the text which should still contrast fine with the very dark background but it will be darker than the rest of the list item so it doesn't draw too much attention now with this group with this menu group header we want to come back up here to the drawer here's where we're building our drawer and it probably makes sense to break this down as well let's have let's have widget build drawer content and build drawer [Music] overhang let's come grab our overhang which is right here build drawer overhang gonna return that from right here and then here is our drawer content and in our drawer content we have this sized box which is as tall as the screen and it's drawer width wide we want to center our content when there's not enough to fill the drawer so we're going to start with the center widget but we have to keep in mind that eventually we're going to have more demos than we have available space which means we need to scroll so we will introduce a single child scroll view whose child will then be a column which gives us vertical orientation we're going to make that column as short as possible main axis size dot min and then the children of that column are the groups and the menu items so we will say for final group in widget dot menu we're going to return multiple things but among them will be a build menu group header where we will say group dot title all right let's see we have something broken uh we need to say return okay let's save that all right or we're somewhere um these need to be stretched but then we do have our two headers here super declarative and the coding train so in this column we will set the cross axis alignment to stretch all right now we have full width now i would like for the end of each group to have a little sliver have a divider and then a little sliver or i guess well the divider can be added by the list item but i want i want a little light gray area at the bottom of each group that clearly separates it from the next group let me again figure out what color i want for that okay so at the end of the group i would like a container with a height of 24 and a color of 30 30 30. let's save that all right so now you see you have that little solid color area at the bottom of a group and now we need the individual list items in the group below the build menu group header let's create a method called build menu item which will take in a demo menu item and let's what do we want to return here we're going to want a list tile for now i'm going to ignore the icon in the subtitle easy enough to add later but i'm just going to worry about the title we're going to have text the text is going to be item dot title and then let's see what do we want the style of that to be i guess we can use whatever default style is but i'm going to set the font size to 14. a little smaller than usual and then we're going to want an on tap but we won't implement that until a little bit later okay build menu item what are we going to do with that so here we build the group header here we build the bottom of the group but for every group we also need to loop through the items and here again we're going to return multiple things because not only are we going to build the menu item but we're also going to create a divider where the height is going to be one and the color is going to be 2a2a2a let's save that all right check it out so we're making some forward progress here we have these two list items but they're showing through to the background and we don't want that so we're going to come back down to build menu item and i tried previously i tried setting the tile color but that did not work see if i say colors.black let's see if that sets it this time no see it doesn't change the color i don't again i don't know if that's a weird intended behavior or if that's a bug but what we're going to do instead is we're just going to say actually we can say decorated box whose decoration will be i think can we do const i don't know let's find out box decoration color and let me see what color i wanted for that i think i want const color 30 30 30. so just like just like the little part at the end of each group okay and now we have the lighter color we don't see through to that pattern background anymore so you can see see this is why i put the pattern background in there again i don't know how much of this is visible on your screen but if you run this in person it's very clear that this area at the top and the bottom are not not a part of the menu and the menu is this part at the vertical center so now the only piece of functionality remaining is that when we tap these list tiles they need to change the content that is displayed on this screen now we're going to go break that child behavior but first the question is how is that supposed to happen when we tap on a list tile what is supposed to happen that changes the content what we're going to do is we're going to come down here to demo menu item and not only is it going to know how to display an item but it's also going to include a widget builder that knows how to build the content i guess we can call it maybe page builder maybe that's a good name and we're going to require that when the user taps on a list tile we know the demo menu item associated with it and then we can run the page builder which builds the page which then becomes the content of the screen let's come back to main you'll notice that we have red squigglies here because we are not providing our required builder for the first one the page builder is going to [Music] return the hacking demo come on come on come on any time that the autocomplete wants to hop in here and then down here for demo menu item star field this one we're gonna have to go make an adjustment because i did not give if we come in here to star field it's just called home screen again why because this had its own entry point so it was its own whole app but now i'm going to rename this to coding train star field demo i'm gonna come back to maine we're going to say coding train s star field demo that allows us to compile but it still isn't going to do anything because we aren't taking advantage of any of these builders yet let's come back to app screen we no longer want child there's not one piece of content there's many pieces of content we'll get rid of the child down here in our build for the screen we have widget.child that's not going to work anymore instead we are going to we need to hold on to the currently selected list item we're going to say active list item dot page builder and then we're going to execute it like that but active list item doesn't exist yet we'll come up here to the top of our state object and we will say late what i i said this is the demo in fact we don't need to we don't technically need to store the list item that's fine let's store the list item there's the active list item and we will create it in init state we'll say active list item is equal to widget dot initial active menu item okay so let's keep the naming consistent i called it a menu item there i'll call it a menu item here okay and then so first let's make sure that works for the initialization case get rid of child initial active menu item that's where we're passing it in it should be the hacking demo again let's save okay it's complaining about stuff let's do a hot restart actually there's something red down here i guess huh this should be a comma okay save let's do a hot restart we're back we're seeing the same thing but this content came from the the list item the active list item not the child property so this should mean we're working but we still can't change the content okay i tap on that nothing happens because we haven't hooked up the on tap callback yet let's create a method in here let me get this out of the way we're going to say what should we call this maybe activate or we'll say show demo and we'll take in a demo list item i did that again demo menu item and then we will say set state active menu item equals menu item and i guess we can also as a little performance improvement we can say if menu item is not equal to active menu item we'll only set state if they're actually different so that if the user sits there tapping all day on the same item in the drawer it's not going to keep repainting everything for no reason now with that show demo we need to call it so when we build the drawer when we build the drawer content when we build the menu item on tap we're going to say show demo and we're going to pass in the item that we tapped on let's save that let's do a hot restart just to make sure everything's clean all right we're back here star field boom hacking boom star field boom all right and just like that we have a completely custom animated drawer that allows us to switch between our different demos now let me just reflect on a few things while we're here some things to keep in mind about flutter if you were to implement this some of you probably would have used a scaffold with a regular drawer and there's nothing wrong with that but i wanted one thing i wanted to show you is that you do not need to rely on the the built-in drawer and built-in scaffold it really didn't take much at all for us to create our own version and i don't even know if it's possible to get an overhang like this using a scaffold drawer i would guess probably not or you might have to hack some really weird you might have to do two different widgets that are totally disconnected your drawer and then the overhang you might have to make those separate widgets and then listen to the the drawer controller so unnecessarily complicated in that case but also if you were to use the scaffold to create this it would automatically use the navigator like notice when i tap out here to close the drawer to close the drawer in a scaffold you have to pop the route off the navigator here we didn't even have a navigator because i don't care about navigation i don't need to bring in route names and routing and route animations i don't whatever i don't care i have a screen where this is just part of my screen and it can be open or closed if i wanted to create if i wanted to make this part darker when it's open i could we have full control the point is we didn't need the navigator so involving routes would just be unnecessary complexity that might get in our way so you know don't be afraid to create your own tools like this just because flutter does it one way doesn't mean you need to start from first principles what do you actually need and what's the simplest way to accomplish what you need i think this is pretty simple and it accomplishes what we need now let me tell you what's going to be coming next i'm not sure what order i'm going to do these things but one thing that i'm thinking is that as much as i love the color yellow and all the things i do on this channel are yellow when it comes to dealing with graphics you want the ability to have either a low contrast or high contrast background either i might want to have a dark gray background to kind of blend in with the star field or i might want a white background to contrast with the star field there's no reason to force people to have a yellow background so i'm thinking about adding some little buttons down here in the in the center of the screen that will allow you to change the background color i'm also i think i'm going to put a toolbar over here on the right side of the screen that allows you to take screenshots generate multiple frame screenshots and record a gif animation by clicking various buttons and telling the telling having little pop overs that say where to save it and i may even create some zoom controls that allow you to zoom in on your sketch it won't like it'll truly be a zoom it won't like the sketch is not going to paint more pixels we're just going to enlarge the sketch i may or may not do that but i'm thinking about it so that's probably going to be the next few videos i also i'll probably create a custom a custom ui for the coding train so that we can clearly show the thematic difference between the coding train and everything else and pay some homage to the coding train those are a number of videos that will probably be coming in the near future the point is to get this example app as useful and looking as good as possible before i consider it to be essentially done and and from there we'll move on to maybe having some of you contribute to the project once the example app is all where it should be all right so that's all for today i hope you learned something i hope you're giving this a try if you know any instructors that would perhaps benefit from this tool please do start to let them know we are pretty much at the finish line here at least for kind of a mature alpha version so i'll see you in the next video where we continue working on this example app and then eventually some of you can contribute to the project as well see you later [Music] so [Music] thank you [Music]
Info
Channel: SuperDeclarative!
Views: 528
Rating: undefined out of 5
Keywords: flutter, widgets, mobile-apps, web-apps
Id: oz5WzMLcP8I
Channel Id: undefined
Length: 56min 44sec (3404 seconds)
Published: Thu Sep 02 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.