【问题标题】:Slow Flutter GridView缓慢颤动的 GridView
【发布时间】:2020-08-14 05:30:28
【问题描述】:

我需要创建一个 52x80 的方格网格。它看起来像这样:

但是在模拟器中开发时性能特别慢(超过 1 秒“滞后”)。我知道 Flutter 代码在物理设备上的发布模式下运行得更快,如果设备是新的,在我的情况下也是如此。但是,如果设备已经使用了几年(即三星 Galaxy S8 或 iPhone 8),那么在加载视图和滚动时会出现令人沮丧的明显时间。而且我不能像那样发布我的应用程序。我正在像这样构建我的 GridView:

  GridView.count(
    shrinkWrap: true,
    primary: false,
    padding: const EdgeInsets.all(5.0),
    crossAxisCount: 52,
    crossAxisSpacing: 1.0,
    mainAxisSpacing: 1.0,
    addAutomaticKeepAlives: true,
    children: blocks.map((block) => // blocks is just a list of 4160 objects
      FlatButton(
        child: null,
        color: block.backgroundColor,
        onPressed: () {
          // open a new route
        },
        splashColor: Colors.transparent,  
        highlightColor: Colors.transparent
      )
    ).toList()
  )

我尝试将FlatButton 换成ImageSizedBox,这有点帮助。关于如何使这更快的任何建议?

【问题讨论】:

    标签: android ios flutter dart flutter-layout


    【解决方案1】:

    您可以使用 CustomPainter 小部件创建自己的 CustomGridView,并绘制所有项目 + 添加一个手势检测器并计算触摸位置,如果您需要将 onTap 行为添加到 blocs

    import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: Scaffold(
            backgroundColor: Colors.black,
            body: SafeArea(
              child: MyHomePage(),
            ),
          ),
        );
      }
    }
    
    final int redCount = 728;
    final int greyCount = 3021;
    final int allCount = 4160;
    final int crossAxisCount = 52;
    
    enum BlockTypes {
      red,
      gray,
      green,
      yellow,
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage()
          : blocks = List<BlockTypes>.generate(allCount, (index) {
              if (index < redCount) {
                return BlockTypes.red;
              } else if (index < redCount + greyCount) {
                return BlockTypes.gray;
              }
              return BlockTypes.green;
            });
    
      final List<BlockTypes> blocks;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      int columnsCount;
      double blocSize;
    
      int clickedIndex;
      Offset clickOffset;
      bool hasSizes = false;
      List<BlockTypes> blocks;
      final ScrollController scrollController = ScrollController();
    
      @override
      void initState() {
        WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
        blocks = widget.blocks;
        super.initState();
      }
    
      void _afterLayout(_) {
        blocSize = context.size.width / crossAxisCount;
        columnsCount = (allCount / crossAxisCount).ceil();
        setState(() {
          hasSizes = true;
        });
      }
    
      void onTapDown(TapDownDetails details) {
        final RenderBox box = context.findRenderObject();
        clickOffset = box.globalToLocal(details.globalPosition);
      }
    
      void onTap() {
        final dx = clickOffset.dx;
        final dy = clickOffset.dy + scrollController.offset;
        final tapedRow = (dx / blocSize).floor();
        final tapedColumn = (dy / blocSize).floor();
        clickedIndex = tapedColumn * crossAxisCount + tapedRow;
    
        setState(() {
          blocks[clickedIndex] = BlockTypes.yellow;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        print(blocSize);
        return hasSizes
            ? SingleChildScrollView(
                controller: scrollController,
                child: GestureDetector(
                  onTapDown: onTapDown,
                  onTap: onTap,
                  child: CustomPaint(
                    size: Size(
                      MediaQuery.of(context).size.width,
                      columnsCount * blocSize,
                    ),
                    painter: CustomGridView(
                      blocs: widget.blocks,
                      columnsCount: columnsCount,
                      blocSize: blocSize,
                    ),
                  ),
                ),
              )
            : Container();
      }
    }
    
    class CustomGridView extends CustomPainter {
      final double gap = 1;
      final Paint painter = Paint()
        ..strokeWidth = 1
        ..style = PaintingStyle.fill;
    
      final int columnsCount;
      final double blocSize;
      final List<BlockTypes> blocs;
    
      CustomGridView({this.columnsCount, this.blocSize, this.blocs});
    
      @override
      void paint(Canvas canvas, Size size) {
        blocs.asMap().forEach((index, bloc) {
          setColor(bloc);
          canvas.drawRRect(
              RRect.fromRectAndRadius(
                  Rect.fromLTWH(
                    getLeft(index),
                    getTop(index),
                    blocSize - gap,
                    blocSize - gap,
                  ),
                  Radius.circular(1.0)),
              painter);
        });
      }
    
      double getTop(int index) {
        return (index / crossAxisCount).floor().toDouble() * blocSize;
      }
    
      double getLeft(int index) {
        return (index % crossAxisCount).floor().toDouble() * blocSize;
      }
    
      @override
      bool shouldRepaint(CustomGridView oldDelegate) => true;
      @override
      bool shouldRebuildSemantics(CustomGridView oldDelegate) => true;
    
      void setColor(BlockTypes bloc) {
        switch (bloc) {
          case BlockTypes.red:
            painter.color = Colors.red;
            break;
          case BlockTypes.gray:
            painter.color = Colors.grey;
            break;
          case BlockTypes.green:
            painter.color = Colors.green;
            break;
          case BlockTypes.yellow:
            painter.color = Colors.yellow;
            break;
        }
      }
    }
    

    【讨论】:

    • 太棒了。这要好 100 倍。感谢您为我指明正确的方向!
    • SingleChildScrollView 不是一个好主意(因为它也会绘制不可见的项目)。自定义 RenderSliv​​er 可能会更好
    • @Rémi Rousselet。在 eth0 谈论 52x80 网格的问题中,它可以在一个屏幕中显示。
    【解决方案2】:

    要查看您的应用在设备上的运行速度如何,您可以在 Android Studio 的“配置文件”中运行它。它会像您在没有所有权限的情况下构建 appbundle 一样运行它。

    【讨论】:

      【解决方案3】:

      GridView.builder() 非常适合生成包含大量项目的网格视图。

      在这种情况下使用GridView.count 效率不高。而是使用GridView.builder,它将提高性能,因为项目将按需生成。

      看什么官方文档

      GridView.builder 构造创建了一个可滚动的 2D 小部件数组,这些小部件是按需创建的。 此构造函数适用于具有大量(或无限)子级的网格视图,因为仅对那些实际可见的子级调用构建器。

      参见下面的示例代码:

      GridView.builder(
                gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
                  // number of items on a row
                  crossAxisCount: 52,
                  // horizontal spacing between items
                  crossAxisSpacing: 3,
                  // vertical spacing between items
                  mainAxisSpacing: 5,
                ),
                itemBuilder: (BuildContext context, int index) {
                  // your list of objects here
                  return blocks();
                },
              ),
      

      我希望这会有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-08-30
        • 2021-07-06
        • 2018-10-19
        • 2020-12-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多