缓慢颤动的 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
换成Image
或SizedBox
,这有点帮助。关于如何使这更快的任何建议?
【问题讨论】:
【参考方案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 不是一个好主意(因为它也会绘制不可见的项目)。自定义 RenderSliver 可能会更好 @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的主要内容,如果未能解决你的问题,请参考以下文章