You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter切换Tab时如何保持PlutoGrid的数据持久化?

解决嵌套Tab中PlutoGrid数据丢失问题

针对嵌套Tab切换后PlutoGrid输入数据丢失的问题,核心原因是Tab切换时非活跃页面会被销毁重建,且你之前的状态管理方式可能没覆盖到嵌套层级的状态持久化需求。以下是具体的解决方案和代码示例:

关键修复点

  1. 确保所有需要保留状态的页面正确使用AutomaticKeepAliveMixin
  2. 将PlutoGrid的数据源抽离到独立的状态管理类,避免随页面销毁丢失
  3. 给每个PlutoGrid指定唯一Key,避免状态混淆

1. 构建全局/页面级的数据管理类

把PlutoGrid的列、行数据抽离到ChangeNotifier类中,确保数据不依赖页面生命周期:

import 'package:pluto_grid/pluto_grid.dart';
import 'package:flutter/material.dart';

class GridDataManager extends ChangeNotifier {
  // 定义表格列
  final List<PlutoColumn> columns = [
    PlutoColumn(title: '序号', field: 'id', type: PlutoColumnType.text()),
    PlutoColumn(title: '内容', field: 'content', type: PlutoColumnType.text()),
  ];

  // 表格行数据
  List<PlutoRow> rows = [];

  // 更新单元格数据
  void updateCell(int rowIdx, String field, dynamic value) {
    rows[rowIdx] = rows[rowIdx].copyWith(
      cells: {
        ...rows[rowIdx].cells,
        field: PlutoCell(value: value),
      },
    );
    notifyListeners();
  }

  // 添加新行
  void addRow() {
    rows.add(
      PlutoRow(
        cells: {
          'id': PlutoCell(value: rows.length + 1),
          'content': PlutoCell(value: ''),
        },
      ),
    );
    notifyListeners();
  }
}

2. 外层Tab页面(处理顶层Tab切换)

class OuterTabPage extends StatefulWidget {
  const OuterTabPage({super.key});

  @override
  State<OuterTabPage> createState() => _OuterTabPageState();
}

class _OuterTabPageState extends State<OuterTabPage> with SingleTickerProviderStateMixin {
  late TabController _outerTabController;

  @override
  void initState() {
    super.initState();
    _outerTabController = TabController(length: 3, vsync: this);
  }

  @override
  void dispose() {
    _outerTabController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        bottom: TabBar(
          controller: _outerTabController,
          tabs: const [
            Tab(text: '主Tab1'),
            Tab(text: '主Tab2'),
            Tab(text: '主Tab3'),
          ],
        ),
      ),
      body: TabBarView(
        controller: _outerTabController,
        children: const [
          InnerTabContainer(),
          InnerTabContainer(),
          InnerTabContainer(),
        ],
      ),
    );
  }
}

3. 内层嵌套Tab页面(必须混入AutomaticKeepAliveMixin)

class InnerTabContainer extends StatefulWidget {
  const InnerTabContainer({super.key});

  @override
  State<InnerTabContainer> createState() => _InnerTabContainerState();
}

class _InnerTabContainerState extends State<InnerTabContainer> with AutomaticKeepAliveMixin, SingleTickerProviderStateMixin {
  late TabController _innerTabController;
  final GridDataManager _dataManager = GridDataManager();

  // 必须返回true,开启状态保留
  @override
  bool get wantKeepAlive => true;

  @override
  void initState() {
    super.initState();
    _innerTabController = TabController(length: 3, vsync: this);
  }

  @override
  void dispose() {
    _innerTabController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // 必须调用super.build,否则AutomaticKeepAlive不生效
    super.build(context);
    return ChangeNotifierProvider.value(
      value: _dataManager,
      child: Scaffold(
        body: TabBarView(
          controller: _innerTabController,
          children: const [
            PlutoGridTab(key: ValueKey('grid-tab-1')),
            PlutoGridTab(key: ValueKey('grid-tab-2')),
            PlutoGridTab(key: ValueKey('grid-tab-3')),
          ],
        ),
        bottomNavigationBar: TabBar(
          controller: _innerTabController,
          tabs: const [
            Tab(text: '子Tab1'),
            Tab(text: '子Tab2'),
            Tab(text: '子Tab3'),
          ],
        ),
      ),
    );
  }
}

4. PlutoGrid页面(保留状态+绑定数据源)

class PlutoGridTab extends StatefulWidget {
  const PlutoGridTab({super.key});

  @override
  State<PlutoGridTab> createState() => _PlutoGridTabState();
}

class _PlutoGridTabState extends State<PlutoGridTab> with AutomaticKeepAliveMixin {
  PlutoGridStateManager? _stateManager;

  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    final dataManager = Provider.of<GridDataManager>(context);
    return PlutoGrid(
      // 固定唯一Key,避免重建时状态混淆
      key: widget.key,
      columns: dataManager.columns,
      rows: dataManager.rows,
      onLoaded: (PlutoGridOnLoadedEvent event) {
        _stateManager = event.stateManager;
      },
      onChanged: (PlutoGridOnChangedEvent event) {
        // 同步数据到全局管理类
        dataManager.updateCell(event.rowIdx, event.field, event.value);
      },
      createHeader: (stateManager) {
        return ElevatedButton(
          onPressed: () {
            dataManager.addRow();
            stateManager.appendRows([dataManager.rows.last]);
          },
          child: const Text('添加行'),
        );
      },
    );
  }
}

注意事项

  • 所有需要保留状态的页面(内层Tab、Grid页面)都必须混入AutomaticKeepAliveMixin,且build方法中必须调用super.build(context)
  • 数据源必须脱离页面State,存储在独立的状态管理类中,确保页面销毁时数据不丢失
  • 每个PlutoGrid需要指定唯一Key,避免Flutter重建时复用错误的组件状态

内容的提问来源于stack exchange,提问作者Pannam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 22:43:17