Flutter async stuttering with FutureBuilder

51 views Asked by At

So i'm trying to make a Voronoi Diagram application using image library. So this algoritm is quite slow, so when the button is pressed I want my not to stutter and show CircularProgressindicator, and then when computions are finished and Image is ready I want to see the image from Uint8List

class _VoronoiPageState extends State<VoronoiPage> {

  final TextEditingController textEditingControllerWidth = TextEditingController();
  final TextEditingController textEditingControllerHeight = TextEditingController();
  final TextEditingController textEditingControllerStations = TextEditingController();
  Future<Uint8List> ?diagramImage;
  

  Uint8List createDiagram() {
    
    int width = 0;
    int height = 0;
    int basedStation = 0;
    setState(() {
    width = int.tryParse(textEditingControllerWidth.text) ?? 0;
    height = int.tryParse(textEditingControllerHeight.text) ?? 0;
    basedStation = int.tryParse(textEditingControllerStations.text) ?? 0;
    });
    
    // Creating new image
    img.Image diagram = img.Image(width: width, height: height);
    print('image created');
    List<DrawPixels> baseStationList = generatePixels(basedStation, width, height);
    for (var pixel in baseStationList) {
      pixel.color = generateRandomColor();
    }
    // Drawingf the diagram
    for (int i = 0; i < width; i++) {
      for (int j = 0; j < height; j++) {
        double minDistance = double.infinity;
        int minIndex = -1;
        for (int k = 0; k < basedStation; k++) {
          double distance = calculateDistance(i, j, baseStationList[k].x, baseStationList[k].y);
          if (distance < minDistance) {
            minDistance = distance;
            minIndex = k;
          }
        }
        img.drawPixel(diagram, i, j, baseStationList[minIndex].color);
      }
    }

    for (var pixel in baseStationList) {
      img.fillCircle(diagram, x: pixel.x, y: pixel.y, radius: 3, color: img.ColorFloat16.rgb(255, 255, 255));
    }
    return img.encodePng(diagram); 
  }
  Future<Uint8List> drawDiagram() async {
    try {
    final data = await Future.value(createDiagram());
    return data;
    } catch (e) {
      throw Exception('pizdec');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Voronoi Diagram'),
      ),
      body: Center(
        child: Column(
          children: [
            FutureBuilder(
              future: diagramImage,
              builder: (context, AsyncSnapshot<Uint8List> snapshot) {
                if (snapshot.hasData == false) {
                  return const Icon(Icons.image, );
                }
                if (snapshot.connectionState == ConnectionState.waiting) {
                  print('connection state waiting');
                  return const CircularProgressIndicator();
                }
                print('begin');
                return Image.memory(snapshot.data!);
              },
            ),
            MyTextField(
              hintText: 'Enter width', 
              textEditingController: textEditingControllerWidth,
            ),
            MyTextField(
              hintText: 'Enter heigth', 
              textEditingController: textEditingControllerHeight,
            ),
            MyTextField(
              hintText: 'Enter amount of stations', 
              textEditingController: textEditingControllerStations,
            ),
            ElevatedButton(
              onPressed: () {
               diagramImage = drawDiagram();
              },
              child: const Text('Create diagram'),
            )
          ],
        ),
      ),
    );
  }
}

I tried using FutureBuilder, but somehow connectionState = ConnectionState.waiting is showing only when everything is ready, so my app is just stuttering until all computions are done.

1

There are 1 answers

12
Emanoel Aleixo On

You are calling the setState inside the function, but you need to rebuild the app when click the button.

Edit 2: Based on your project at GB, change your drawDiagrama function to

Future<void> drawDiagram() async {
    setState(() {
      diagramImage = null;
    });
    final data = await compute(createDiagram, [width, height, basedStation]);
    setState(() {
      diagramImage = Future.value(data);
    });
}

Edit 2:

Change the if inside your FutureBuilder to

if (snapshot.connectionState != ConnectionState.done) {
  print('connection state waiting');
  return const CircularProgressIndicator();
}
if (snapshot.hasData == false) {
  return const Icon(
    Icons.image,
  );
}