Generative art in Flutter

Art and computer science are both ever-evolving and beautiful things are created when their concepts combine. Generative art is a prime example of this. In this Answer, we'll look at what generative art is and how to use Flutter to render basic generative art.

Generative art
Generative art

Flutter

Flutter is a UI development toolkit primarily used for building applications for various platforms using a single codebase. It is completely open-source and is a product of Google.

Simply put, this means that developers can write the code once and use it on different platforms without having to start from scratch for each one.

Flutter in a few words
Flutter in a few words

Note: We offer various courses if you aim to continue your journey in learning Flutter!

Beginning Flutter: Android Mobile App Development

Learn Dart: First Step to Flutter

Note: Before proceeding with the answer, ensure that your Flutter setup is complete.

Generative art

Generative art is a form of art created using algorithms, code, or other systematic processes to produce different kinds of artwork. These artworks could be in the form of:

  • visual

  • auditory

  • interactive

Simply put, instead of being drawn by the artists themselves, generative art is created using a systematic procedure in a virtual environment. It relies on rules, randomness, and input data to create unique art pieces.

Samples of generative art in Flutter

The following samples have been generated by configuring our Flutter application with different algorithms. Wonderfully crafted, aren't they?

Generative art made using Flutter
Generative art made using Flutter
Generative art made using Flutter
Generative art made using Flutter

Some of the pieces are static, while others are dynamic. Static generative art is rendered once and doesn't contain motion or interactivity, and dynamic generative art changes with time according to the specified rules.

Static artwork

Static generative art
Static generative art

Dynamic artwork

The process of creating generative art

CustomPainter class

First and foremost, we create a new CustomPainter class and make it extend CustomPainter. This class will be responsible for drawing art mainly.

paint method

Next, we override the paint method inside the CustomPainter class. This is where we define what shapes, patterns, or mathematical operations we'll be modeling.

Mathematical operations

We can make shapes by using different draw methods like drawRect, drawCircle in Flutter or take it to the next level by applying mathematical functions, such as sin, cos, tan, atan exp, etc., to generate dynamic shapes and patterns. For instance, we can use sin, and cos functions to create smooth curves or waves and exp for exponential growth or decay effects.

Color styles

It is up to us to choose colors or gradients to style the patterns made by our chosen mathematical operations. For example, we could use hue shifting based on angles.

Optional animations

To add animations, we can use the AnimatedBuilder widget or Ticker. We can specify how we want to control the updates in our custom painting logic using these tools. Within the paint method, we can update the mathematical operations or variables at each frame to create an animation effect. For instance, we can increment angles or change colors.

Display the animation

Finally, we wrap our CustomPainter inside the CustomPaint widget and use it within the widget tree. We can run the animation using a Timer to trigger the repaints or utilize the AnimationController to manage the animation duration and progress.

This table depicts how generative artworks in general.

Steps

Explanation

Algorithm design

We should first figure out what algorithm rules or patterns we want our art to be based on.

Randomization

We can also make our art unpredictable and unique through randomization.

Parameterization

Our art can also be influenced by adding parameters.

Iterations

We can make use of loops to make similar patterns repeatedly.

Data input

We can also take input from the user or environment.

Mathematical equations

This is the crux of the process in which we define mathematical equations to model our artwork.

Color and gradients

We can add colors and gradients in our art to add depth.

Animations

We can add motion to implement different animations of patterns.

Interactivity

We can incorporate user interactions to modify the generative process too.

Rendering

Our artwork can finally be rendered and exported in the needed formats.

An animated generative art example

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: GenerativeArtScreen(),
    );
  }
}

class GenerativeArtScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Center(
        child: GenerativeArt(),
      ),
    );
  }
}

class GenerativeArt extends StatefulWidget {
  @override
  _GenerativeArtState createState() => _GenerativeArtState();
}

class _GenerativeArtState extends State<GenerativeArt> with TickerProviderStateMixin {
  final random = Random();
  final int maxSquares = 100;
  final int maxCircles = 100;
  final double minCircleRadius = 2.0;
  final double maxCircleRadius = 20.0;

  late AnimationController controller;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(vsync: this, duration: Duration(seconds: 10))..repeat();
  }

  Color randomDarkColor() {
    return Color.fromARGB(
      255,
      random.nextInt(100),
      random.nextInt(100),
      random.nextInt(100),
    );
  }

  Color randomLightColor() {
    return Color.fromARGB(
      255,
      100 + random.nextInt(155),
      100 + random.nextInt(155),
      100 + random.nextInt(155),
    );
  }

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: GenerativeArtPainter(
        random: random,
        maxSquares: maxSquares,
        maxCircles: maxCircles,
        minCircleRadius: minCircleRadius,
        maxCircleRadius: maxCircleRadius,
        controller: controller,
        randomDarkColor: randomDarkColor,
        randomLightColor: randomLightColor,
      ),
      size: MediaQuery.of(context).size,
    );
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }
}

class GenerativeArtPainter extends CustomPainter {
  final Random random;
  final int maxSquares;
  final int maxCircles;
  final double minCircleRadius;
  final double maxCircleRadius;
  final AnimationController controller;
  final Color Function() randomDarkColor;
  final Color Function() randomLightColor;

  GenerativeArtPainter({
    required this.random,
    required this.maxSquares,
    required this.maxCircles,
    required this.minCircleRadius,
    required this.maxCircleRadius,
    required this.controller,
    required this.randomDarkColor,
    required this.randomLightColor,
  }) : super(repaint: controller);

  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawColor(Colors.black, BlendMode.clear);

    for (int i = 0; i < maxSquares; i++) {
      final double x = random.nextDouble() * size.width;
      final double y = random.nextDouble() * size.height;
      final double side = random.nextDouble() * 50 + 10;
      final double angle = random.nextDouble() * pi;
      final double opacity = random.nextDouble();
      final Paint paint = Paint()
        ..color = randomDarkColor().withOpacity(opacity)
        ..style = PaintingStyle.fill;
      final Rect squareRect = Rect.fromCenter(
        center: Offset(x, y),
        width: side,
        height: side,
      );
      canvas.save();
      canvas.rotate(angle);
      canvas.drawRect(squareRect, paint);
      canvas.restore();
    }

    final double t = controller.value; 
    for (int i = 0; i < maxCircles; i++) {
      final double x = random.nextDouble() * size.width;
      final double y = size.height * (1 - t); 
      final double radius = random.nextDouble() * (maxCircleRadius - minCircleRadius) + minCircleRadius;
      final double opacity = random.nextDouble();
      final Paint paint = Paint()
        ..color = randomLightColor().withOpacity(opacity)
        ..style = PaintingStyle.fill;
      canvas.drawCircle(Offset(x, y), radius, paint);
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

Code explanation

  1. The first thing that we do is define our main function to run the MyApp widget.

  2. Second, we create the GenerativeArtScreen as our home screen.

  3. The GenerativeArtScreen widget sets up the main screen, which is a black background with the GenerativeArt widget centered on it.

  4. We pair the GenerativeArt widget with a custom animation controller. It generates random dark and light colors for the squares and circles.

  5. We use the CustomPaint widget to draw generative art. The code specifies the GenerativeArtPainter as the painter for the canvas.

  6. Next, we move to the GenerativeArtPainter. It is a custom painter that draws this art on the canvas. It generates random dark squares and animated light circles with random positions, sizes, and colors.

  7. For the animation, it updates the animation based on the value of the AnimationController, which causes the circles to move from the bottom to the top of the screen.

Demonstration

How well do you know Flutter’s generative art?

Question

What should we use if we aim to change our static art to dynamic art?

Show Answer

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved