How I Replicate The Apple Cash Card UI in Flutter

Heshan Wijesinghe
Nerd For Tech
Published in
5 min readNov 22, 2023

In the Apple Wallet app, there’s a default card called ‘Cash’ with an engaging UI. I’m particularly curious about its holographic effect, which reacts to the device’s gyro input.

Apple Cash Card UI

All the work mentioned in this article is available in this GitHub repo. 🚀

So, What I Wanted to Replicate…

  1. Embossed Pebble — A circle with an arrow-shaped symbol positioned at the center and subtly raised appearance around its edges.
  2. Fibonacci Spiral — Arrangement of the pebbles following the Fibonacci number sequence.
  3. Diffraction Effect — White light grating or the effect seen on a CD.
  4. Overlay Elements — A method for positioning any widget on top of the spiral.

Embossed Pebble

Left — Natural tones, Right — Elevated contrast for enhanced clarity

Initially, I experimented with various styling options using gradients. Nailing that perfect curved gradient, especially around the arrow, was a struggle, and trying to control each little detail felt impossible with this approach. So, I abandoned this approach and opted to try something else.

Because we’re aiming to create an effect related to lights and casting light, adopting a tried-and-true game development approach seems like the perfect solution.

With the use of normal maps and Flutter shader API, I set up a straightforward proof of concept for the new approach. So, the next challenge was to generate the normal map and ensure it matched the characteristics of the original reference pebble.

Blender, This powerhouse of a tool came to the rescue. 💪✨

In Blender, I developed a material to display its geometry normals. I then combined it with a height map I created, resulting in the distinctive bevel normal for the arrow. Next, I processed the same height map through a BW color ramp, turning it into an alpha map. Then I used it to carve out the arrow shape.

I performed all these adjustments within the material itself rather than creating a solid shape. This approach made it easier to fine-tune and match the reference, as manipulating materials allowed for more intuitive adjustments compared to solid objects.

Blender material preview of a “pebble”

Fibonacci Spiral

In creating the spiral, I had to consider two main features. Firstly, its arrangement aligns with the Fibonacci number set. The other characteristic involves the pebbles following a gradual size reduction from the center outward.

I wrote a Blender Python script to duplicate, resize and distribute objects. It turns out scripting may not be the best approach. Despite attempting to eyeball things, pinpointing the correct golden angle in the script proved challenging. I experimented with several values, but none seemed to align perfectly with the original reference.

Then I stumbled upon an article highlighting the incredible capabilities of the Blender Geometry Nodes system. I decided to give it a shot with this new approach. After some tinkering, I assembled a node structure that not only incorporates the golden angle but also allows adjustment via a slider. To achieve the gradual scaling of pebbles, I used a Float Curve node for precise control.

The angle in this arrangement is precisely -137.508 degrees.

Generated spiral in Blender

Diffraction Effect

Examples of the diffraction grating effect in action

Diffraction gratings are optical devices that disperse light into its various components based on their wavelengths.

To replicate this physical property in a graphical medium, I took a low-level approach. While I was browsing through stuff, I came across this fascinating article about light and diffraction grating by Alan Zucconi. Additionally, he developed a shader for it.

Next is to combine all together.

Left — VS Code, Right — KodeLife

In this case, I created a separate shader file and used KodeLife to display the output. This allowed me to swiftly modify aspects of the code and see the output in real-time.

First, I exported the normal map as a PNG from Blender and loaded it into KodeLife shader stage as a texture parameter. Then in the shader file, we can utilize it as a 2D texture.

In the shader code, I corrected the coordinates and normalized the textures. I used a 2D vector parameter to set the offset for the effect. After normalizing it, I calculated the light direction. Following this, I determined the dot product with the surface normals and offset direction, providing values that could be used to illuminate the pebbles. Then, I used the spectral_zucconi6 function to generate gradient from the distance value.

Now, the shader is complete! Textures are in place. ✨

Next up, Flutter Integration… 🚀

I created the shader program to take viewport resolution, texture resolution, effect offset, and the image sampler as uniforms. This allows me to manipulate them during the render cycle.

To isolate the spiral from the background, I used the ShaderMask widget with an ImageShader and set the blend mode to BlendMode.dstIn. This process acts like a cookie-cutter, leaving me with only the pebble pattern.

All the textures are rendered from Blender.

For the effect offset value, I wrapped the painter with a ValueListenableBuilder and provided the ValueListenable Which is coming from an InheritedWidget. In this way, I can assign any listenable at runtime, making it incredibly convenient for the presentation.

Overlay Elements

This is to utilize the custom spiral painter as a regular widget. I created a card widget that accepts a foreground builder to render its overlay elements.

The implementation is available in this GitHub repo.

...   
SpiralCard(
foregroundBuilder: (context, constraints) {
return const Padding(
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 18),
child: Column(
children: [
...
],
),
);
},
),
...

🎉 Ta-da! Behold the masterpiece…

Not an exact replica, but the techniques are valid. A few more tweaks, and we’ll get closer.

This is how I replicate the Apple Cash Card UI in Flutter, GLSL, and a bit of Blender. I hope you found this interesting. Thank you for reading!

All the source files are available in the repository; Blender files reside in the ‘misc’ directory, and Flutter sources are in the ‘app’ directory.

--

--

Heshan Wijesinghe
Nerd For Tech
0 Followers
Writer for

A passionate UI/UX engineer dedicated to crafting engaging digital experiences.