缓慢颤动的 GridView

Posted

技术标签:

【中文标题】缓慢颤动的 GridView【英文标题】:Slow Flutter 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,这有点帮助。关于如何使这更快的任何建议?

【问题讨论】:

【参考方案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();
          ,
        ),

我希望这会有所帮助。

【讨论】:

以上是关于缓慢颤动的 GridView的主要内容,如果未能解决你的问题,请参考以下文章

设置颤动路径时遇到问题 - 找不到颤动命令

如何通过颤动中的设备位置获取颤动中的时区、语言和县 ID?

由于未能安装SDK而无法“颤动”,但颤动的医生运行正常

我想在颤动中用手电筒录制视频,任何人都知道颤动中的任何插件或方法

小部件对颤动的故障影响?

在颤动中强制纵向视图