Медленный флаттер GridView - PullRequest
       6

Медленный флаттер GridView

1 голос
/ 30 апреля 2020

Мне нужно создать сетку из квадратных блоков размером 52x80. Выглядит это так:

enter image description here

Однако при разработке в эмуляторе производительность снижается особенно медленно (более 1 с). Я понимаю, что код Flutter работает быстрее в режиме выпуска на физическом устройстве, что также верно в моем случае, если устройство новое-i sh. Но если устройству несколько лет (например, Samsung 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, что немного помогает. Любые предложения о том, как я мог бы сделать это быстрее?

Ответы [ 3 ]

1 голос
/ 03 мая 2020

Вы можете создать свой собственный CustomGridView с виджетом CustomPainter и нарисовать все элементы + добавить один детектор жестов и рассчитать место касания, если вам нужно добавить поведение onTap к блокам

enter image description here enter image description here

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;
    }
  }
}
0 голосов
/ 03 мая 2020

A 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();
          },
        ),

Надеюсь, это поможет.

0 голосов
/ 30 апреля 2020

Чтобы увидеть, как быстро ваше приложение будет работать на устройстве, вы можете запустить его в «Профиле» на Android Studio. Он будет запускаться так, как если бы вы создали пакет приложений без всех разрешений.

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...