Moving to Medium… Last post ;o(

I can no longer upload to my WordPress site without resolving the issue which I suspect is caused by a change Ionos my website host has made. When I went to contact them they now require me to ring them and that was the straw for me.

I’ve been posting the articles to both WordPress and Medium, which is extra work will little payback as the WordPress site only gets a few hits a month and the articles on Medium get 100’s of reads each week, the down side is Medium is subscription based, but I would recommend you at least try it.

Here is the link and good luck with your Flutter projects:

https://medium.com/@simbu/flutter-lets-go-95924ee6db28

https://medium.com/subscribe/@simbu

SimBu

Flutter – Lottie Animation

A picture paints an thousand words, an animation more…

A Lottie is a JSON-based animation file format that enables designers to ship animations on any platform as easily as shipping static assets. They are small files that work on any device and can scale up or down without pixelation. https://lottiefiles.com

I want to start with a typical case where we are waiting for something to load, in this case the animation will repeat and let the user know we are working on their request.

I’ve abstracted this into a new widget in our DigestableLego library called DlLottie.

In order to allow designers to test the look and feel of any Lottie animation, I’ve added a Knob to the Widgetbook use case that accepts a Json string.

Lottie Json Knob
Lottie Json Knob

If you are new to Widgetbook you can find links to the posts and a quick start guide below.

Ta Da

Lottie Animation Widget
Lottie Animation Widget

Xp

Quick Start

Just download the component library DigestableLego.

Open in Visual Studio code and run the following two launch agents Visual Studio Launch.

Builder launch agent
Builder launch agent
Widgetbook Launch Agent
Widgetbook Launch Agent

Then download any Lottie Json files https://lottiefiles.com and just copy the contents and paste it into the knob:

Lottie Json Knob
Lottie Json Knob

Links

Flutter – Widgetbook (Storybook) – Part 2

Widgetbook (Storybook) – Part 1

Just a follow-up to part 1 to talk about Knobs.

Knobs allow the users of WidgetBook to supply their own values and see the changes the values make to the widgets displayed.

For the list widget I added knobs to allow changes to list item title, subtitle and selected image.

Ta Da

Knobs to specify Title, subtitle and image for a list item.
Knobs to specify Title, subtitle and image for a list item.
Tabbed Layout
Tabbed Layout

BackBurner

I only moved one more widget iPhone tabbed layout widget to DigestableLego.

You can already see the difference, DigestableMe is getting simpler to use again. I will those remaining over the course of the next few blogs, it just takes time to do it right.

Xp

Knobs

.knobs is an extension on the build context:

@WidgetbookUseCase(name: 'Avatar, Title and Subtitle', type: DlList)
Widget dlListAvatarTitleAndSubtitle(BuildContext context) {
  return buildItem(DlListItem(
    leading: DlAvatar(
      imageOptionsKnob(context),
      radius: 22,
    ),
    title: Text(context.knobs.text(label: 'Title', initialValue: "Title")),
    subtitle:
        Text(context.knobs.text(label: 'SubTitle', initialValue: "SubTitle")),
  ));
}

You can specify multiple images for the user to choose from:

Image imageOptionsKnob(BuildContext context) {
  return context.knobs.options(
    label: 'Logo',
    options: [
      Option<Image>(
        label: 'Flutter logo',
        value: Image.asset("images/flutter.png"),
      ),
      Option(
        label: 'Widgetbook logo',
        value: Image.asset("images/widgetbook_logo.jpeg"),
      )
    ],
  );
}

See the Widgetbook documentation for details.

Simplifying DigestableMe startup

Removing code, simplifying and finding clarity, always feels good, it’s something worth fighting for.

There is a tendency for complexity to creep in organically.

As part of the process of separating and moving widgets, I got the chance to clean up and strip away some complexity from application providers.

Application Providers before the clean up.
Application Providers before the clean up.
Application Providers after the clean up.
Application Providers after the clean up.

This puts us in a good position to start work on the application flow on startup.

Excluding unused import warnings on generated files

Turned out that the syntax to exclude a file from the analyser analysis_options.yaml was a comma separated list of files:

Exclude widgetbook generated file from the analyzer
Exclude widgetbook generated file from the analyzer

So back to no warnings now we have excluded the file generated by WidgetBook.

No problems in the project
No problems in the project

It is important to target no warnings or errors b4 checkin, once you let it slip then its harder to spot issue you introduce.

Shared Stuff

Some of the widgets I moved into DigestableLego used provider state e.g.

Providers in widgets
Providers in widgets

Too avoid the issue of creating shared classes required by both DigestableMe and DigestableLego I removed the provider state and added the necessary information to the widget constructors.

Widget data now in the constructor
Widget data now in the constructor

It then meant that I need additional mapping code in DigestableMe to map its types to those in DigestableLego:

New mapping files
New mapping files

Which are just simple extension methods on the types in DigestableMe.

extension TabItemMap on List<TabItem> {
  List<DlTabItem> toDlTabItems() {
    return map((item) => DlTabItem(
          label: item.label,
          icon: item.icon,
          content: item.content,
          globalKey: item.globalKey,
        )).toList();
  }
}

Now we have a new rule in the Done Done Done checklist.

No provider state in widgets, pass in the required info on the widget constructor

As part of this I removed provider: from pubspec.yaml in DigestableLego.

Removing the shared state simplifies the widget development and widget tests.

There will be more to come on this but we just need to avoid coupling things together, we don’t want to create monoliths.

The whole point of microservices is that they are independent. You can create a service that has countries (for example so that you can display a list of available countries), but I would avoid binding that service to your other ones. Copy the country information into those other services. This allows these services to evolve independently. For example, if a person has nationalities that are at a finer grain model than buildings country (because subtleties like that really happen in the real world).

Services can call other services, coarser business services should caller finer-grained ones, but do not make a network, make sure that you strongly define your hierarchy, and make high-level services call low-level ones. I don’t think “Building” or “Person” services are those coarse-grained services, you probably want a layer above them that is integrating those with preferences. Building and Person probably should be on their own, but Microservices design is not an exact science, and a lot depends on your situation. If User Preferences is deeply integrated into the behavior of Person, it might make sense to make Person your coarse-grained service. Just make sure that it’s an explicit decision, and you don’t do something horrible like make them co-dependent on each other.

Finally, do not create a “Shared Stuff” assembly. It will become your Monolith in very short order, and will bind everything together into an unruly mess. You will end up with all the disadvantages of a monolith, combined with all the overhead of microservices. Rob Conklin

Sound & Vision

Moby Essentials
Moby Essentials

Power is not shared, power is taken

Links

Flutter – Widgetbook (Storybook)

Component driven design is a great way to safe guard your brand/s and make it easy to quickly develop new screens and UI experiences from existing components, or to build new ones.

It was made popular by Storybook.js for good reasons:

  • Prevents the same components from been rebuilt in different parts of the UI.
  • Provides a toolbox of components, a bit like Lego.
  • Components can be built and modified in isolation so they are not affected by flakey date, business logic or unfinished UI’s
  • Makes it easy to focus on difficult use cases
  • Allows for collaboration with the design team and other stakeholders

Flutter is less mature than React and there is no clear alternative to storybook.

After a little research I selected Widgetbook because it is been actively developed and used.

Flutter gems storybook

Widgetbook
Widgetbook

And followed their instructions set it up in the new components project DigestableLego.

Ta Da

Widgetbook in action

Xp

Widgetbook Approach

I decide to use the widget book generator rather than the manual approach to avoid having to setup:

  • Categories
  • Folders
  • Widgets
  • Use cases
Widgetbook Manual Configuration Example
Widgetbook Manual Configuration Example

When you use the generator you just need to write the use cases and annotate them.

@WidgetbookUseCase(name: 'List', type: DlList)

There is a little more upfront, you need to add config for the Widgetbook application and for themes using two other annotations:

  • @WidgetbookApp
  • @WidgetbookTheme

Widgetbook Setup

I added Widgetbook to the /example project because it needs the macOS applications files to run and it meant that the examples created by the package code be reused by the scenarios.

I started by adding the package references for the generator recommend by the Widgetbook.

Then the App widget and annotated it with @WidgetbookApp.

// Flutter imports:
import 'package:flutter/cupertino.dart';

// Package imports:
import 'package:widgetbook_annotation/widgetbook_annotation.dart';

@WidgetbookApp.cupertino(
  name: 'Digestable Lego',
  devices: [
    Apple.iPhone13Mini, Apple.iPhone13, Apple.iPhone13Pro, Apple.iPhone13ProMax
  ],
  textScaleFactors: [
    1,
    2,
    3,
  ],
  foldersExpanded: true,
  widgetsExpanded: true,
)
class CupertionApplication extends StatelessWidget {
  const CupertionApplication({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

And the first use case:

// Flutter imports:
import 'package:flutter/cupertino.dart';

// Package imports:
import 'package:digestable_lego/widget/dl_list.dart';
import 'package:widgetbook_annotation/widgetbook_annotation.dart';

// Project imports:
import 'package:example/dl_list.dart';

@WidgetbookUseCase(name: 'List', type: DlList)
Widget listItemRecipe(BuildContext context) {
  return const CupertinoPageScaffold(
    navigationBar: CupertinoNavigationBar(
      middle: Text(
        "Example List Items",
      ),
    ),
    child: DlListExample(),
  );
}

Next I ran the builder:

flutter pub run build_runner build

// Optionally to automatically rerun when files change
flutter pub run build_runner watch

That generated the .widgetbook.dart file.

Widgetbook files
Widgetbook files

Then just run it:

flutter run -t lib/widgetbook/cupertino_application.widgetbook.dart -d macos

To make this step easier I added it as a new launch agent.

Widgetbook launch agent
Widgetbook launch agent

Widgetbook Themes

To support themes in widget book I had moved them to the DigestableLego package, they could potentially become their own package if the code and assets grows.

Upgrade a Git package

Whenever we make changes to DigestableLego and want to use those changes in DigestableMe, we need to update the package from the git repository.

To do this run

flutter pub upgrade digestable_lego
pub upgrade Digestable Lego
pub upgrade Digestable Lego

BackBurner

MaterialApp support for Widgetbook

The Widgetbook application runs widgets in a CupertinoApp, you get to choose as part of the application config.

@WidgetbookApp.cupertino

I would like a version of Widgetbook for Android devices, Ideally widget book would allow a dynamic switch of application types.

There does not seem an easy way to make @WidgetbookTheme work with both Material and Cupertino Apps in the same application so even if I have two @WidgetbookApp configurations each picks up all the @WidgetbookTheme themes and then one configuration complains that the ThemeData is the wrong type.

Excluding unused import warnings on generated files

Unable to ignore unused import warnings for the widget book generated file cupertino_application.widgetbook.dart

I was able to exclude other linting rules in the analysis_options.yaml file.

ignore:
      - lib/widgebook/cupertino_application.widgetbook.dart

Sound & Vision

One flew over the cuckoo's nest

Links

Flutter – Packages

The number of files in the project has grown to a point where it is more difficult to work with. I know because I now need to use ? + P to find files rather than the file explorer.

It now makes sense to start to break up the solution into packages to help make it easier to understand and maintain, and help to ensure each part has a single responsibility.

I started by pulling out the UI widgets as a precursor for creating a storybook to show case them to the stakeholders.

To do this I created a new project called DigestableLego and hosted it on GitLab.

I then moved in the Listview and Listitem widgets, built the package and included it back in DigestableMe Application.

Ta Da

Digestable Lego: List item examples
Digestable Lego: List item examples
Using the DigestableLego list widget in the project.
Using the DigestableLego list widget in the project.

Xp

Creating and using a package

Create package.

flutter create --template=package digestablelego

Create a new Git repository.

git init
git add --all
git commit -m "initial commit"
git remote add origin https://gitlab.com/simbu-mobile/DigestableLego.git
git push -u origin master

Add code and examples to the project.

Check Package is ready to publish.

flutter packages pub publish --dry-run

Once the code passes the checks, check it in.

For now we are going to use it directly from the Git repository e.g.

dependencies:
  digestable_lego:
    git: https://gitlab.com/simbu-mobile/digestablelego.git

It is likely that in the future I move to a privately hosted pub.dev repository to get the version and update benefits. You can find more details here:

Application Types

We are now up to four:

  • MaterialApp
  • CupertinoApp
  • FluentApp
  • MacosApp

They reflect the different design systems/guidelines, Material on Android, Google, Linux and the Web, Cupertino on iOS, Fluent on windows and MacOs on macs.

Currently we are only targeting iOS & Android and extending to the web, so that narrows it down to two:

  • MaterialApp
  • CupertinoApp

For simplicities sake I’ve now started to move away from device detection and use the application type to differentiate in the widgets that require cross platform support.

import 'package:flutter/cupertino.dart';

extension BuildContextExtension on BuildContext {
  bool isCupertinoApp() {
    var cupertinoApp = findAncestorWidgetOfExactType<CupertinoApp>();
    return cupertinoApp != null;
  }
}

// And usage in the cross platform widget

return context.isCupertinoApp()
              ? const CupertinoListTile()
              : const ListTile();

NB: findAncestorWidgetOfExactType is powerful stuff and has a number of uses.

Testing & Development Rhythm

Packaging the UI widgets has given me a moment of clarity with regard to test types.

I would now expect widget and unit tests in the DigestableLego project and feature and unit tests in the DigestableMe project.

Whilst there could possible be the need for widgets tests in DigestableMe it is unlikely, so we shall see how that pans out.

Just looking to build a development rhythm e.g. new UI, build widgets in DigestibleLego and showcase, agree on look and feel and style. Then move to API data side calls and mapping etc… Finally run through the Dev done checklist. More on this later on.

Sound and Vision

Better call Saul (Netflix)
Better call Saul (Netflix)

Links

Flutter for Android – Layout

The code for this article

We are going to focus on adding a native layout for an Android phone.

At this point it makes sense to make a broad distinction and go with a MaterialApp for all devices and platforms except iOS devices.

We can add the web and desktops platforms later if need, all platforms are stable now that Flutter 3 has been released.

This blog covers:

  • Device detection.
  • Adding a material app phone layout to the layout selector.
  • Supporting material styles.
  • Deciding on the components needed to give a native Android phone experience.
  • Creating adaptive wrappers for widgets with platform specific implementations.
  • Going test first with Gherkin feature tests and using them to confirm the correct application is run for each device and the appropriate layout is used.
  • Improving the automation to run and report on the Gherkin feature tests.

That’s quite a lot, but supporting new devices or platforms is not trivial.

Thankfully we don’t have endless options, but they do slowly change, folding phone and folding tablet usage is increasing and you can now change the application experience based on the hinge angle.

I wanted to able to easily add features tests and build the application test first through them, to do this I started by adding more automation, for running and reporting on the feature tests.

The next step was to create and automate the story to use a material application when we detect an android device.

Example map for Platform support feature
Example map for Platform support feature

In order to make the feature test pass I made some improvements to device detection on startup.

To cater for the different application theme implementations, I moved the theme state up into some new classes and wrote some extension methods to convert them into ThemeData for Cupertino and Material applications.

At this point the platform tests passed for both platforms but the recipe feature tests only passed on the iPhone and I needed to start work on swapping out platform specific widgets.

To do this I have created cross platform widgets where platform specific implementations are required.

The main difference is that we are replacing the tabbed navigation on iOS with a draw navigation for Android.

Android side draw navigation
Android side draw navigation

Once that was done I turned my attention to the content and AppBar.

With the CupertinoTabScaffold you passed in a list of widgets that had an app bar and content.

With Android you need to scaffold the full screens and pass the same draw navigation to each.

The Cupertino scaffold preserves the widget (screen) states when we switched tabs, but the Material scaffold does not, to achieve this I put an IndexedStack at the top level that switches the screens in and out and preserves the state.

Too some degree I’ve shortcut the process by creating three widgets to replace the original DigestTab widget used in the single platform CupertinoApp:

  • CpDigestTab (Selects iOS/Android version depending on client device).
  • IosDigestTab (iOS version)
  • AndroidDigestTab (Android version)

There was a temptation to abstract this more to CpTab with platform specific tab builders, but that would limit the implementations, so sticking with the ‘Simplest’ thing for now, until we know more, have built more tabs.

We are now little bit closer to having a native Android & iOS experience with a shared code base and good test coverage that will let us improve and optimise the code as we add features.

Ta Da

List recipes on Android & Apple phones
List recipes on Android & Apple phones
Display recipe on Android & Apple phones
Display recipe on Android & Apple phones
Android feature test report
Android feature test report

Next

UI Flow

We will be taking a look at the flow when a user starts the application and thinking about when to display the different start screens:

  • Splash
  • Login & Sign up.
  • Onboarding (Help on how to use the app, would be nice)
  • Dashboard

Shortcuts

New section, try to use a new shortcut once a week to make life easier, here are a few for Visual Studio Code on a Mac:

? + P Find/Open a file by name.
? + ? + P Run a command

[? + ? + Folds the innermost uncollapsed region

[? + ? + ]]() Unfolds the collapsed region at the cursor

? + + (K => 0) Folds all regions

? + + (K => J) Unfolds all regions

BackBurner

Native Android experience

We have only just started to implement a native Android UI by having a side draw menu, there is lots more to come as we tack more of the items like the floating action button detailed in iOS vs. Android App UI Design: The Complete Guide.

Add a Recipe on Android

Will add a feature test and Android implementation at a later date.

Api feature tests

The feature tests are just against the UI with a stubbed data source, we will need more feature tests to run against the API when we work on data access.

flutter_gherkin

Report on failed feature tests.

The Json reporter does not write the report when any of the feature tests fail, this needs to be fixed so we can report on test failures.

Inject device into the Tag Builder

If we can inject the device into the tag builder we can filter out device specific, tests, just difficult this early in the chain, needs research. Work around for now is to just make them pass if run on the wrong device.

Device specific feature test step workaround
Device specific feature test step workaround

Emulators

Extend the command line tools to automate running Android emulators.

For now you can just use the launch target to run it.

Launch agent to run Andriod Pixel emulator.
Launch agent to run Andriod Pixel emulator.

XP

Cross Platform Widgets

The key to cross platform work is to raise state up in the providers and then have widgets and adapters to build platform specific UI.

Cross platform widgets are now prefixed with ‘cp’ and they select the native implementation based on the device platform.

Cross platform widget - Bottom tab layout.
Cross platform widget – Bottom tab layout.
/*
  Cross platform Bottom Tab Layout, switches out the bottom tab layout dependent on the device platform e.g. Ios, Android...
*/

// Flutter imports:
import 'package:digestableme/model/config/platform.dart';
import 'package:digestableme/model/tab/tab_item.dart';
import 'package:digestableme/widget/android/tab/android_tabbed_layout.dart';
import 'package:digestableme/widget/ios/tab/ios_tabbed_layout.dart';
import 'package:flutter/material.dart';

// Package imports:
import 'package:provider/provider.dart';


class CpBottomTabLayout extends StatelessWidget {
  final List<TabItem> tabItems;
  CpBottomTabLayout(this.tabItems, {Key? key}) : super(key: key) {
    assert(tabItems.length > 1);
    assert(tabItems.length < 6);
  }

  @override
  Widget build(BuildContext context) {
    var platform = Provider.of<Platform>(context);

    if (platform.isIos) {
      return IosTabbedLayout(tabItems);
    }

    return AndroidTabbedLayout(tabItems);
  }
}

When the number of cross platform widgets increases we can build a storybook of the widgets with design and usage examples.

Theme Changes

Theme files
Theme files

To cater for both the Cupertino and Material themes I moved the state up into a three classes:

  • ATheme
  • ATextTheme
  • AColorTheme

the’A’ prefix is purely too different from framework names.

I then change the theme files for light, dark, Whitelabels to use them rather than the Cupertino theme classes.

The I wrote a couple of Adapters as extensions to convert the theme classes into framework themes, CupertinoThemeData and ThemeData.

Thats good enough for now, but there is still a little discord between the classes and design system which will need clarification, shoring up.

Feature tests – Platforms and devices.

It took me a long time to settle on only having a single set of integration tests that can run on all devices.

I made the decision when device targeted feature tests started to get really complicated and required multiple launch agents rather than the standard one added by the Flutter extension.

Launch agent - Flutter integration tests
Launch agent – Flutter integration tests

The down side of a single set is it is hard to exclude device specific tests:

Gherkin ‘Tags’, partly solved that but not completely.

Platform support feature
Platform support feature
Tag builder function
Tag builder function

I cannot find a way yet to get the device information before the tests are filtered.

So it’s not perfect, but simple enough to work with for now.

Where I can’t filter out a device specific test, I simply make it pass when run on another device, which is information we have once the test is running.

Launch Agent - iPhone cucumber report
Launch Agent – iPhone cucumber report
Launch Agent - iPhone feature tests
Launch Agent – iPhone feature tests

flutter_gherkin

Automation

My first change was to automate the running and reporting. To do this I added a command line project under a new /tools directory and integrated the running of commands into launch.json using the ‘F5 Anything’ extension. It’s named dt.dart which stands for dart tools.

Debugging

You can set break points and debug the feature tests and code, to do this I added a new launch targets:

Launch Agent - iPhone debug feature tests
Launch Agent – iPhone debug feature tests
iPhone debug feature launch agent code
iPhone debug feature launch agent code
Debugging a feature step
Debugging a feature step

Gotcha

You need to specify where to find the features twice in the suite, once for the builder(featurePaths:) and once for the runner(..features =).

Flutter Gherkin config gotcha
Flutter Gherkin config gotcha

Speed

When developing the speed the tests run is critical, once they become too slow to run you stop using them to develop test first, to get feed back from the new code and that’s a big problem.

To some degree you can reduce the number of tests that run during development and run the full suite on integration but eventually you will still face the same issue.

There are two parts of running the integration tests that take significant time.

Launching the iPhone simulator took 7.11 seconds and building the project for the iPhone took 22.2 seconds and that will increase as the project grows in size.

Whilst the tests only took 2.52 seconds to run.

Feature test speed
Feature test speed

I thought I was snookered until I released that you can just launch the project once and then rerun many times.

So for development open the iPhone simulator and then use the new launch target ‘iPhone Debug Features’. This will take about 30 seconds to run but once it has your can just restart the project to rerun the tests without a full rebuild.

Regenerating the features after a wording change takes a bit of time, but if your are careful to only set it up once you have understood and agreed the requirements it will be a fairly limited activity.

Feature test step generation
Feature test step generation

Cucumber Report

There are two new launch targets for each platform that can be used to generate and view the cucumber report for the feature tests.

Launch Agent - iPhone cucumber report
Launch Agent – iPhone cucumber report
Launch Agent - iPhone feature tests
Launch Agent – iPhone feature tests

The first just generates the report from the last test run and the latter does the job lot, opens the simulator, runs the feature tests and then generates the report.

Automating commands

I added a command line project dt.dart under a new /tools directory and integrated the running of commands into launch.json using the ‘F5 Anything’ extension.

F5 Anything Visual Code packge
F5 Anything Visual Code packge

That gives us plenty of options to automate the running of commands, if they are simple just use F5 directly

F5 Launch agent code
F5 Launch agent code

If they are more complicated or composite you can extend the command line tooling and then run it using ‘F5’.

dt.dart commands

Just open a command prompt at the root of the project and use these commands:

// Refresh features

dart run tools/dt/bin/dt.dart -r /Users/simbu/Mobile/flutter/digestableme

// Run feature tests and generate/show cucumber report:

dart run tools/dt/bin/dt.dart -f /Users/simbu/Mobile/flutter/digestableme B7A5F50E-DA94-49D0-913F-698AF846365D

// Generate/Show cucumber report:

dart run tools/dt/bin/dt.dart -c /Users/simbu/Mobile/flutter/digestableme B7A5F50E-DA94-49D0-913F-698AF846365D

The guid is the simulator device id (uuid).

The easiest way to find out a uuid is to run the simulator and then at a terminal prompt, type:

flutter devices
Cmd to list devices
Cmd to list devices

If the process fails or hangs then try the following to find the problem:

//Check that the project builds and runs
flutter run

//Check the tests run
flutter drive --driver=test_driver/integration_test_feature_driver.dart --target=integration_test/gherkin_suite_test.dart             

Testing network images

Added an extension to allow network images requests to be stubbed out for unit and widget tests where the HTTP client is not available.

ORIGINAL:         Image.network(recipe.thumbnail),
WITH EXTENSION:   context.networkImage(recipe.thumbnail)

Relies on EnvironmentMode being set in the <StartUp> provider.

Sound & Vision

Squeeze - Spot the Difference
Squeeze – Spot the Difference
Florence + the Machine - Dance Fever
Florence + the Machine – Dance Fever

Links

Flutter – Tests

The code for this article

on extreme programming and testing —>

We can’t just test manually. Since we are changing the program every week, we need to test pretty much everything every week. So if it only took ten percent of a week to test what we write each week, three months in we’d be testing more than 100 percent of the time. We’d have to hire more and more testers, and presumably we have a fixed budget, so we’d have to lay off programmers, and pretty soon all we have left is one programmer and nine testers who can’t keep up with the load.

My current/recent thinking centers around the “increment”, the running tested software which I believe a team should1 produce as soon as possible2 and keep running thereafter.

Look at that right there. I’m asking that the product grow in running tested ready-to-ship code from the very beginning, every day, until we finally stop funding the effort.

I think that when Kent first introduced C3 to the ideas, he did say that we would be “aggressive”. He didn’t mean with each other, he meant as regards going after good things with some fervor.

In subsequent threads, Martijn Meijering said that he felt it connoted “drive” or “striving for excellence”. He went on to remind us that we used to say merciless refactoring, relentless testing, continuous integration, the simplest thing that could possibly work, and to suggest that it was the italicized extreme bits that seem missing from today’s teams.

Excerpts from Ron Jeffries thoughts on Extreme Programming (XP)

My own experience and expectations are:

  • Running the tests help me understand how a piece of code works, they should be a go to, providing valuable readable living documentation that shows and proves what the code does.
  • They are a contract i.e. that I can easily and confidently make big changes as long as the tests continue to pass, then what was intend is still working, and I can be confident there will be no regressions, no fear!
  • They help me write better code, by forcing it to be decoupled and giving me time to play with new code I write.

Flutter says tests fall into three categories:

  • Unit
  • Widget
  • Integration

See the official documentation for more details.

So far we have a few unit and widget tests.

To improve on this I first added widget tests for the SplitView widget that displays content on iPads, as this is a widget we have built from scratch.

Now we need to show and prove the application behaves as expected.

For this I’ve turned to feature tests, which I’ve added as integration tests.

My aim is to follow the Behavioural Driven Development process:

The BDD process

Ta Da

Unit Test

Unit test results
Unit test results

Widget Test

Widget test results
Widget test results
Widget ViewModel test results
Widget ViewModel test results

Integration Test

Cucumber Feature Test Report
Cucumber Feature Test Report

Cucumber style behavioural tests have features that have a number of scenarios and scenarios can have rules. Screenshots can be added for additional clarity.

Next

We move onto Android, starting with the tabbed navigation layout.

BackBurner

Quite a lot still to firm up, injecting test data/adpaters, details on the discovery and formulation of testable specifications, whether to use Gherkin syntax for other tests like widget tests, can we move non-ui specification tests out of the integration tests to improve performance, to mock or not/very much.

Quite a lot still not covered, navigation, adapters to get device details, data access.

Automating the creating of the living documentation (Cucumberjs Report) will be add with the next feature under test.

XP

A starting point

By introducing feature tests we have a good starting point to keep the focus on delivering new functionality.

The details, the discipline will evolve as we add the new features.

We are now moving to test first, outside in. Each new addition will start with a feature test and then move down to widgets and unit tests.

More importantly we are starting to building software we have the confidence to change knowing it still does what was intended and that the checks are automated, quick to run and cover the requirements.

John Ferguson Smart makes the distinction between feature tests that prove the UI and those that prove the applications API’s and logic, I will be exploring this and other questions testing is raising as we progress.

I will post again on testing when I have more to share.

Good luck, be patient, there is a pot of gold at the end, remember what Uncle Bob says:

“The only way to go fast is to go well”.

Hard to test code

Watch Jeremy Fairbanks video on Practical Functional Programming  where he looks at code that is hard to test and recommends isolating the code you write in pure functions to simplify testing.

Flutter_Gherkin

The BDD tools for Flutter are maturing so I just had to chose one and work with it, things could change but BDD brings significant benefits and is worth the effort.

My choice was flutter_gherkin because it as it was the closet implementation of the Gherkin syntax used by Cucumber.

I used the new version of flutter_gherkin that is in beta 3.0.0-rc.9, It uses Flutter’s new integration_test framework which Is a breaking change to remove RPC calls by wrapping the app with the tests. It’s much faster and integrates better with external testing tools.

Every time you update or add a feature file then you need to update the generated code files:

flutter pub run build_runner clean
flutter pub run build_runner build

You can run the tests at the command line:

flutter drive --driver=test_driver/integration_test_iphone_driver.dart --target=integration_test/iphone_gherkin_suite_test.dart

On completion you can find a report in json format that can be used with Cucumber reporters.

There are still a few issues/side effects to work on that will be tightened up as the feature tests and package mature e..g. there is an additional warning at the end of the test run, even though the plugin is used:

Flutter Gherkin integration warning
Flutter Gherkin integration warning

And some post test exceptions in debug mode when the runner is not stopped.

Flutter Gherkin timer not closing on test completion
Flutter Gherkin timer not closing on test completion

It appears that ‘context.world.appDriver.waitUntil(’ is not releasing the timer on completion.

Cucumber Html Reporter

Use to generate Cucumber HTML reports with pie charts.

Cucumber Html Reporter - Report created message
Cucumber Html Reporter – Report created message

There are some prerequisites to get the reporter running:

  • install node, ideally via NVM
  • install yarn

Once installed run a command prompt at:
*integration_test\gherkin\reporters\cucumber_html

and run these commands

yarn
node index.js

Note the json output from the feature test needs some formatting for it to work with this reporter, which is something I will integrate when I add the next feature.

If you want to do it manually for now you need to the following:

  1. Remove additional structure
Cucumber Html Report - Manual formatting to remover extra elements
Cucumber Html Report – Manual formatting to remover extra elements
  1. Replace all occurrences of Cucumber Html Reporter - Format json remove quotation escape characters. with “

Injecting Providers

Error when providers not supplied for test.
Error when providers not supplied for test.

To get round this for now I moved the application providers into a file.

We will still need to inject env dependencies like API once we have them.

People

Kent Beck

Kent Beck
Kent Beck
Test Driven Development book
Test Driven Development book

Quite simply, test-driven development is meant to eliminate fear in application development. While some fear is healthy (often viewed as a conscience that tells programmers to be careful!), the author believes that byproducts of fear include tentative, grumpy, and uncommunicative programmers who are unable to absorb constructive criticism. When programming teams buy into TDD, they immediately see positive results. They eliminate the fear involved in their jobs, and are better equipped to tackle the difficult challenges that face them. TDD eliminates tentative traits, it teaches programmers to communicate, and it encourages team members to seek out criticism However, even the author admits that grumpiness must be worked out individually! In short, the premise behind TDD is that code should be continually tested and refactored. Kent Beck teaches programmers by example, so they can painlessly and dramatically increase the quality of their work.

Dan North

Dan North
Dan North

Invented Behavioural Driven Development at Thoughtworks.

Behaviour-driven development is about implementing an application by describing it from the point of view of its stakeholders.

Aslak Hellesøy

Aslak Hellesøy
Aslak Hellesøy

Cucumber founder.

Cucumber is a tool that supports BDD, which is a variant of TDD (Test-Driven Development). With BDD, all the tests are customer acceptance tests, written in plain (human) language so that non-technical stakeholders can understand them. Cucumber combines requirements specifications, automated tests and living documentation into a single format called Gherkin, which is just plain English with a little more structure.

The benefits you can get from BDD (if you do it well) is less rework, fewer bugs and more maintainable code. In order to reap those benefits you have to invest some effort in exploring requirements and designing the software to be testable. Exploring requirements typically pays off quicker, while designing testable software tends to pay off in the long run. BDD helps teams discover mistakes quickly, which makes software development more enjoyable and sustainable. – InfoQ

Sound & Vision

Severance, Apple TV drama
Severance, Apple TV drama
Wet Leg Album
Wet Leg Album

Links

One more thing…

“I’m not a great programmer; I’m just a good programmer with great habits.”

Kent Beck

“When I use a word”, said Humpty Dumpty in a rather scornful tone, “it means just what I want it to mean, neither more nor less”

Lewis Carroll – Through the Looking Glass

Flutter for iOS – Side Menu Widget

Code

Ok, time to add the side menu widget to the iPad split view, it should look something like this.

Side menu example.
Side menu example.

We only need one level for now so it is relatively straight forward.

Ta Da

Shows the recipes when the 'Digest' menu item is selected
Shows the recipes when the ‘Digest’ menu item is selected
Shows the content placeholders when the 'Friends' menu item is selected
Shows the content placeholders when the ‘Friends’ menu item is selected

The tricky bit this time was getting the side menu to communicate with the content areas to switch content out based on the the menu item selected.

Next

That’s it for iOS for now.

Before then turning out attention to Android, we will take a look at unit, widget and acceptance tests, see you next time.

XP

Widget Events

The side menu needed to communicate when a menu item was selected so that the content areas could be changed based on the item selected.

To do this I create an event ‘MenuItemtapped’ that is fired when a menu item is tapped and bubbles up to the parent splitview widget, which in turn, sets the selected index value and notifies the content to change.

The event extends the built in widget notification class.

This is powerful stuff and allows us to keep the widgets loosely coupled so that we can use the side menu in another widget other than the splitview should we need to. Chema Rubio article is a good summary on how to use events in your application.

ListTile

Replaced custom list item with the Cupertino version of the ListTile.

Now we have a widget for lists and view model for the list items, that provides a standard list tile experience on iOS that can easily be extend to Android.

Testing Widgets Throw Assertions

Found these useful matchers in the Flutter source code in ‘flutter/dev/conductor/core/test/common.dart’.

Matcher throwsAssertionWith(String messageSubString) {
  return throwsA(
      isA<AssertionError>().having(
          (AssertionError e) => e.toString(),
          'description',
          contains(messageSubString),
      ),
  );
}

Matcher throwsExceptionWith(String messageSubString) {
  return throwsA(
      isA<Exception>().having(
          (Exception e) => e.toString(),
          'description',
          contains(messageSubString),
      ),
  );
}

Usage Example:

expect(
	() => Version.increment(version, level).toString(),
    throwsAssertionWith("Do not increment"),
);

BackBurner

Adding recipes

The functionality to search and add recipes has not been implemented, the priority now is to get everything fully under test and provide a native experience for Android users.

Sub menus

Submenu items with expansion will be added when needed.

Shows and example of submenu items with expansion
Shows and example of submenu items with expansion

List Items

Shows and example of a list item using the CupertinoListTile widget.
Shows and example of a list item using the CupertinoListTile widget.

Not sure if the CupertinoListTile is quite what I want.

  • Text is small.
  • Divider line placement is not centred.
  • Highlighting of selected item could be polished up.

It’s functional for now but list items are very import so we want a great one, so we will revisit it after adding the Android version.

Links

Sound & Vision

Apple TV drama, Suspicion.

One more thing…

“I don’t think it’s good that Apple’s perceived as different. I think it’s important that Apple’s perceived as much better. If being different is essential to doing that, then we have to do that, but if we can be much better without being different, that’d be fine with me. I want to be much better.”

Steve Jobs

Flutter – Split View Widget

Apple recommend using a split view layout on iPads.

Apple's split view layout. (They have resued the term in multitasking)
Apple’s split view layout. (They have resued the term in multitasking)

There is no Cupertino split view component out of the box, so we are going to build this one from scratch.

I used Apple’s Mail application as my benchmark and came up with some requirements:

Three Columns
- Sidebar
- Supplementary (Optional)
- Content

All columns are displayed when in Landscape.

In portrait only the content column is displayed and the nav bar allows you to view the Sidebar and supplementary columns.

The Sidebar has:
- A nav bar with icons
	- Collapse
	- Other actions like edit, del…
- Title (Heading)
- Navigation Tree

The supplementary column has:
- A nav bar with icons
		- Collapse
		- Other actions like edit, del…
- Large Title 
- Sub-title
- Content area, often a list of items

The content column has:
 - A nav bar with icons
	- Other actions like edit, del…
- Large Title
- Content area

Ta Da

Shows a recipe on the iPad in the new splitview.
Shows a recipe on the iPad in the new splitview.
The split view widget tests
The split view widget tests

Next

Spoiler alert: Welcome to the new ‘Next’ section.

The next blog will complete the split view by adding a navigation menu to the sidebar.

This will conclude our work with iOS devices for a while.

Before then turning out attention to Android, we will take a look at unit, widget and acceptance tests, see you next time.

BackBurner

Ran out of time this week to complete the sidebar menu, so it gets it’s own post next time.

The navigation bars in the split view are opaque and do not have the same visual feature of a tab bar that allows the content to scroll under it. The aim is to add this later when we look at more advanced animation techniques.

We are leaving the requirement to only display the content area in portrait mode, with the option to slide in the sidebar and supplementary columns via the navigation bars. It fell into nice to have.

Isolating navigation and httpclient, so that they can be mocked or stubbed in the unit and widget tests, I will pick this up in the testing blog.

XP

85% Reuse

I renamed the ‘screen’ directory to ‘content’ with the aim that all the widgets under it will be used cross platform, and widgets in other directories will be platform specific.

I intend to monitor the code/widgets to see if we can hit or even exceed the expected reuse target across platforms.

Shows expected code resuse of 85% when targeting multiple platforms along side the project widget subdirectories.
Shows expected code resuse of 85% when targeting multiple platforms along side the project widget subdirectories.

More on metrics another time.

Project Structure

The tricky bit is deciding when to put areas under items and when to put items under areas. I start by putting areas under items e.g. the digest folder will have a models folder, a data folder etc… this way the supporting files are located next to the files referencing them.

I will then move things around when I start to feel lost.

Whilst creating the split view widget I moved all widgets models in to the area lib/model directory and all data under lib/data, which is a change in direction, but is working better for me.

Another way to tackle this is to just use the finder in VSCode ?P.

Themes

As part of building this layout, I refactored themes moving them out of the DigestibleMeState file into their own ‘ChangeNotifierProvider’, this has cleaned up the responsibilities and put us in a good position to setup the theme to dynamically change based on the time of day.

Building layouts with Rows and Columns

Split view is the largest piece of UI so far and I personally find it useful to experiment with the layouts using rows, columns and containers with background colours.

It doesn’t take much longer than using a drawing tool to produce a screen design.

I recommend playing with Columns, Rows and Containers when building new UI and just watching what happens.

Working with rows and columns
Working with rows and columns

Sound & Vision

Moments by Jamie Webster

One more thing…

You’ve got to find what you love. And that is as true for your work as it is for your lovers. Your work is going to fill a large part of your life, and the only way to be truly satisfied is to do what you believe is great work. And the only way to do great work is to love what you do. If you haven’t found it yet, keep looking. Don’t settle.”

Steve Jobs_ on finding your passion

Flutter – Adaptive Layout

I rotated the screen on the iPhone and it worked, so first strike, no hard coded UI positional issues with the widgets and scaffold so far.

Shows the digest list in landscape.
Shows the digest list in landscape.
Shows the recipe details in landscape.
Shows the recipe details in landscape.

Apple’s human interface guidelines recommend using a split view layout instead of a tab bar on an iPad.

Based on the current phone screens we need something like this:

Split view design for the recipes digest
Split view design for the recipes digest

And some information and logic to detect the device and select the appropriate layout.

Shows the adaptive layout working for iPhone and iPad.
Shows the adaptive layout working for iPhone and iPad.

Ta Da

Three column splitview.
Three column splitview.

In the next post we will add the functionality to the new split view screen and decide on which devices and orientations it will be available in.

XP

For such a seemly simple change adapting the layout to different devices, I had to consider a number of tricky architectural areas and make some structural changes

Hopefully, they will help keep the code base clean and healthy as the application grows.

Device info and layout.

Welcome to the chicken and egg world of relying on data from asynchronous calls.

To decide between tabbed or split view I need to know about the client device and its orientation.

However, the device information is only available after the application loads and builds the screen.

To get around this I created a widget “LayoutSelector”, and a new change notifier provider called ‘Layout’.

When the application first runs the device information is not available and it displays a blank screen.

When the device information call finishes its sets the value on the ‘Layout’ provider, which then notifies the ‘LayoutSelector’ to rebuild.

This time it has the device information and can choose between the tab or split view layout.

The user experience works well because all the layout backgrounds are the same colour, so the screen change between loading and the selected device layout goes un-noticed.

Now we have a loading screen there is an option to improve the Ux when things take longer than usual to complete i.e. add a funky animation that is displayed if the application takes longer than a second to load the layout.

This feels the right way to go, but I welcome comments on this.

One thing to note is I’m trying to keep application state simple and just use providers over a DI package.

Package Imports

I’ve added another requirement to the ‘Done, Done, Done, Done’ check list for development.

  • Relative imports for children and package imports for everything else.

It was tempting to just go with package imports but having relative ones for children makes it easier to move folders around under the \lib dir.

There will be a blog on the ‘done, done, done, done’ checklist later in the series.

Project Structure

It is time for a little bit more structure as the number of files and directories grow.

Up to this point all the screens where under the /lib/tab directory. Now they are shared with the split view they have been separated and we have three directories.

  • \lib\ui\tab
  • \lib\ui\screen (Used by the tabs and split view panels.)
  • \lib\ui\splitview

The number of top level directories had also increased so I added \ui at the top level and moved a number of the top level items under it.

Project structure for /lib & /test directory
Project structure for /lib & /test directory

I follow a general rule of 7s, never more than 7 items at a given level. Not sure who I picked this rule up from.

Links

Sound & Vision

BBC drama: The Tourist

One more thing…

“We do no market research. We don’t hire consultants. We just want to make great products.”

Steve Jobs_