Back to the blog

Iridescence Effect with Flutter

June 15th, 2022

I found this cool iridescent effect on Twitter.

First week at the new job ✨ I’m experimenting with using device sensors to change the primary color of the UI based on the angle you hold the phone. It’s hard to capture tho but feels much better in hand. #protopie pic.twitter.com/vv0UDLjEPr

— Yasir (●ᴗ●) (@yasirbugra) March 16, 2022

Let's do it with Flutter!

Iridescence by definition depends on the angle at which you look at an object. Unfortunately, it's not so easy to determine what angle you're looking at your phone at, but we can emulate the shift in angle that happens when the phone's orientation changes. Monitoring the phone's orientation is enough to create a convincing pseudo-iridescence effect.

Conceptually, we want to adjust the hue of the widget based on the angle the device is at.

In Flutter, we will use the motion_sensors package.

$ flutter pub add motion_sensors

The package's motionSensors.absoluteOrientation stream gives us the absolute orientation of the device in terms of pitch, roll, and yaw. The sum of all these three components gives us a hue shifting angle. We convert this to degrees and generate a color that matches that hue with HSLColor.

import 'package:motion_sensors/motion_sensors.dart';

...

    StreamBuilder<AbsoluteOrientationEvent>(
      stream: motionSensors.absoluteOrientation,
      builder: (context, snapshot) {
        final orientation = snapshot.data;
        
        // Calculate the iridescence angle.
        final deg = orientation == null
            ? 0
            : rad2deg(orientation.pitch + orientation.roll + orientation.yaw);

        // Create an HSLColor with that hue.
        final hsl = HSLColor.fromAHSL(1.0, deg % 360, 1.0, 0.5);

        // Apply this color to the widget.
        return Center(
            child: Text(
          'Shimmer',
          style: TextStyle(
              fontSize: 96,
              fontWeight: FontWeight.bold,
              color: hsl.toColor()),
        ));
      });
    

The last thing we need to do is increase the sampling rate to make the effect smoother. In the widget's initState(), add

import 'package:motion_sensors/motion_sensors.dart';

...
      
  @override
  void initState() {
    super.initState();

    motionSensors.absoluteOrientationUpdateInterval =
        Duration.microsecondsPerSecond ~/ 60;
  }
    

to sample at 60 frames per second. And that's it! Here's what it looks like.

For a complete demo, see the example repository.


Comment on this post on Twitter