0
votes

I'm experimenting with Flutter on Android in my spare time, and I'm struggling to make a toy Mandelbrot application wait for an async function to complete.

I've designed my Widget build method to show a circular progress indicator until the Mandelbrot points have been calculated (which takes a few seconds) - it checks a boolean flag "_gotPointValues" and shows the progress indicator if it is false.

The issue is that the async/await mechanism doesn't wait for the long running calculation to complete.

Execution from the line:

print("** Got data from async future **");

.. onwards continues immediately after entering the MandelbrotWidgetState._getMandelbrotPoints function.

I'm sure I'm missing something very obvious - appreciate any help if anyone can spot what I'm doing wrong.

Thanks.

import 'package:flutter/material.dart';
import 'package:phil_flutter_app/mandelbrot_painter.dart';
import 'package:phil_flutter_app/mandelbrot_points.dart';

/// This class defines a Mandelbrot widget.
class MandelbrotWidget extends StatefulWidget
{
    @override
    MandelbrotWidgetState createState()
    {
        return MandelbrotWidgetState();
    }
}

/// This class defines a Mandelbrot widget state.
class MandelbrotWidgetState extends State<MandelbrotWidget>
{
    int _pointsWidth;

    int _pointsHeight;

    int _maxIterations;

    MandelbrotPoints _mandelbrotPoints;

    MandelbrotPainter _mandelbrotPainter;

    List _pointValues;

    bool _gotPointValues;

    /// Initialise.
    void initState()
    {
        super.initState();

        print("initState");

        _pointsWidth = 1080;

        _pointsHeight = 1920;

        _maxIterations = 256;

        _mandelbrotPoints = new MandelbrotPoints(_pointsWidth, _pointsHeight, _maxIterations);

        _mandelbrotPainter = new MandelbrotPainter(_pointsWidth, _pointsHeight);

        _pointValues = null;

        _gotPointValues = false;

        _getMandelbrotPoints();
    }

    /// Get the mandelbrot points.
    void _getMandelbrotPoints() async
    {
        _pointValues = await _mandelbrotPoints.calculateMandelbrotPoints();

        print("** Got data from async future **");

        setState(() {
            _gotPointValues = true;
        });
    }

    @override
    Widget build(BuildContext context)
    {
        print("build");

        _mandelbrotPainter.setDevicePixelRatio(MediaQuery.of(context).devicePixelRatio);

        return Scaffold(
            backgroundColor: Colors.black,
            appBar: AppBar(
                title: Text("PH - Mandelbrot Test - Google Flutter"),
                centerTitle: true
            ),
            body: _buildBody(),
            floatingActionButton: new FloatingActionButton(
                onPressed: _redraw,
                tooltip: 'Refresh',
                child: Icon(Icons.refresh),
            ),
            floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat
        );
    }

    /// Build the body.
    Widget _buildBody()
    {
        if (_gotPointValues)
        {
            print("** Building fractal **");

            _mandelbrotPainter.setPointValues(_pointValues);

            return new Center(
                child: new CustomPaint(
                    painter: _mandelbrotPainter,
                    child: Center()
                )
            );
        }
        else
        {
            print("** Building progress indicator **");

            return new Center(
                child:  new CircularProgressIndicator()
            );
        }
    }

    /// Redraw.
    void _redraw()
    {
        _mandelbrotPainter.repaint();
    }

}

/// This class calculates points for the Mandelbrot fractal.
class MandelbrotPoints
{
    int width;

    int height;

    int _maxIterations;

    /// Constructor.
    MandelbrotPoints(int width, int height, int _maxIterations)
    {
        this.width = width;
        this.height = height;
        this._maxIterations = _maxIterations;
    }

    /// Calculate the Mandelbrot points.
    Future<List> calculateMandelbrotPoints() async
    {
        print("MandelbrotPoints.calculateMandelbrotPoints");

        int w = width;
        int h = height;

        List _pointValues = new List.generate(w, (i)
        => new List(h));

        int xOffset = 0;
        int yOffset = -(h ~/ 6);

        double zoom = 0.8;

        for (int x = 0; x < w; x ++)
        {
            for (int y = 0; y < h; y ++)
            {
                double cRe = 1.5 * (y + yOffset - h / 2) / (0.5 * zoom * h);
                double cIm = 1.0 * (x + xOffset - w / 2) / (0.5 * zoom * w);

                double zx = 0;
                double zy = 0;

                int iteration = 0;

                while (zx * zx + zy * zy <= 4 && iteration < _maxIterations)
                {
                    double zyNew = zy * zy - zx * zx + cRe;

                    zx = 2 * zx * zy + cIm;

                    zy = zyNew;

                    iteration ++;
                }

                if (iteration < _maxIterations)
                {
                    _pointValues[x][y] = iteration;
                }
                else
                {
                    _pointValues[x][y] = -1;
                }
            }
        }

        return _pointValues;
    }

}

import 'dart:math';
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:phil_flutter_app/mandelbrot_colour_utils.dart';

/// This class defines a custom painter
/// to draw a Mandelbrot fractal.
class MandelbrotPainter extends CustomPainter
{
    int _pointsWidth;

    int _pointsHeight;

    List _pointValues;

    Random _random;

    MandelbrotColorUtils _mandelbrotColorUtils;

    int _randomHue;

    Size _size;

    Canvas _canvas;

    double _devicePixelRatio;

    Paint _paint;

    double _width;

    double _height;

    /// Constructor.
    MandelbrotPainter(int _pointsWidth, int _pointsHeight)
    {
        print("MandelbrotPainter constructor");

        this._pointsWidth = _pointsWidth;

        this._pointsHeight = _pointsHeight;

        _random = new Random();

        _mandelbrotColorUtils = new MandelbrotColorUtils();

        _randomHue = _getRandomNumber(0, 255);

        _canvas = null;

        _width = -1;

        _height = -1;

        _paint = null;
    }

    /// Set the device pixel ratio.
    void setDevicePixelRatio(double devicePixelRatio)
    {
        _devicePixelRatio = devicePixelRatio;
    }

    @override
    void paint(Canvas canvas, Size size)
    {
        print("paint");

        _size = size;

        _width = size.width;

        _height = (size.height * 0.86);

        _canvas = canvas;

        _paint = Paint();
        _paint.strokeWidth = 1;
        _paint.style = PaintingStyle.fill;

        _randomHue = _getRandomNumber(0, 255);

        _drawMandelbrotPoints();
    }

    /// Repaint.
    void repaint()
    {
        paint(_canvas, _size);
    }

    /// Set the point values.
    void setPointValues(List _pointValues)
    {
        this._pointValues = _pointValues;
    }

    /// Draw the Mandelbrot points.
    void _drawMandelbrotPoints()
    {
        print("_drawMandelbrotPoints");

        _paint.color = Colors.black;
        _canvas.drawRect(Rect.fromLTWH(0, 0, _width, _height), _paint);

        int w = (_width * _devicePixelRatio).round();
        int h = (_height * _devicePixelRatio).round();

        for (int x = 0; x < _pointsWidth; x ++)
        {
            for (int y = 0; y < _pointsHeight; y ++)
            {
                int pixelValue = _pointValues[x][y];

                if (pixelValue == -1)
                {
                    _paint.color = Color.fromARGB(255, 0, 0, 0);
                }
                else
                {
                    double h = ((2 * pixelValue) + _randomHue).toDouble();

                    _paint.color =
                        _mandelbrotColorUtils.createColorFromHsv(h, 100, 100);
                }

                List<Offset> points = new List();

                int pointX = (x * (w / _pointsWidth)).round();
                int pointY = (y * (h / _pointsHeight)).round();

                points.add(Offset(pointX.toDouble() / _devicePixelRatio,
                    pointY.toDouble() / _devicePixelRatio));

                _canvas.drawPoints(PointMode.points, points, _paint);
            }
        }
    }

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

    /// Get a random number.
    int _getRandomNumber(int min, int max)
    {
        return min + _random.nextInt(max - min);
    }

}
2
use AWAIT on async callAntoniossss
Please post the entire code. It's easier to help you.Willie Nandi
Thanks, editing original post to include all code.phil76

2 Answers

0
votes

Try changing void _getMandelbrotPoints() async to Future _getMandelbrotPoints() async

0
votes

Even if calculateMandelbrotPoints() is an async function, and does alot of calculations, it doesn't mean it'll take too long to execute, and in this case it doesn't. It executes fast and you get the result instantly.

I wrote two functions for you to demonstrate this, you can play with them in this dart pad