Flutter & Web – Unite your code and your teams (DartConf 2018)

[APPLAUSE] MATTHEW SMITH: Well thank you I am Matthew Smith, and with me is John Ryan And we are from Portland You can probably tell by my metro logging shirt, which feels out of place a place up here But we’re going to talk about how we have maximized our code sharing between mobile and web thanks to Flutter So a little bit about us– we are a PaaS company for developers And our goal is to make enterprise software easier to build, support, and deploy We have customers in the commercial space, customers like Wayfair and Taylor, higher education, some great universities like Yale and Stanford, and we are also in the public sector We have research labs and some municipalities that use our software So all of these customers, including about 100 others, are going to be going live on our latest platform, which will include the Flutter application The first customer going live is going to be Taylor They are a 90-year-old manufacturer of restaurant equipment And this application will be used in about 150,000 stores in North America, including like McDonald’s and Wendy’s So this is a big deal for us So I’m going to talk a little bit about our path to Flutter We started back in 2012, and we wanted to be mobile focused And so we wrote our iOS app in Objective-C. And in 2013, we wrote our Android app in Java We then rewrote our iOS app and Swift, and we rewrote our Android app in Kotlin And then in 2016, we wanted to add web to our platform, so we chose Dart for that And now in 2018, we’re happy to say all of our clients are written in Dart, and we’re in the process of releasing that So why did we start with native coding? We started with native coding because, for one, we needed or applications to work offline And this is a big deal for our customers They’re down in vaults They’re in manholes, all kinds of situations with no network And so this was a must have And because they’re working offline, and they’re dealing with enterprise data, they have millions of records So they’re down there and they need to search hundreds of thousands of parts, and those parts need to be available, cached offline And so that’s a lot of data for us to deal with And then we have to deal with security and regulatory compliance And we felt that we could handle this best with native But this did create a problem for us For each client we built, it took us about 18 months to build And then, when we did those rewrites, it took us about nine months to rewrite them And on top of that, it’s very difficult to hire and train developers on iOS and Android, and especially if you’re trying to cross train them So our solution to this was let’s build engineering teams for iOS, for Android, and for web As our product got more sophisticated, the time to develop new features increased And we were developing those features, of course, three times So this problem just continued to get worse as we start to grow We were adding features We were trying to catch up And at the end of 2016, we kind of looked at our platform and we thought, well, we’re about a year behind of getting these all up to feature parity And so this led us to why we chose Flutter When we wrote our web client, we really fell in love with Dart And we saw Flutter We were part of the community We had been watching it And we were excited about it But we found that breaking up is a really hard thing to do We invested a lot of time and a lot of money into our clients And this was a tough decision, but Flutter made it much, much easier And we still get to see Swift and Kotlin on the side for plug-ins We also found that, for enterprise software and for us, native coding for each individual platform is just not sustainable So what we did internally is we agreed to a six-week project to kind of test the waters of Flutter and make sure some of the big questions that we had could be answered And after just three weeks, we had a functional Flutter client working Obviously it wasn’t production ready, but it was enough to prove to us that this is the move we need to make So we found, when we started doing our Flutter implementation, that most of this code was already complete

We had written our web client We had just finished that And basically what we were doing was going in and writing views for Flutter And because the hot reloading stuff that you just saw, building UI in Flutter is extremely fast And so we didn’t quite predict how fast we could build out our UI And now we’re going to get into how we went about sharing our code So John’s going to come up and talk about some of our techniques JOHN RYAN: Thanks, Matt So Matt mentioned that three week number And it’s important to explain where that number came from It took a longer effort over a period of time to kind of massage the code base and get it to a point where we were ready to implement our Flutter apps And when we thought about this, we really have tried to follow three simple rules to do this We used layers So we thought of our application as a layered system We tried to take as much important logic out of the views as possible And we also tried to inject dependencies into our entry points for our application So depending on what app starts up, we can inject those in So let’s talk about what we mean by layers here Well, this is an example of a non-layered architecture It’s a simple Flutter widget that you may write if you’re testing things out It queries Hacker News and it calls setState when you get the result But the problem with this is that you can’t share that Hacker News request if you’re trying to use it on web as well So each layer, in our mind, can use the layer below it And, in general, it tries to implement or interact with the other layers in an abstract way meaning they don’t concern themselves with the implementation You may also want to do this for other reasons The primary one is to target web in Flutter You can swap the layers out You can use a different view for Flutter, and you can use a view for web And you may just want to minimize your dependencies between them and think about the dependencies of your application and understand each layer separately This lower layer, what we’re talking about when we mean this are things like Firebase things like JSON over HTTP, web sockets, and things like caching on a local storage or file system on Flutter So package:http also allows us to write our services in a cross-platform way It’s a cross-platform HTTP request library So our Hacker News service asks for a generic HTTP client And then, when our app starts up, we know what client to give our services On browser, we can use a browser client And on Flutter, we can use an IO client And this is awesome Our middle layer is where we’re going to be managing our state And it contains all of our domain-specific logic And it interacts with the other layers in an abstract way It doesn’t concern itself with the implementation Here we have a fancy controller, and it asks for a view, and it asks for a service And it’s important to note here that you can use whatever architecture you want The important thing is that it’s separate from the other layers At this top layer, we obviously use Flutter, and we use Polymer on our team, but you can use any view implementation you want And it also becomes easier to switch between React and Angular, for example There’s a few patterns that we found in our codebase that we use a lot We think of our app in a model view presenter way usually, where the view knows what it needs from a presenter, and the controller can implement that interface Or we have a view model that is a shared way of communicating state changes and updates with its view Or a view that implements an API that is very clearly defined For example, a view just emits events and knows how to render itself What we mean by having a simple view is that it doesn’t have as much logic as you might put into a view on other platforms And it implements an abstract interface on Flutter and web So this process of moving code out of your views is just a matter of finding the logic you want to take out, moving it into the controller or into a service, and hopefully writing a test as you’re doing that As you’re doing this, you may find that you’re moving code you didn’t intend to move In this example, we have a bunch of dependencies

These are library imports And one of them is importing Dart HTML, and we can’t import Dart HTML in Flutter One way to quickly diagnose this problem is to build a test that implements your controller So as you’re moving code into your controller or your other state management tool, you know instantly whether you’ve moved something you didn’t intend to move And you get a little error here saying, this code right here is importing Dart HTML And that’s the problem And you may just want to do this because you want faster unit tests Or you want to build a command line tool that interacts with your services in the same way that your app does Or you may just want a cleaner library and package structure that you can talk about with your coworkers OK, so that’s rule two is to keep your views simple The third rule that we noticed in our code base is we inject a lot of dependencies and we just do this in a simple way We have an app controller And app controller knows what it needs to do its job In this case, it’s calling a service and it’s caching that data And it doesn’t concern itself with how the service or cache is implemented So the two rules here are pretty easy to follow but hard to follow in practice It’s just about asking for things and avoiding things like singletons OK, so after all that what you end up with is, hopefully, a slightly better architecture that can be used cross-platform You have your services at the bottom that use a cross-platform HTTP library or some other library And for things like shared preferences, you have an app that has some thing the user wants to save for their use case, And you want to use the shared preferences plug-in on Flutter And you may choose to implement that interface with local storage on the web You have your middle layer handling, hopefully, your complex logic and your state management And you have your view layer that is implemented using the same interfaces And it becomes very clear the relationship between these layers So let’s go through an example A big problem for a lot of companies, unless you’re at Google, is deciding where to get lunch So we solved this problem by building a voting app Our voting app is going to have some state it needs to manage It’s going to have a few pages that it’s going to show to the user A user might create an election, enter their name, submit an idea, wait for other people to join that election and vote, and then show the results So to do this, we can break our app into separate packages We can have our shared logic in our Let’s Vote package We can build our Flutter package And we’ll have our web package Our Flutter and web packages will just import our shared package In our shared package, we’ll have some basic types, like an election, a voter, an idea And each class will know how to serialize to and from JSON, using the JSON serializable package Now we need to build our services This is an example of our shelf server that’s serving our rest end points So we need to build a service for this Our service will use package:http and implement methods like Create So this will create an election And this can be shared cross-platform This controller layer will manage our state And it will have a reference to the view and service that it needs to do its job A quick side note here, this is an example of how we can test our controller because we’re using dependency injection So we can mock a view, mock a service, and call init and verify that we’re doing the right things OK, so that’s our controller layer And for our view layer, we need to wire up that two-way relationship between the view and its presenter, the controller So we’ll call init in our app controller, and that’ll expect an app view Then, in our Flutter app, we can just call things like controller startbyCreating, which will call the presenter and create the election On web, we can do the same thing So to make that work, we implement our app controller using various pieces And in this case, the only piece that’s different is the client, the HTTP client On Flutter we use a browser client–

or a regular client And on web we use a browser client And you can see the components here re implementing AppView It’s implementing that interface And on Flutter it’s implementing that state object The state object in implementing the view Once we do this, we can fix cross-platform bugs So as we were doing this, we actually wrote a bug That’s very easy to do We sorted the voters, or the votes– we sorted the ideas by the vote, and we messed it up It returned the list in low-to-high order So we need to reverse the list to return the actual winner, not the loser OK, so let’s see a quick demo In this demo, we have our web version running and our Flutter version running We’ll create an election And this will call our REST services using the same shared service It will call Create So Eric is going to enter an idea for where to get lunch All the state management in this application is managed by the same controller class Now Somebody on the Flutter app is going to [INAUDIBLE] the wrong code And once they enter the wrong code, the controller knows that and can tell the view, “I want you to show an error.” And on Flutter, you call showDialog Now they’re going to enter their name, submit their idea– sizzle pie And now the voting can occur, and we can determine a winner All right, so problem solved [APPLAUSE] So following these three simple rules really helped us think about how to do this in a way that was really effective and get to that three-week number that we showed The app is hosted on GitHub You can actually use the app if you go to that URL on the right And it’s running on a single instance, so like most voting systems, it’s also not secure Be nice to it You know [LAUGHTER] MATTHEW SMITH: All right, so we are fairly new to this, but we’ve learned a lot of things so far that we thought we should share The first thing is we’ve been able to share about 70% of our code between web and mobile And this includes our tests But this is a big number, so we were really excited about this Going back over our development timeline, iOS and Android back in 2013, ’14, ’15, we weren’t sharing any code I have 15% up there because we had some idea sharing between iOS and Android because Kotlin and Swift were fairly similar, and we were using Realm as a cross-platform store And we were using RX, and so you can copy some Swift code, and paste it in Kotlin, and massage it, and hey, you know, share some of the same ideas, but obviously not real sharing Now in 2018, 70% real sharing, and all of our clients are in Dart So going back to the time it took us, it took us 18 months for each platform And a lot of that was we were developing new features as we were trying to catch up our platforms that we’re developing So it’s not like– it was a catch-up game the whole time Because we started building Flutter so fast though, what we decided was all new features go in Flutter only And at the speed we were developing, we knew that was viable for us So now with our latest release, there’s no more catch-up We’re done with that game, which is very– it’s a big relief And also, 70% is a great number But it’s not the amount of code that you’re sharing that really matters It’s what code you’re sharing And what we’re sharing is the most mission-critical code It’s the stuff that’s dealing with security, and persistence, and talking to our services And it’s where you’ll find most of your bugs And so it’s also the code that is the most expensive to write It takes your most senior engineers And it’s the most expensive to maintain We also learned that hot reload is the real deal So everybody sees the counter going up and the counter going down as you change your code on the web site And that’s super exciting, obviously But we have built a big application

on this with a lot of state and a lot of complexity And we use this every day, and it has made a world of difference in our development So much so that we develop all of our features on Flutter first because with hot reloading we can write it so fast, and testing is so fast that all we’ve got to do, when we’re done with Flutter, is go back and implement the view on web So it’s really, changed our approach to this We are developing features at three times the speed we were before, for obvious reasons We’re not writing it three times And this is a real number Our features are of higher quality because we are testing better, we’re all reviewing each others code, and we’re all thinking about the problem in the same way And we’ve reduced our QA time significantly And a lot of this is because, when that logic is all contained in the controller, before– an update was made, you’ve got to test something in iOS, you’ve got to test it on Android, you got to test it on web And now we can communicate to our QA people that this fix was made at the controller layer There’s a good possibility it’s fixed across all platforms And this has really sped things up And probably the best thing about all this is that the three teams that we’ve had are now one big Flutter and web team, all writing Dart And so this has been one of those things that sounds great, but you just don’t know how much– it’s just so exciting to have everybody on the same page We sit down and we plan a feature And everybody is equally involved There’s no miscommunication between teams And we’re all thinking about the problem from the same perspective So this has made us very happy developers And our team’s very happy And so we love it We wrote some more information about this on our blog, so you can check that out We wrote a Maps plug-in, and also John has a code sharing write up that he did along with some of the changes it created in our business So that is it Thank you And definitely check out Flutter It’s great [APPLAUSE]