Recently, I again clamped. I live in Europe, and here instead of fines for improper parking and “clamper” tow trucks, they chain your car’s wheel. To get out, you need to make a phone call, pay a round sum and wait for the guy with the keys to lift the chain. It is long, humiliating and sometimes (depending on the area) predatory expensive.
That day I was late everywhere. Waiting for a worker ringing with keys, I wondered how stupidly I got. I ran in, left the car for half an hour instead of the maximum free 20 minutes - exactly in the 21st minute and got caught. Unlucky, the striped van of the parking lot was not far away, and they instantly reacted. They had been catching me before, for various reasons: I forgot, the paid term expired, and sometimes I just could not find my car in the maze of streets.
“There must be an application for everything,” I thought, and began to delve into the app store. After a heap of dubious results, my confidence diminished, and I decided to clarify: “there must be an android application for everything.” Then he found his huavei and climbed into the bowels of the play store. From there, more debris spilled out on me, and, drowning in clumsy crafts, I spat. Either I'm looking somehow wrong, or there is no convenient and understandable parking tracker. The conclusion is simple: if we don’t have something, let's do it ourselves.
Against the background, I already sawed a long-term construction
, which took almost all my free time. I decided to take a break and in the interval to collect another projection. Having estimated the desired functionality, I imagined a period of a month and a half, and, looking ahead, I’ll say that, in principle, it turned out even faster. The result was a compact and clean application
on both platforms, very convenient. Now I use it myself and suggest you try it.
So what would I want from a parking tracker? What do I always miss when I get a fine? After thinking, I formulated the following introductory:
- Simple. Ideally, one-button. A minimum of distracting functions: no profiles, parking history, support for multiple cars at the same time, etc.
- With notifications. I would not forget about the car, if the application reminded me: bro, you have a parking there by the way, and it is about to expire.
- Consider walking time. This is the simplest thought that I have not seen embodied anywhere. I don’t need to know that the parking will expire in 5 minutes if I walk about half an hour from it. I need to be reminded so that I can get to the car a couple of minutes before the end and leave.
- Route and navigation to the parking lot. Navigation, of course, through Google or Apple Maps, but the route can be shown in your application. This will help if you left the car in an unfamiliar area, the timer ticks, and you run in circles along the same streets.
I chose Flutter as the technological base, since I have been working with it for some time and consider it to be good. For routes I decided to use the HERE API, also not for the first time, but for the map - Mapbox. I sketched out several options for the interface, chose the most minimalistic one and got involved in the work.
In principle, there is not much to tell about the development process itself. The server part is not here, the simplest client, consisting essentially of one large view in different states. On the first page, you either select a preset - 1, 2, 4, 6 hours - or use the slider to adjust your parking period. Having tried it live, I realized that the slider was rude enough, and added the “+” and “-“ buttons to it. A funny and rather unobvious nuance here was that while you choose - time goes on. So you need to update this view in real time. After choosing the term, click on the only button below, and the location and time data are saved, and parking starts. It is important to note that I basically store all the data on board, nothing is transferred to any server. In the future, this, by the way, will create difficulties for me, but more on that later.
The second page is, in fact, the parking timer. I tried a bunch of options here: I started with wildly overloaded prototypes of info, then iteratively got rid of and simplified to the limit. I really wanted the parking status to be displayed visually, rather than dry numbers, so the main element of this page along with the map was a donut chart. It shows elapsed time, walking time and remaining time. If you are not near the car and the walking time is non-zero, a navigation start button appears, and the map shows an approximate route to the parking lot. That, in fact, is all.
Despite the visual simplicity, this screen pretty much ruffled my nerves. It must be understood that while parking is active, a number of processes occur simultaneously:
- time goes by and the timer runs out
- you move and travel time changes
All this happens in real time and should be instantly displayed on the interface. The chart should be smoothly rebuilt, the map should move and redraw the route, if there is a risk of being late or parking expires, the corresponding warning should also pop up. Moreover: when you close and open the application, you need to restore all this correctly. And finally, the scary: all the functionality should be able to work in the background, because the application will spend most of its life in the background. So it goes.
I will not describe the implementation in detail, I can only say that the flutter work with the states is very worthy. Where in a pure android an overloaded object-oriented design would turn out, it turned out simply and elegantly. Normal understandable states, sensible binding of views to models - in tasks like this flutter shines. However, it was not without typical jambs: for example, the local library of working with maps does not have an animated transition to new coordinates. I had to crawl into her code, pull out some private methods and add my crutch. It is possible that this functionality will be added soon, but this is the whole flutter - with all its attractiveness, be prepared for such discoveries.
Everything else seemed to work out smoothly. Actually, there was not much else: a couple of dialogues, and an intro page for which my girlfriend (she was an artist
) drew nice animations. Finally, I translated the entire application into Russian, including the map. But then the pain started: notifications.
For me, the ability to send smart notifications was key for the application - without it, there’s nothing to make a fuss of. However, when I started working on this feature, I realized that I had stepped on my own foot. There were actually two problems. The first is flutter, which does not know how to work with low-level api devices out of the box: you need to write a gasket in the form of a plug-in that contains two native codes and an abstraction on the dart. The second problem is my fundamental position that everything should be carried out on board. So, we summarize what is needed. It is necessary that the application on the background
- Constantly monitoring your geolocation,
- Checked how many minutes walk to the parking lot
- Checked with expiring timer
- If there is a risk of not having time to return, a notification shot
Feel the scale of the tragedy? It is clear that there is no technical possibility to do this if the application is completely killed. But suppose it is simply minimized, and the screen is off. HERE api gives me the route and estimate of the walking time, does this mean that the application will spam requests in the background? How much code will be run at all? How are we with memory and energy consumption?
I sat down to think. Started with a small one: technically, flutter has recently been able to execute code in the background, it is called isolate. If everything is written correctly, then you can build logic in a specific way so that not all of it runs against the background, but some specific functionality. Indeed, in minimized mode, we do not need to draw interfaces or update states - we are interested in the bare logic of calculating time, and this logic is quite compact and cheap. At the same time, it should not be triggered by a change in geolocation, as I had in the original version, but by some internal timer. Because the notification should fly, even if you do not move, but sit still.
Well, we wrote a lightweight code that can run in the background and is controlled by a timer. How can we trigger it within the framework of native applications and transmit geolocation data there? It turned out differently.
The logic of the android and the iPhone in matters of code execution on the background is radically different. For an iPhone, it is absent as such: the background code can only trigger a system event of a certain kind, for example, playing streaming audio or, fortunately for me, updating the location. To do this, just request permissions and in the native part of the Flater plug-in subscribe to the event. From there, through a specific communication channel (MethodChannel), we call the callback in a dart, pass the geodata to it and start the background with the logic we need. Beauty.
Android works differently. Technically, he has the ability to execute code in the background without being tied to system events - for this you need to describe the service. It hangs separately from the application, either completely in the background or on the forgground (there he makes himself aware of the attached message in the blind). It would seem that the problem has been solved: in the native part of the plugin, we describe a background service that tracks the location and pulls the Darth code. However, there is a nuance. From Android version 8.0, the attitude towards such services has changed: now, if the application is minimized at the moment, its background services are hellishly cut by the axis in order to save energy. And, if your code performs, for example, data backup, then you basically do not care at what exact moment the axis will let it work - the main thing is that it works. But in my case, we need to track the change in location in almost real time, and timing is critical here. Therefore, the only way out is to start the service in forgound. We describe a small notification attached in the curtain, subscribe to the location, through MethodChannel we pass it to the dart. Everything seems to work.
By the way, returning to the issues of memory, energy consumption and the number of requests - the code is run when the location is shifted to 50 meters, or by timer. Moreover, he himself is very light, and the requests do not pass through me, but go directly to the HERE api. I myself do not receive any data and do not save it anywhere. In general, such an implementation is very well described here
, this is the blog of one of the flutter engineers - the code is attached there. His example is somewhat different, but the general approach is clear, so I recommend reading it.
That, in fact, is all. For a month of free time, I got an application that perfectly solves a specific problem. I hope this article will be interesting to someone, and the application will save you from fines and stress. You can look and scold here: Android
Thank you all for your attention.