Category: Coding & Scripting

Flutter on iOS: themeMode does not change to dark mode if `ThemeMode.system` is used

In my case, a simple app should automatically use the theme (light or dark) of the system to style the user interface. By default, this should work when using ThemeMode.system (see flutter documentation). But it didn’t.

The themes have been defined as follows:

    return MaterialApp(
      themeMode: ThemeMode.system,
      theme: ThemeData( ... ),
      darkTheme: ThemeData( ...),
      ...
    );

In addition, the WidgetsBindingObserver callback didChangePlatformBrightness() was never called. It was defined as follows:

class MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver
{
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangePlatformBrightness() {
    print(WidgetsBinding.instance.window.platformBrightness);
    // > should print Brightness.light / Brightness.dark when you switch
    super.didChangePlatformBrightness();
  }
}

After hours and days of searching, it turned out, that the following definition was set in info.plist of iOS:

<key>UIUserInterfaceStyle</key>
<string>Light</string>

Removing this line solved the issue. This setting sets the apps theme to Light, which results in a constant value even if the user changed the brightness to dark. Without this line, UIUserInterfaceStyle depends on the global setting.

Source: https://stackoverflow.com/questions/61620332/platform-brightness-is-still-light-on-ios-even-when-dark-mode-is-on-flutter

fatal error: ‘Flutter/Flutter.h’ file not found

After switching the flutter channel to beta and back to stable, my app did not compile anymore. The compilation stopped with the error:

fatal error: 'Flutter/Flutter.h' file not found

Multiple flutter clean and channel switches did not work in this case.

The following commands fixed this behavior:

rm ios/Flutter/Flutter.podspec
flutter clean

See: https://github.com/flutter/flutter/issues/70895#issuecomment-744734693

Swift: how to create a Singleton pattern

What is a Singleton?

A singleton pattern guarantees that only one instance of a class is initialized and available from different points of an app.

Some examples are already available in Apple’s frameworks:

// Shared URL Session
let sharedURLSession = URLSession.shared

// Default File Manager
let defaultFileManager = FileManager.default

// Standard User Defaults
let standardUserDefaults = UserDefaults.standard

How to define a Singleton

Often a static constant is used to adopt the Singleton pattern. To do that the reference to the shared instance is stored inside a static constant. In addition, the initializer is private and therefore hidden:

class NetworkManager {
    static let sharedInstance = NetworkManager()

    private init() { }

    func doSomething() {
        // ...
    }
}

By making the initializer method private, it’s guaranteed that no other instance of this class can be created. It would also be possible to move the static constant outside the class, but then the initializer might be accessible and that’s not what we want.

To access our NetworkManager instance just call:

NetworkManager.sharedInstance.doSomething()
NetworkManager.sharedInstance.doSomething()

This approach allows to use always the same instance, even in different points of the app.

PHPUnit: faster and better unit tests with pcov

When using PHPUnit there are different ways to create a code coverage report. By default, XDebug is used. But as mention on different sites, XDebug is very slow and the generation of a code coverage report might take several minutes for big projects.

phpdbg

To speed up things, phpdbg can be used. This significantly speeds up unit tests and code coverage. Phpdbg can be used as follows:

phpdbg -qrr ./vendor/bin/phpunit --coverage-text --colors=never

But there are different problems with the code coverage report of phpdbg. For example phpdbg does not cover a case line of a switch statement:

pcov

A better solution to cover this is pcov, which can be installed using pecl:

pecl install pcov

To create a code coverage report, phpunit can be called with:

php -dpcov.enabled=1 -dpcov.directory=. ./vendor/bin/phpunit --coverage-text

To exclude a directory, the following parameter can be used:

php -dpcov.enabled=1 -dpcov.directory=.  -dpcov.exclude="~vendor~" ./vendor/bin/phpunit --coverage-text

I don’t know if it’s really a problem with pcov, but: with pcov installed, it is not possible to use phpdbg anymore!

PHP: realpath() for non-existing path

The php method realpath() can transform the string format of a path into a real path. Means, a path string like:

/Users/mathias/data/../projects/website

will become:

/Users/mathias/projects/website

But this only works, if the path really exists. For non-existing paths, this function cannot be used. To get the same functionality, the following function can be used:

/**
 * Get normalized path, like realpath() for non-existing path or file
 *
 * @param string $path path to be normalized
 * @return false|string|string[]
 */
public function normalizePath(string $path)
{
    return array_reduce(explode('/', $path), function($a, $b) {
        if ($a === null) {
            $a = "/";
        }
        if ($b === "" || $b === ".") {
            return $a;
        }
        if ($b === "..") {
            return dirname($a);
        }

        return preg_replace("/\/+/", "/", "$a/$b");
    });
}

How to ignore PHP_CodeSniffer warning: Line exceeds 120 characters;

When using codesniffer to check your code, a lot of warnings might appear when the lines are too long:

----------------------------------------------------------------------
FOUND 0 ERRORS AND 4 WARNINGS AFFECTING 4 LINES
----------------------------------------------------------------------
  73 | WARNING | Line exceeds 120 characters; contains 162 characters
  75 | WARNING | Line exceeds 120 characters; contains 124 characters
 102 | WARNING | Line exceeds 120 characters; contains 168 characters
 108 | WARNING | Line exceeds 120 characters; contains 121 characters
----------------------------------------------------------------------

To ignore those warnings, we can add // phpcs:ignore as a comment to the end of a (too long) line. For example:

<?php
$message = 'This is my long message. It\'s not only long, it\'s extreeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeemly long'; // phpcs:ignore

Flutter: generating *.g.dart files for json serialization

The full documentation for this is available on flutter.dev

When creating json_serializable classes the first time, you’ll get errors similar to what is shown in the image below.

IDE warning when the generated code for a model class does not exist yet.

These errors are entirely normal and are simply because the generated code for the model class does not exist yet. To resolve this, run the code generator that generates the serialization boilerplate.

There are two ways of running the code generator.

One-time code generation

By running 

flutter pub run build_runner build

in the project root, you generate JSON serialization code for your models whenever they are needed. This triggers a one-time build that goes through the source files, picks the relevant ones, and generates the necessary serialization code for them.

While this is convenient, it would be nice if you did not have to run the build manually every time you make changes in your model classes.

Generating code continuously

watcher makes our source code generation process more convenient. It watches changes in our project files and automatically builds the necessary files when needed. Start the watcher by running

flutter pub run build_runner watch

in the project root.

It is safe to start the watcher once and leave it running in the background.

Source: https://flutter.dev/docs/development/data-and-backend/json#code-generation