flutter中key的使用

主要区分

维度 ValueKey UniqueKey PageStorageKey GlobalKey
类型 LocalKey LocalKey LocalKey(继承自 ValueKey) GlobalKey
相等性判定 值相等 (==) 永远不相等(每次新建都是全新随机身份) 值相等 (==) 全局唯一性(同一个 GlobalKey 实例在整个 App 中只能出现一次)
作用域 同一父组件的兄弟节点之间 同一父组件的兄弟节点之间 同一父组件的兄弟节点之间 整个 Widget 树全局
主要目的 按数据身份保持 Widget 状态,防止增删排序时状态错位 强制丢弃旧状态,完全重建 Widget 及子树 自动保存和恢复滚动组件的滚动位置(如列表、PageView) ① 跨任意层级保持状态移动 ② 直接访问子组件的 State / RenderObject
是否保持 Widget 复用 ✅ 是 ❌ 否(强制创建新 Element) ✅ 是(继承自 ValueKey,也具有复用能力) ✅ 是(全局匹配 Element)
是否自动保存滚动位置 ❌ 否 ❌ 否 ✅ 是(内建与 PageStorage 协作,保存滚动偏移) ❌ 否(需手动实现)
能否访问子 State ❌ 否 ❌ 否 ❌ 否 ✅ 是(通过 currentState)
性能影响 极小,局部比对 较大,破坏复用,造成额外重建 极小,等同于 ValueKey(额外的滚动位置存取开销可忽略) 较大,全局维护映射表,滥用会导致性能下降和状态混乱
典型用法场景 动态列表的增删重排:key: ValueKey(item.id) 强制重置表单、动画或重新创建组件:key: UniqueKey() 保持 Tab 切换后的列表滚动位置:key: PageStorageKey('myList') 表单验证、动画控制、获取子组件尺寸、跨树移动大组件

GlobalKey(访问子组件 State(表单验证为例))

通常作为全局作用域下widget对应的key,可以获取对应的currentState,如key.currentState 直接调用其方法(如 validate())、获取属性或触发刷新。

less 复制代码
import 'package:flutter/material.dart';

class GlobalKeyDemo extends StatelessWidget {
  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('GlobalKey 示例')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Form(
          key: _formKey,
          child: Column(
            children: [
              TextFormField(
                decoration: const InputDecoration(labelText: '用户名'),
                validator: (value) =>
                    value?.isEmpty ?? true ? '请输入用户名' : null,
              ),
              const SizedBox(height: 20),
              ElevatedButton(
                onPressed: () {
                  if (_formKey.currentState!.validate()) {
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(content: Text('验证通过')),
                    );
                  }
                },
                child: const Text('提交'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

ValueKey(保持列表状态并复用diff)

给每个列表项绑定一个与数据唯一标识相关的值(如 id),当列表顺序或数量变化时,框架通过 Key 找到对应的旧 Element 并复用,从而保留滚动位置、输入框内容、动画状态等。

scala 复制代码
import 'package:flutter/material.dart';

class Item {
  final String id;
  final String title;
  Item(this.id, this.title);
}

class ValueKeyDemo extends StatefulWidget {
  @override
  State<ValueKeyDemo> createState() => _ValueKeyDemoState();
}

class _ValueKeyDemoState extends State<ValueKeyDemo> {
  List<Item> items = [
    Item('1', 'Apple'),
    Item('2', 'Banana'),
    Item('3', 'Cherry'),
  ];

  void _removeItem(int index) {
    setState(() {
      items.removeAt(index);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('ValueKey 示例')),
      body: ListView.builder(
        itemCount: items.length,
        itemBuilder: (context, index) {
          final item = items[index];
          return ListTile(
            key: ValueKey(item.id), // 关键:用 id 作为 ValueKey
            title: Text(item.title),
            trailing: IconButton(
              icon: const Icon(Icons.delete),
              onPressed: () => _removeItem(index),
            ),
          );
        },
      ),
    );
  }
}

UniqueKey( 强制完全重建组件)

当需要主动丢弃一个 Widget 的所有内部状态(如动画、表单输入、滚动位置)并全新创建时,可以给该 Widget 一个 UniqueKey()。每次 build 时生成的新 UniqueKey 与旧的不等,框架将销毁旧 Element 并创建新的。

less 复制代码
import 'package:flutter/material.dart';

class UniqueKeyDemo extends StatefulWidget {
  @override
  State<UniqueKeyDemo> createState() => _UniqueKeyDemoState();
}

class _UniqueKeyDemoState extends State<UniqueKeyDemo> {
  // 通过改变这个 Key 来触发完全重建
  Key _resetKey = UniqueKey();

  void _forceRebuild() {
    setState(() {
      _resetKey = UniqueKey(); // 创建一个全新的 Key
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('UniqueKey 示例')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            // 目标组件:使用当前的 _resetKey
            Form(
              key: _resetKey,
              child: TextFormField(
                decoration: const InputDecoration(labelText: '输入内容'),
              ),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: _forceRebuild,
              child: const Text('重置表单状态'),
            ),
          ],
        ),
      ),
    );
  }
}

PageStorageKey(保留滚动位置)

less 复制代码
TabBarView(
  children: [
    ListView.builder(
      key: const PageStorageKey('fruits'), // 切换 Tab 后自动恢复滚动位置
      itemBuilder: (_, i) => ListTile(title: Text('水果 $i')),
    ),
    ListView.builder(
      key: const PageStorageKey('veggies'),
      itemBuilder: (_, i) => ListTile(title: Text('蔬菜 $i')),
    ),
  ],
)
相关推荐
萌新杰少2 小时前
Kuikly股票查看软件开发体验——SaiRen
android·kotlin·客户端
yuanlaile17 小时前
2026年全新Flutter 跨平台开发全栈学习资料整理
flutter·getx·鸿蒙 next·flutter开发鸿蒙·flutter入门实战·flutter全栈
恋猫de小郭1 天前
Genkit Dart 1.0 发布,Flutter 原生的 AI Agent 终于完整了
android·前端·flutter
第10086个小白1 天前
flutter桌面端打包方式(Inno Setup Compiler)版本
前端·flutter
陆断枫2 天前
Flutter 3.47 动画引擎盘点:Impeller 全平台,和那些"新特性"
flutter
程序员老刘2 天前
Flet 1.0 思路很好,可惜来晚了
flutter·ai编程·客户端
m0_738185822 天前
Flutter 鸿蒙化实战:qr_code_scanner_plus 适配 OpenHarmony,二维码扫描
flutter·华为·harmonyos·鸿蒙
恋猫de小郭2 天前
Flutter + EmbeddingGemma 2,谷歌发布完全端侧的 AI Edge Foresight
android·前端·flutter
君赏3 天前
基于 FlutterPatch 快速接入 Flutter 热更新
flutter
律宏阔3 天前
Dart FFI 内存管理:用 using + Arena 替代嵌套 try-finally
前端·flutter