Flutter 中的 setState() 方法直到 for 循环中的最后一次调用才更新视图

setState() method in Flutter is not updating the view until the last call in a for loop

问题

我正在尝试使用 Flutter 构建一个排序可视化工具。我已经使用 CustomPainter 绘制与我打算排序的整数列表的值相对应的垂直线。 该应用程序设置为当用户单击 DropDownButton 中的 "Bubble sort" 选项时 (我打算稍后添加更多排序算法)CustomPainter 中的垂直线 Canvas 必须自行排序。

代码

import 'dart:io';
import 'dart:math';

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

void main() => runApp(HomePage());

class HomePage extends StatefulWidget {
  @override
  HomePageSate createState() {
    return HomePageState();
  }
}

class HomePageState extends State<HomePage> {
  List<int> arr;

  @override
  void initState() {
    super.initState();
    arr = _getRandomIntegerList();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Sorting Visualiser'),
          actions: <Widget>[
            DropdownButton<String>(
              onChanged: (dynamic choice) {
                switch (choice) {
                  case 'Bubble Sort':
                    _bubbleSortVisualiser();
                    break;
                }
              },
              items: <String>['Bubble Sort']
                  .map<DropdownMenuItem<String>>((String value) {
                return DropdownMenuItem<String>(
                  value: value,
                  child: Text(value),
                );
              }).toList(),
            )
          ],
        ),
        //The following flatButton refreshes arr to have a new array of random integers.
        bottomNavigationBar: FlatButton(
          onPressed: () {
            setState(() {
              arr = _getRandomIntegerList();
            });
          },
          child: Icon(Icons.refresh),
        ),
        body: CustomPaint(
          size: Size(double.infinity, double.infinity),
          painter: SortingCanvas(arr),
        ),
      ),
    );
  }

  //function to sort the list using bubble sort and repaint the canvas at every iteration.
  void _bubbleSortVisualiser() {
    print('Bubble sort function called');
    List<int> bubbleArr = List.from(arr);

    for (int i = 0; i < bubbleArr.length - 1; i++) {
      for (int j = 0; j < bubbleArr.length - 1 - i; j++) {
        int temp;
        if (bubbleArr[j] < bubbleArr[j + 1]) {
          temp = bubbleArr[j];
          bubbleArr[j] = bubbleArr[j + 1];
          bubbleArr[j + 1] = temp;
          //Every time arr changes setState() is called to visualise the changing array.
          setState(() {
            arr = List.from(bubbleArr);
            print("Updated to : $arr");
          });

          //Sleep is called so that the change is noticeable
          sleep(Duration(seconds: 1));

        }
      }
    }
  }
}

class SortingCanvas extends CustomPainter {
  List<int> arr;

  SortingCanvas(this.arr);

  @override
  void paint(Canvas canvas, Size size) {
    var linePaint = Paint()
      ..color = Colors.black
      ..style = PaintingStyle.stroke
      ..strokeWidth = 5.0
      ..isAntiAlias = true;
    //IMP the first offset is the bottom point and the second is the top point of the vertical line. 
    //It is offset from the top left corner of the canvas
    for (int i = 1; i <= arr.length; i++) {
      canvas.drawLine(Offset(50.0 + (20 * i), size.height - 50),
          Offset(50.0 + (20 * i), 50.0 * arr[i - 1]), linePaint);
    }
  }

  @override
  bool shouldRepaint(SortingCanvas oldDelegate) {
    return !listEquals(this.arr, oldDelegate.arr);
  }
}

//Helper function to get a list of 10 random integers
List<int> _getRandomIntegerList() {
  List<int> arr = [];
  Random rng = new Random();

  for (int i = 0; i < 10; i++) {
    arr.add(rng.nextInt(10));
  }
  return arr;
}

然而,最终发生的是只有最终排序的 arr 在 canvas 上可视化并且由于某种原因跳过了所有中间步骤。但是日志显示列表正在更新。

截图

带有随机列表的初始屏幕 点击下拉按钮中的冒泡排序 排序后的列表

添加 sleep() 功能只是为了模拟动画,我打算在解决此问题后将其替换为适当的动画。另外,我知道该应用程序有点难看,但我想在任何 UI 润色之前弄清楚逻辑。

这一行:

class HomePageState extends State<StatefulWidget> {

应该是

class HomePageState extends State<HomePage> {

就是这样。那么它应该可以工作了。

另外,你应该改变这个:

  @override
  State<StatefulWidget> createState() {
    return HomePageState();
  }

至:

  @override
  State<HomePage> createState() => HomePageState();

尝试使用Future.delay喜欢

      void _bubbleSortVisualiser() async {
        print('Bubble sort function called');
        List<int> bubbleArr = List.from(arr);

        for (int i = 0; i < bubbleArr.length - 1; i++) {
          for (int j = 0; j < bubbleArr.length - 1 - i; j++) {
            int temp;
            if (bubbleArr[j] < bubbleArr[j + 1]) {
              temp = bubbleArr[j];
              bubbleArr[j] = bubbleArr[j + 1];
              bubbleArr[j + 1] = temp;
              //Every time arr changes setState() is called to visualise the changing array.
    await Future.delayed(const Duration(seconds: 1), () {
      setState(() {
                arr = List.from(bubbleArr);
                print("Updated to : $arr");
              });
    });




        }
      }
    }
  }
}