PlutoGrid数据更新后界面不渲染问题及文档咨询
PlutoGrid数据更新不渲染问题排查与解答
场景与代码说明
给定如下Flutter组件代码:
import 'package:cerebellum/const.dart'; import 'package:cerebellum/models/api/patientfinder.dart'; import 'package:cerebellum/models/api/patientfindrec.dart'; import 'package:cerebellum/utils/dateutil.dart'; import 'package:flutter/material.dart'; import 'package:flutter_it/flutter_it.dart'; import 'package:pluto_grid/pluto_grid.dart'; class PatientFinderGridScreen extends StatefulWidget { final List<PatientFindRec> patients; const PatientFinderGridScreen({super.key, required this.patients}); @override State<PatientFinderGridScreen> createState() = _PatientFinderGridScreenState(); } class _PatientFinderGridScreenState extends State<PatientFinderGridScreen> { final List<PlutoColumn> columns = []; final List<PlutoRow> rows = []; PlutoGridStateManager? stateManager; @override void initState() { super.initState(); _buildGridColumns(); _handleIncomingDataUpdate(); } @override void didUpdateWidget(covariant PatientFinderGridScreen oldWidget) { super.didUpdateWidget(oldWidget); // TODO: what if the signal's value was not replaced but altered??? if (widget.patients != oldWidget.patients) { _handleIncomingDataUpdate(); } } void _handleIncomingDataUpdate() { _buildGridRows(widget.patients); if (stateManager != null) { stateManager!.refRows.clear(); stateManager!.refRows.addAll(rows); stateManager!.notifyListeners(); } // Tell the data model that the current patient has changed. // Not important for this question di<PatientFinderModel>().patient.value = widget.patients.isEmpty ? null : widget.patients[0]; } void _buildGridRows(List<PatientFindRec> patientList) { rows.clear(); rows.addAll( patientList.map((patient) { return PlutoRow( cells: { 'id': PlutoCell(value: patient.id), // more cell values are here... }, ); }).toList(), ); } void _gridStateListener() { if (stateManager == null) return; final cr = stateManager!.currentRowIdx; // Tell the model that the current patient has changed // Not important for this question if (cr == null) { di<PatientFinderModel>().patient.value = null; } else { if (cr >= 0 && cr < widget.patients.length) { di<PatientFinderModel>().patient.value = widget.patients[cr]; } } } @override void dispose() { stateManager?.removeListener(_gridStateListener); super.dispose(); } void _onGridLoaded(PlutoGridOnLoadedEvent event) { stateManager = event.stateManager; stateManager!.setShowColumnFilter(false); // turn off in-memory filter stateManager!.addListener(_gridStateListener); // Tell the data model that the current patient has changed. di<PatientFinderModel>().patient.value = widget.patients.isEmpty ? null : widget.patients[0]; } void _buildGridColumns() { columns.addAll([ PlutoColumn( title: 'Identifier', field: 'id', type: PlutoColumnType.text(), enableFilterMenuItem: false, width: 140, ), // more columns are here... ]); } @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(8.0), child: PlutoGrid( columns: columns, rows: rows, onLoaded: _onGridLoaded, // this is a constant value taken from the const module, not important for this question configuration: PlutoGridConfiguration( localeText: plutoLocaleText, style: plutoGridStyle(context), shortcut: PlutoGridShortcut(), ), ), ); } }
注:PatientFindRec为freezed类,PatientFinderModel是通过Signal管理全局状态的实例。
问题详情
初始状态下patients为空列表,当将其更新为非空列表时,_PatientFinderGridScreenState._handleIncomingDataUpdate方法会被调用,且stateManager不为空,但PlutoGrid仅显示列标题,行数据未在界面渲染。
曾尝试以下修改,但均无效:
void _handleIncomingDataUpdate() { _buildGridRows(widget.patients); if (stateManager != null) { stateManager!.removeAllRows(); stateManager!.appendRows(rows); stateManager!.notifyListeners(); } }
若将组件从Widget树移除后重新挂载(传入初始非空的patients),网格可正常显示行数据,但该操作会导致PlutoGrid获取焦点,不符合需求(需保持焦点在搜索框中,因网格展示实时搜索结果)。
解答
1. 网格数据更新后未渲染的原因及解决方案
核心原因
- 错误的行数据更新方式:直接操作
stateManager.refRows或使用removeAllRows+appendRows未正确触发PlutoGrid的内部状态更新逻辑,导致UI未同步刷新。 - 列表更新判断逻辑缺陷:原
didUpdateWidget中仅通过widget.patients != oldWidget.patients判断列表是否变化,若仅修改原列表内容而非替换整个列表对象,该判断会失效,无法触发数据更新。
修复方案
(1)使用官方推荐的行更新方法
将_handleIncomingDataUpdate中的行更新逻辑替换为stateManager.setRows方法,该方法会自动处理内部状态同步并触发UI重绘:
void _handleIncomingDataUpdate() { _buildGridRows(widget.patients); if (stateManager != null) { stateManager!.setRows(rows); // 替换为setRows方法,内部已包含状态通知 } di<PatientFinderModel>().patient.value = widget.patients.isEmpty ? null : widget.patients[0]; }
(2)修复列表更新判断逻辑
导入package:flutter/foundation.dart中的listEquals,通过比较列表内容而非对象引用判断数据是否变化(因PatientFindRec是freezed类,默认支持正确的相等性判断):
import 'package:flutter/foundation.dart'; // 添加导入 @override void didUpdateWidget(covariant PatientFinderGridScreen oldWidget) { super.didUpdateWidget(oldWidget); // 替换原判断,确保列表内容变化时也能触发更新 if (!listEquals(widget.patients, oldWidget.patients)) { _handleIncomingDataUpdate(); } }
2. PlutoGrid相关文档资源
目前PlutoGrid的官方文档确实以API参考和演示项目为主,缺乏系统性的详细教程,可通过以下途径获取更多实用信息:
- 查看官方GitHub仓库的example目录:包含大量覆盖各种使用场景的示例代码,是学习的核心资源。
- 浏览GitHub仓库的Issues页面:很多常见问题已有成熟解决方案,可通过关键词搜索类似问题。
- 参与开发者社区交流:和其他使用PlutoGrid的开发者讨论经验,解决实际使用中的问题。
内容的提问来源于stack exchange,提问作者nagylzs
相关产品推荐
相关产品推荐

