【问题标题】:setState() method in Flutter is not updating the view until the last call in a for loopFlutter 中的 setState() 方法直到 for 循环中的最后一次调用才更新视图
【发布时间】:2020-04-01 20:19:18
【问题描述】:

问题

我正在尝试使用 Flutter 构建排序可视化工具。我使用了CustomPainter 来绘制与我打算排序的整数列表的值相对应的垂直线。 该应用程序设置为当用户单击DropDownButton 中的“冒泡排序”选项时 (我打算稍后添加更多的排序算法)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 在画布上可视化,并且由于某种原因跳过了所有中间步骤。但是日志显示列表正在更新。

截图

随机列表的初始画面 单击下拉按钮中的冒泡排序 排序的列表

添加sleep() 函数只是为了模拟动画,我打算在此问题解决后将其替换为适当的动画。此外,我知道该应用程序有点丑陋,但我想在任何 UI 优化之前获得逻辑。

【问题讨论】:

    标签: flutter dart setstate statefulwidget


    【解决方案1】:

    尝试使用 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");
                  });
        });
    
    
    
    
            }
          }
        }
      }
    }
    

    【讨论】:

    • 是的,它有效!谢谢。毕竟使用 sleep() 是错误的举动。
    【解决方案2】:

    这一行:

    class HomePageState extends State<StatefulWidget> {
    

    应该是

    class HomePageState extends State<HomePage> {
    

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

    另外,你应该改变这个:

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

    到:

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

    【讨论】:

    • 感谢您指出这些错误。我已经纠正了它们,但问题仍然存在。我正在寻找的是一种将中间重绘延迟到有状态小部件的方法。无论如何,我已经根据您的更正编辑了问题中的代码。应用的运行方式是:我按下下拉按钮,应用看似冻结了一段时间,然后屏幕上的数组直接转换为排序后的数组
    • 你认为什么 setState() 是问题?另外,您是否尝试过完全重启应用程序?
    • _bubbleSortVisualizer() 函数中的 setState()。我用它来用部分排序的列表更新列表成员变量。是的,我尝试重新启动应用程序,但无济于事
    • 尝试将List.from(bubbleArr)提取到它上面的自己的变量中(不在setState()中)并将arr设置为等于它。
    • 或者更好的办法是修改_bubbleSortVisualizer()的返回类型,返回List.from(bubbleArr),设置调用方法的状态。
    猜你喜欢
    • 2022-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-11
    • 1970-01-01
    • 2016-05-16
    相关资源
    最近更新 更多