Sunday, May 10, 2020

Learning Flutter - Part 5 - Building out the UI and Incurring Technical Debt

In my last post, I wrote about the Provider class to separate presentation logic from other "business" logic.

My next step was to build out a UI to allow the user to select a vehicle make and model. At this point, I was only building broad categories (car, truck, motorcycle) and got familiar with gridlines and the various properties of common widgets to build UI:



Building the UI is where hot reload, Dart DevTools, and the screen painting functions (see the 2nd image above) really help. It is really quite quick to change a parameter and see the UI change almost immediately. It took me longer to look for data on vehicles (images, fuel efficiency, wear-and-tear cost estimates) - for the v1 it may be that I only get some basic 'generic' vehicle types like the above.

I also started playing with some icon designs - a few iterations thus far but nothing that looks at all good:




In this process, I've built up some technical debt:

  • Magic numbers/strings within the code (though for UI/layout type items, some of these might be acceptable, for text strings they definitely are not); separating text strings is important both for maintainability and for internationalization
  • Repetition of certain aspects of code (e.g. purple background colours); repetition makes code more difficult to change, for example when adding support for Dark Mode
  • No unit tests or project tests
That said, given the project is quite young, having a bit of technical debt is not terrible and not surprising. Particularly as I'm just starting to work on the UI code, my process is to get it working then get it clean. Some of the technical debt is really just unimplemented features (e.g. the code does not yet support switching between metric and imperial units).

Avoiding too much technical debt is important - if you don't then you'll end up with something that isn't robust and that you can't change without spending (wasting) too much time cleaning up impacts of changes where the impacts really should be isolated.

Looking at the plan, I've made some progress - still lots to do and to learn!
  • Selecting vehicles that are not 'average' which will require:
    • UI to select a vehicle make and model [DONE]
    • Vehicle data (icon image, fuel efficiency, wear-and-tear cost estimates) [STARTED ON BASIC 'GENERIC' TYPES]
    • Code within the model to use the parameters
  • Selecting and saving parameters such as metric vs imperial
    • UI to select parameters
    • Ability to save parameters between runs of the app
    • Code within the model to respond to parameters (e.g. by displaying metric)
    • Ability to pull defaults based on location (for imperial/metric, local fuel prices)
  • Support for 'trips' 
    • Building a UI to start and stop trips
    • Updating the model to enable starting and stopping the GPS
    • Persisting trips to the phone 
    • Viewing past trip data and total data
  • Support for user alerts (as the app will normally be in the background)
    • When the user has forgotten to turn the trip recording off (could be fancy AI but likely if the speed has just been low enough for long enough)
    • Alerts based on cost parameters (e.g. every dollar of fuel cost or after 25 liters -- when you might need to refuel)
  • Changes to the UI to make it look nicer and to adapt to various sized phones
  • Unit tests to help keep the project relatively bug free


Sunday, May 3, 2020

Learning Flutter - Part 4 - State Management and Provider

In my last post, I explored how to pull real-time GPS locations using streams in a simple app.

My next step was to pull the GPS code into the prototype I'd made in part two of this series. To do so required understanding how to manage state within Flutter apps. Fortunately, Flutter.dev's section on state management is pretty well explained and the talk from Google I /O '19 on Pragmatic State Management in Flutter was helpful as well. As both of them recommended the Provider approach, that's the approach I chose.

Implementing Provider required (in addition to adding Provider as an import):

  • Building a class to hold the GPS data and route data separate from the UI as a class that extends ChangeNotifier
  • Putting a ChangeNotifierProvider at the top of the widget tree in main.dart
  • Adding a Consumer in the builder that builds the distance display in the UI
None of the points above were too difficult. 



Once I'd connected the pieces, I needed to link up the points to calculate distances (which worked but only in production - not in testing - see the next paragraph). Once I could calculate distances, I needed to hard-code some parameters into the model to calculate fuel use, fuel cost, wear and tear cost, and carbon footprint. 


In building the model class (that held the GPS and route data) I tried to build a unit test. Unfortunately that did not work so well as the locationprovider package uses platform implementation to implement the distanceBetween method. So my unit tests were failing given that when I called them, the platform GPS had not been initialized. The code itself worked but the test cases did not. I did learn something about how the unit test framework in Flutter works though, which was useful.

I also added a FloatingActionButton to reset the trip to zero. What the project looks like thus far is below:



At this point, I'd say that I have an alpha version. The core functionality is there but it needs a lot of work and polish to enable use in a variety of circumstances (e.g. by selecting vehicles that are not average, to automatically decide on metric vs imperial and let the user change that, to allow for stopping and starting separate trips, and by persisting these data points). The UI also needs to look nicer and work on a variety of screen sizes. I'll also want to enable alerts to the user (as the app will normally be in the background) to tell the user when they've spent a particular amount of money (total or in gas costs). 

To break the work up into bits of functionality to add:
  • Selecting vehicles that are not 'average' which will require:
    • UI to select a vehicle make and model
    • Vehicle data (icon image, fuel efficiency, wear-and-tear cost estimates)
    • Code within the model to use the parameters
  • Selecting and saving parameters such as metric vs imperial
    • UI to select parameters
    • Ability to save parameters between runs of the app
    • Code within the model to respond to parameters (e.g. by displaying metric)
    • Ability to pull defaults based on location (for imperial/metric, local fuel prices)
  • Support for 'trips' 
    • Building a UI to start and stop trips
    • Updating the model to enable starting and stopping the GPS
    • Persisting trips to the phone 
    • Viewing past trip data and total data
  • Support for user alerts (as the app will normally be in the background)
    • When the user has forgotten to turn the trip recording off (could be fancy AI but likely if the speed has just been low enough for long enough)
    • Alerts based on cost parameters (e.g. every dollar of fuel cost or after 25 liters -- when you might need to refuel)
  • Changes to the UI to make it look nicer and to adapt to various sized phones
  • Unit tests to help keep the project relatively bug free
Eventually it might make sense to put a server back-end of the app but I don't think that would be in the v1.0 - not that a back-end would not be useful (you could see stats/cost online and not lose data when you upgrade your phone) but I think it will be a useful app without server functionality. 


Sunday, April 26, 2020

Learning Flutter - Part 3 - Pulling Real-Time GPS Location and Updating the UI

In my last post, I laid out the design of a prototype complete with a paper drawing 'spec'.

The next step was to understand how to access (and update in the UI) real-time GPS location data using Flutter.

First, I looked up what the Google recommended method of accessing device GPS data was. Google recommends a package called geolocator which is maintained not by Google itself but by a team at Baseflow (my preference is to have to depend on fewer third parties to reduce security risk but the little checkmark icon baseflow.com at pub.dev indicates at least that it "Published by a pub.dev verified publisher" whatever that entails).

To understand and get started, the geolocator page recommends an article called Flutter: Getting a User's Location with the Geolocator Plugin which shows you the steps to configure and use location services:

  1. Importing the package in the pubspec.yaml file (like for every other package)
  2. Setting the permission requests for location data (which is unique to packages that want GPS access on the iPhone or Android device)
  3. Adding the code into the app to pull and use the GPS data

What the article did not show was a simple code snippet on how to pull real-time GPS location data (the code with the article gets location upon the user clicking). The geolocator package has an example with a section on getting a stream of locations but for someone new to Flutter I wanted the pare it down to understand the code a bit more.

Before understanding the example in geolocator, I needed to go back to fundamentals as I'd not yet learned about streams, async/await, and futures in Dart so I took a detour over to the Dart codelab on Asynchronous programming: futures, async, await to understand the language a bit better. As it turns out, most of that is hidden in the example created but it is a fundamental part of the language so doing the codelab was useful.

Once I'd done that, I pulled the example code into the Flutter default app and pared it down to a very simple app in a single dart file that displays on the GPS location data pulled in real-time:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import 'dart:async';

import 'package:geolocator/geolocator.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(title: 'Real Time Location Demo App'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  var _location = 'No Location';
  var geolocator = Geolocator();
  StreamSubscription<Position> _positionStreamSubscription;

  @override
  void initState() {
    super.initState();
    const LocationOptions locationOptions =
        LocationOptions(accuracy: LocationAccuracy.high);
    final Stream<Position> positionStream =
        Geolocator().getPositionStream(locationOptions);
    _positionStreamSubscription =
        positionStream.listen((Position position) => setState(() {
              _location = """
LAT: ${position.latitude.toString()}\nLNG: ${position.longitude.toString()}""";
            }));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Text(_location),
      ),
    );
  }
}

The code above creates a stream of Position objects and sets up a listener that calls setState to update a String variable that then gets redrawn into a Text widget. 

Once you run the app, the geolocator package will trigger the request for location data (of course, if you say no or your device (or virtual device) doesn't support GPS then the prototype won't work):



Once you've allowed, the app will display real-time GPS coordinates:




If you change the line of code that updates the string displayed, you can also add speed and heading:

40
41
42
43
44
              _location = """
SPEED: ${position.speed.toString()}\n
HEADING: ${position.heading.toString()}\n
LAT: ${position.latitude.toString()}\n
LNG: ${position.longitude.toString()}"""




Of course, with the code above you don't close your stream subscription (you don't even use it) so head over to the geolocator example section where this code was pulled from to show a more complete example along with other code you'll need to actually use this functionality in an app.

Going forward, I'll need to update the location code into my prototype app, use the speed and time between GPS updates to calculate distance travelled, and from there calculate cost, fuel use, and carbon footprint. To start the parameters will be hard-coded but eventually the app will ask the user to select a vehicle and pull fuel cost and perhaps temperature based on location to much more accurately calculate fuel cost, fuel use, and carbon footprint.

Saturday, March 21, 2020

Learning Flutter - Part 2 - Configuring and Designing a Prototype

In my last post, I went over the start of my Flutter learning journey.

Since then, I've installed the Flutter SDK and VS Code, watched the tutorials over at fluttercrashcourse.com and started into a Udemy course on Flutter.

A few initial thoughts on the language are that first it has annoying layers of braces and brackets ({}{[[},,]]) but if you can get past that, it is a C-like language so isn't too difficult to get started with. On the Flutter SDK, the hot reload feature is useful when it works and the tools in VS Code in terms of formatting and looking up definitions of functions are quite useful.

Learning a mobile app is best demonstrated by actually building an app and one of the first things in building an app is to draw it out on paper. What is below is a very rough drawing (that was a collaboration with my 7 year old). The point is not to make it pretty but to quickly sketch out the most basic design.




Deciding what is enough for the MVP (Minimum Viable Product) and for a prototype is also key. For this app (a tool to show the cost - financial and carbon cost - of driving), the prototype would need to calculate costs based on distance travelled as measured by GPS. The prototype will not to allow for any parameters (type of car, metric/imperial, saving, manual override, etc.) but will just provide the basic functionality of providing a cost for an 'average' car.

With that, the next step is to create a mock-up in the app of what it will look like. It's not too difficult to make something that doesn't look too bad (running on a simulator of an iPhone 8 below):


Going forward, the mock-up will need to be made updatable (it is static now), figuring out how to pull data in from the GPS to calculate distance travelled, then setting some parameters (hard coded at first) to calculate cost, finally to update them real-time when the app opens. At that point, the app will be usable (though not useful). 








Sunday, March 1, 2020

Learning Flutter - Part 1 - Gathering Learning Resources

In my previous post I outlined my plan to learn Flutter (and Dart).

In preparing to get started, I've been looking into various learning resources.

The Flutter Website is a great place to start. The documentation if pretty good and there is both a language tour and a style guide that are useful to peruse:
https://flutter.dev/
https://flutter.dev/docs/
- Language Tour: https://dart.dev/guides/language/language-tour
- Dart Style Guide: https://dart.dev/guides/language/effective-dart/style
- Flutter Install Guide for Mac: https://flutter.dev/docs/get-started/install/macos

Google also has some Code Labs to help learn Flutter - I've not tried these but have taken a quick look and may do so as I start my hands-on learning:
Google Code Labs: https://codelabs.developers.google.com/?cat=Flutter

Youtube also has some decent Flutter materials. I'm well into the first one and may look through the other ones. As is my habit, I've been watching on the train to work:
- Flutter Full Tutorial for Beginners (freeCodeCamp.org and his website https://fluttercrashcourse.com): https://youtu.be/pTJJsmejUOQ
- 44 Short Flutter Videos: https://www.youtube.com/playlist?list=PLUbFnGajtZlX9ubiLzYz_cw92esraiIBi
- 35 Short Flutter Tutorial Videos: https://www.youtube.com/playlist?list=PL4cUxeGkcC9jLYyp2Aoh6hcWuxFDX6PBJ
- Dart Programming Tutorial (1 hr 40 min): https://www.youtube.com/watch?v=Ej_Pcr4uC2Q
- Crash Course on Flutter (5 hrs 44 min start of an Academind course): https://youtu.be/x0uinJvhNxI

There are a few Medium pages with text intros/posts/articles that seem worthwhile along with some Example Sites with working code. I've only taken a brief look but these look like very useful resources for learning more advanced concepts and understanding what Flutter can do
- GetHub Awesome Flutter examples: https://github.com/Solido/awesome-flutter
- "Flutter Awesome" Website: https://flutterawesome.com
- List of apps built in Flutter: https://itsallwidgets.com 
- Medium Flutter Landing Page https://medium.com/flutter
- Another Medium Flutter Landing Page https://medium.com/flutter-community

Rounding out the learning materials are a Flutter Book (with Kindle download available free) and a Tutorials Site. I've downloaded the book but have not started reading it. I've looked at some of the tutorials briefly but have not tried any of them.
- Flutter Succinctly Book: https://www.syncfusion.com/ebooks/flutter-succinctly
- Flutter Tutorials Handbook (several small tutorials): https://kodestat.gitbook.io/flutter/

My likely approach will be to build and learn in parallel. What I mean by that is that I'll continue to tackle the above in parallel with starting into actually building an app. I'll definitely go down some blind alleys and develop some code that's not great but by building in parallel I'll be able to apply learnings immediately which will be better both for my learning and for my building. 

If there are any great Flutter (or Dart) resources you think I've missed in the list above please let me know. Now onto some more Flutter learning! 

Sunday, February 23, 2020

Learning New Things - Deciding to Build a Mobile App

I've wanted to get back to building for fun (outside of work). I've always enjoyed software development and making useful tools. So I'd been thinking for a while about creating something that might be somewhat useful to build as a good and relatively simple mobile app to build.

Recently, I settled on building a mobile app that will measure how much gas I'm using when I drive and the cost of that gas. Something that would start out as just a very simple enough app (with some GPS/mapping, basic UI, inputting or pulling gas prices from a web service) but would be significant enough to be a project over several months (given I have a very demanding job and two young children). I also wanted a project that could be useful and could be extended (with carbon footprint, incorporating maintenance and insurance costs, etc.). The driving cost tracker app seemed to fit the bill.

In building it, one of the first choices was to pick a framework. I found the video below a good introduction to the trade-offs involved:



While I installed XCode and learned a bit of SWIFT recently, the idea of a cross-platform framework that speeds development sounded appealing as did learning a new language. So I've decided to choose the Flutter framework (and thus learn the Dart language). 

I'll be posting here as I get to learning and building my app - feel free to leave any comments about any of my design or development choices as I build!