更新 GridView 列而不重建子项

问题描述 投票:0回答:1

我正在尝试构建一个

GridView
,它可以在应用放大/缩小手势时更改其列。

但每当这种情况发生时,它的孩子就会被重建/刷新。我想这是预料之中的,因为 GridView 的状态发生了变化。有什么办法可以防止这种情况发生吗?

这是相关代码:

double scale = 2.0, previousScale = 1.0;
int columns = 2;

return GestureDetector(
        onScaleStart: (details) {
          previousScale = scale;
        },
        onScaleUpdate: (details) {
          scale = previousScale * details.scale;
          scale = scale.clamp(1.0, 6.0);
          var newColumnsCount = (6 - scale).round().clamp(1, 6);
          if (columns != newColumnsCount) {
            setState(() {                       // <===== setState on columns
              columns = newColumnsCount;
            });
          }
        },
        onScaleEnd: (details) {
          previousScale = 1.0;
        },
        child: Stack(
          children: [
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: GridView.builder(
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: columns,       // <===== columns is used here
                  mainAxisSpacing: 4,
                  crossAxisSpacing: 4,
                ),
                itemCount: ...,
                itemBuilder: ...,
              )
            )
          ]
        )
      );
...

当列更改时,我不希望重建项目。我尝试在物品上使用

RepaintBoundary
,但没有成功。

flutter optimization gridview
1个回答
0
投票

这里有一个优化的解决方案,可防止 GridView 项目在缩放手势期间重建。关键是使用

RepaintBoundary
ValueKey
和预缓存项目的组合。
RepaintBoundary
有助于最大限度地减少重绘,而
ValueKey
有助于 Flutter 优化重建。通过预先生成并缓存
initState()
中的项目,我们可以防止网格布局更改时不必要的重建。该解决方案还包括平滑的缩放手势,可在保持项目状态的同时调整列数。我已经提供了一个完整的实现,您可以轻松地将其集成到您的项目中。该代码使用适当的状态管理来确保仅在必要时进行有效更新。

import 'package:flutter/material.dart';

class ZoomableGridView extends StatefulWidget {
  final List<Widget> items;

  const ZoomableGridView({Key? key, required this.items}) : super(key: key);

  @override
  _ZoomableGridViewState createState() => _ZoomableGridViewState();
}

class _ZoomableGridViewState extends State<ZoomableGridView> {
  double _scale = 2.0;
  double _previousScale = 1.0;
  int _columns = 2;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onScaleStart: (details) {
        _previousScale = _scale;
      },
      onScaleUpdate: (details) {
        setState(() {
          // Calculate new scale with clamping
          _scale = (_previousScale * details.scale).clamp(1.0, 6.0);
          
          // Calculate new columns based on scale
          _columns = (6 - _scale).round().clamp(1, 6);
        });
      },
      onScaleEnd: (details) {
        _previousScale = 1.0;
      },
      child: GridView.builder(
        // Unique key to help Flutter optimize rebuilds
        key: ValueKey(_columns),
        
        // Customize grid delegate
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: _columns,
          mainAxisSpacing: 8,
          crossAxisSpacing: 8,
          childAspectRatio: 1.0, // Square items
        ),
        
        // Item count and builder
        itemCount: widget.items.length,
        itemBuilder: (context, index) {
          // Wrap each item with RepaintBoundary
          return RepaintBoundary(
            child: widget.items[index],
          );
        },
      ),
    );
  }
}

// Example Usage
class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Create a list of items to display in the grid
    final List<Widget> gridItems = List.generate(
      20, 
      (index) => _GridItemWidget(index: index),
    );

    return Scaffold(
      appBar: AppBar(title: Text('Zoomable GridView')),
      body: ZoomableGridView(items: gridItems),
    );
  }
}

// Custom grid item widget to demonstrate state preservation
class _GridItemWidget extends StatefulWidget {
  final int index;

  const _GridItemWidget({Key? key, required this.index}) : super(key: key);

  @override
  __GridItemWidgetState createState() => __GridItemWidgetState();
}

class __GridItemWidgetState extends State<_GridItemWidget> {
  bool _isSelected = false;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        setState(() {
          _isSelected = !_isSelected;
        });
      },
      child: Container(
        decoration: BoxDecoration(
          color: _isSelected 
            ? Colors.blue.shade200 
            : Colors.blue.shade100,
          borderRadius: BorderRadius.circular(10),
        ),
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                'Item ${widget.index}',
                style: TextStyle(
                  fontWeight: FontWeight.bold,
                  color: Colors.white,
                ),
              ),
              SizedBox(height: 8),
              Icon(
                _isSelected ? Icons.check_circle : Icons.circle_outlined,
                color: Colors.white,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

如果您需要任何说明或对实施的特定部分有疑问,请告诉我!

© www.soinside.com 2019 - 2024. All rights reserved.