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')),
    ),
  ],
)
相关推荐
常在士心19 小时前
Flutter Dio高阶封装&多级缓存策略
flutter
司素网20 小时前
Flutter Platform Channel 的 await 陷阱:为什么加与不加相差 100ms?
flutter
zengorange21 小时前
Dart语言
学习·flutter
YM52e1 天前
鸿蒙Flutter Stack堆叠布局:实现多层级界面
学习·flutter·华为·harmonyos·鸿蒙·鸿蒙系统
帅次1 天前
Android 高级工程师面试:Flutter 渲染与性能 近1年高频追问 20 题
android·flutter·面试·渲染·性能
世人万千丶1 天前
鸿蒙Flutter Icon与IconButton
flutter·华为·harmonyos·鸿蒙
程序员老刘1 天前
跨平台开发地图 | 2026年7月
flutter·ai编程·客户端
世人万千丶1 天前
Flutter 鸿蒙Text组件详解
flutter·华为·harmonyos·鸿蒙·鸿蒙系统
世人万千丶1 天前
鸿蒙Flutter Image图片组件
flutter·华为·harmonyos·鸿蒙·鸿蒙系统