By: Daniel Griesser
Once a year we let our imagination go wild for a whole week during our annual Hackweek event. It’s where we come up with product updates, like dark mode support, design them and implement prototypes.
The mobile engineering team came up with the idea for a Sentry mobile app that focuses on Release Health. We wanted to give developers a concise but comprehensive view of whether a release a release was healthy, errored, or experiencing abnormal crash sessions across multiple projects.
Because we wanted to bring this to both iOS and Android, and we only had a week to build a proof of concept we decided to build the app in Flutter. Oh by the way, we also have a Flutter SDK, it’s great.
We fetch all projects from all your linked organizations that have session data. Every card represents one project and shows the name, organization, platform and a graph with the total session data over the last 24 hours. Below the cards the sessions are split up into healthy, errored, abnormal and crashed, all with individual graphs. There are also two cards showing crashfree sessions and users in percent.
In the settings screen users can view a list of all projects and favorite them. Those will show up first on the main screen. Apart from that, we show the usual suspects, like terms, sharing and logout, in the settings screen.
Flutter takes the main concept of view rendering from react. The user interface is described using widgets which are derived from the applications state. When the state changes, the system creates the new widgets from it and only redraws the interface elements. You can head over to the widgets intro documentation to learn more about this.
We use Redux for state management. There is a central object holding the global application state. When an event in the application occurs, let’s say a reload of the session data, an action is dispatched.
This action then goes through multiple middlewares. In this case, it would go through our API middleware, where it would trigger a network request. This request would then itself trigger new actions when it (asynchronously) finishes with success or a failure. All those actions travel through other middlewares as well and finally reach the reducer.
The reducer is the only place where the global app state is mutated. In the sample above it would set a loading state when started and replace the loaded data upon success, or set an error state when there was a problem we want the user to know about.
• When state changes, widgets can be updated automatically. With the help of ViewModels implementing BLA, the Widget will only be rendered if the derived state changed. Neat!
• With the reducer it,s explicit where state is mutated
• The global state tends to get large quickly, even for such a small application. As the app will grow, we will have to think about how to best split this up.
• Some Flutter API relies on Futures which does not yet play well with the Redux concept. One example of this is the “RefreshIndicator” widget.
Of course we use our own Sentry SDK to check for issues. This was especially helpful during development, as we started out without sound null safety (more on that below) and were able to catch many issues with our SDK that would just show up in the console and could go easily unnoticed otherwise.
The prototype used a dependency with embedded ECharts for chart drawing. This made use of web views, so every chart we would draw would also embed a full blown web view into our widget hierarchy. We would quickly encounter performance issues as well as flickering UI when redrawing.
This was a great way for us to get started during hackweek, as we use this JS dependency in the Sentry web application, but it was clear to us that we would need to replace this for the production app.
We decided to draw the charts that our designers prepared for us in the Figma design ourselves with the help of the canvas API. Android developers will feel right at home with this API, as it is basically the same on Android. Flutter also uses Skia as the drawing library, so no surprise there.
So in just a couple of lines of code we were able to draw the charts that our designers came up with, including gradients and cubic lines (which we removed again). Drawing was faster, pixel perfect and we could also get rid of a large dependency.
We mainly used Android Studio with the Flutter plugin for application development. This is especially convenient for Android developers, as they can use a familiar environment when starting out with Flutter applications. Of course, you have the freedom to use whichever editor you are comfortable with.
Fastlane is an open source tool written in Ruby. It is mostly used by the iOS community, but also works exceptionally well for Android. We use it locally to create release builds and ship them to the respective stores, as well as upload debug symbols to Sentry.
We used the Dart analyzer during development. It helped us to keep track of code issues that might come up during development.
We migrated the application to sound null safety during development when we were already pretty far along with the app. Luckily, the Flutter team came up with an incremental way to do this. So we were able to convert file by file and have a working application during migration. I’m sure there are some Swift developers reading this who remember migrating in the early days. Yeah, my deepest condolences, I have been there.
Everything we do at Sentry is built in the open. Find us on GitHub.
We have many ideas for future releases of the application, but ultimately it should be a tool that users find useful. So we can’t wait to hear your feedback, please give it a try!