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
,但没有成功。
这里有一个优化的解决方案,可防止 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,
),
],
),
),
),
);
}
}
如果您需要任何说明或对实施的特定部分有疑问,请告诉我!