Intro


Have you ever arrived at work at an irregular hour and had to count when you’re supposed to leave for home? Ever had a shorter lunch break and was supposed to leave the workplace earlier that day? Have you ever tried to hit a time goal at a [park, gym, library, etc.] ..? …You get my point. It would be nice if an app orchestrated that for us. So that’s the problem we’ll be solving.

Oh, you know what? Neumorphism is trending right now, and we haven’t tried it yet. You’re probably thinking

Say no more! Our task is clear - combine the two into a location-based time tracker and give it an appropriate touch of neumorphism in the User Interface! 😲

…and you’re right! That’s what’s coming up next.

The Recipe 📚


Food advertisements have mouthwatering images, yet we still buy food recipe books as it’s not that straightforward to reverse-engineer the process from the cover.

So I’ll tell you what - I don’t have a plan yet, but I have a pretty accurate idea of how I want it in the end!

Well, kind of. Here’s the straw-man proposal:

Straw-man proposal sketch

💡 The app should track time at a designated place (e.g. user’s workplace) regardless of user arrival time and notify the user when the desired amount of time is spent that day at that location.

For instance:

  • The user adds a specific location to the Time Tracker app
  • The user sets time spent on-site preference to 7 hours
  • The user arrives at the location at 9:30 am
  • The user leaves the location at noon and returns after a 2-hour lunch
  • At all times, the Time Tracker app should indicate the current time progress
  • Time Tracker app should notify the user at 18:30 they’ve fulfilled their time goal
    • Since the user was present at the location:
      • 9:30-12:00 (2.5h)
      • 14:00-18:30 (4.5h)

Coming up with the Main Ingredients List 🥦 🍅 🥜

Flow design sketch
Geocoding

To protect users from having to spin the globe manually or navigating to their desired place from their current location, we require to provide them with an input field to find their desired address. Therefore, I’ve settled for Google’s Geocoding API, which will take a user query and return a list of metadata-enriched addresses to us.

Geofencing

One option to detect a user entering a specific location is to poll GPS for the user’s current location repeatedly; however, this is problematic:

  • Privacy: such high-frequency polling provides app excessively detailed information about the user’s location
  • Energy consumption: GPS has to be active very often, clever optimisations have to be implemented manually and might be error-prone

Instead, Geofencing seemed like the right tool for the job. Unfortunately, there was no reliable free, open-source, & actively supported Geofencing Flutter plugin available at the time, but there was a great effort by Ben Konyi. His proof of concept plugin accepts a circle (latitude + longitude + radius) to register the geofence and broadcasts to us as the user enters or exits the registered geofence boundaries.

Progress Status & Notifications

For cases where users want to check their progress at a particular place proactively, we can use an in-app place dashboard. Once the set goal is complete, we want to inform the user even if the Time Tracker app is not running (even if the user closes the app in the “recents” tab). So we’ll use platform notifications to display the appropriate message we want.

Cooking it all up 🥧


By combining the features mentioned above, I’ve enabled the user to:

  • Search for the desired address in free-form text
  • Fine-tune the specific location after geocoding lookup on a map or locate the device’s current position
  • Tweak the geofence radius to set the preferred trigger proximity
  • Add the selected place to the Time Tracker dashboard
  • For each of the locations:
    • Inspect the intraday geofence entry & exit times
    • Customise the title
    • To further adjust the geofence radius
    • Choose what kind of events should be shown as alerts (platform notifications)
    • Select preferred Daily Time Goal
    • De-register the geofence by removing the tracker altogether
  • Bonus: Colour Theming

Interactive demo app - Try it out 🧙


Download Android APK (arm64-v8a)

See it in action instead 👀


Lessons learned


  • Packages are there to help with neumorphic design
    • The Neumorphic design elements implementation was done by myself from scratch using gradients and shadows since I wanted to learn more about this concept and how to build it from scratch.
      However, packages like flutter_neumorphic can save you a significant amount of time, whether you’re into tinkering with neumorphic designs or adding it to your production app for good.

  • Geofencing on its own is not sufficient for location-critical systems
    • This implementation never polls for user location & lazily relies on the system to broadcast the fence breaches. Despite being energy efficient, it may miss events or report the entrance and exit through fences incorrectly when near the border. Thus, more sophisticated situation evaluation may need to be incorporated to deliver a precise & consistent user experience. On the other hand, it can still serve as a “nice to have” additional feature when things don’t solely rely on it to function.

  • Geofencing requires very dangerous permissions
    • By ’very dangerous’, I mean that OS may remind users up to daily that your application has such “excessive” permission and suggest they limit access. The fact that ’All the time’ precise background location permission is required means it’s harder to convince users to allow it in the first place (e.g. on Android 12, users must manually turn such permission on in the Settings). So it’s harder to both acquire and retain this permission.

  • With the foreground of the Flutter app closed, updating database data, showing notifications, etc. can be achieved using Dart code background execution, Isolates
    • We can receive system broadcasts (e.g. geofence triggers) on the platform level at any time and trigger a call to custom Dart Isolate that can trigger a pre-configured static Dart function to receive a callback & execute the required code.

      💡 Read more about the geofencing plugin’s mechanism involving isolates on the very detailed Ben Konyi’s Medium post.

      Though one has to be very cautious - not even static entities are shared. Entities in different isolates (for example, the Flutter main UI Isolate & another Background Isolate) ⚠️ do not know ⚠️ about each other. So when “sharing” static instances, some synchronisation mechanism may be required (since there will be as many instances as isolates).

      For example, SharedPrefs/database reference in the main Flutter app isolate may serve old (cached) data if another isolate changed the underlying data on-disk through their own reference & no re-sync broadcast mechanism is implemented across them.

Noteworthy Ingredients 🧂


Geofencing

Plugin: FlutterGeofencing fork

Underlying Native APIs:

Native API Guides:

Other Plugins

Opportunities for enhancement


Critical

  • The ability for a user to modify entry & exit times
    • I match up subsequent geofence Entry and Exit events to form TimeBlocks. As geofencing proved not trustworthy, you sometimes get multiple Enter or Exit events in a row. Since the application cannot always correctly recover from this due to missing data, it would be helpful for the user to have the ability to modify entry & exit times

Nice to haves

  • Accommodate ellipse-shaped locations
    • Some locations, such as parks or golf courses, will not follow a shape of a perfect circle. Thus, configuring a tracker for such a place may be inaccurate or interfere with nearby tracker locations. Adding support for composite trackers consisting of multiple geofences could improve usability significantly

  • Set the preferred end of day time
    • Currently, the Daily Time Goal resets at midnight, which can render it less compatible with the lifestyles of the night owls. The ability to set custom start & end times of a day may help to mitigate that

It’s a wrap


Thanks for stopping by!

Follow me on @morphingcoffee to stay in touch

Hourglass art

Attributions


Big shout out to @AndreiEdouard and @MBLExperiment for feedback & help testing 👏

Unsplash Photos by Alexander Andrews and Wilhelm Gunkel