一个例子直观的告诉你flutter中key的作用

笔者之前是java后端开发,转flutter之后又被公司裁员,当时对flutter知之甚少,去面试没看面经遂被第一题考倒:flutter中widget的key有什么作用?

查阅了网上很多描述都不太理解,最近在某视频学习网站看了一个讲解视频才略有了解。

没有key的情况下

先上代码,以下代码自定义了组件Box,Box中用GestureDetector包裹了一个有定义颜色的Container,每点击Box一次就会使的里面的数字+1.

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

void main() {
  runApp(
    MaterialApp(
      home: MyHome(),
    ),
  );
}

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

  @override
  State<MyHome> createState() => _MyHomeState();
}

class _MyHomeState extends State<MyHome> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          children: [
            Box(Colors.red),
            Box(Colors.yellow),
            Box(Colors.blue),
          ],
        ),
      ),
    );
  }
}

class Box extends StatefulWidget {
  const Box(this.color); // 接收并传递 key
  final Color color;

  @override
  State<Box> createState() => _BoxState();
}

class _BoxState extends State<Box> {
  int _counter = 0;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        _counter++;
        setState(() {});
      },
      child: GestureDetector(
        onTap: () {
          _counter++;
          setState(() {});
        },
        child: Container(
          width: 80,
          height: 80,
          color: widget.color,
            child: Text("$_counter"),
          ),
      ),
    );
  }
}

把以上代码运行起来,再从上往下依次点击1、2、3次,我们会得到这么一个页面

此时我们再把_MyHomeState中的builder方法里的Box(Colors.yellow)注释掉,再hot reload一下,各位读者可以猜下页面会变成什么样

js 复制代码
class _MyHomeState extends State<MyHome> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          children: [
            Box(Colors.red),
            // Box(Colors.yellow),
            Box(Colors.blue),
          ],
        ),
      ),
    );
  }
}

如果各位读者像我一样对flutter的key不甚了解,可能会认为,页面上从上往下依次是:红1、蓝3,然而页面变成了这样

为什么会变成这样呢,这正是因为没有指定key导致的。如下图所示,我们在创建widget树的时候也会创建对应的element树,而状态是保存在elemnet树中的

当我们删除了黄色的box之后,蓝色的box就顺理成章上移动一格,匹配到右边的的state = 2的box element。而state = 3的boxelement因为找不到对应的widget也被释放掉了。

正是因为上图所说没匹配到的elemnet被释放了,我们如果把之前注释的代码加回来,再hot reload一下,得到的不是红1、黄2、蓝3,而是红1、黄2、蓝0(初始值为0)

添加了key的情况下

简单修改代码(26、27、28、37行),给Box传递参数key

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

void main() {
  runApp(
    MaterialApp(
      home: MyHome(),
    ),
  );
}

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

  @override
  State<MyHome> createState() => _MyHomeState();
}

class _MyHomeState extends State<MyHome> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          children: [
            Box(Colors.red, key: ValueKey(1),),
            Box(Colors.yellow, key: ValueKey(2),),
            Box(Colors.blue, key: ValueKey(3),),
          ],
        ),
      ),
    );
  }
}

class Box extends StatefulWidget {
  const Box(this.color, {super.key});
  final Color color;

  @override
  State<Box> createState() => _BoxState();
}

class _BoxState extends State<Box> {
  int _counter = 0;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        _counter++;
        setState(() {});
      },
      child: GestureDetector(
        onTap: () {
          _counter++;
          setState(() {});
        },
        child: Container(
          width: 80,
          height: 80,
          color: widget.color,
          child: Text("$_counter"),
        ),
      ),
    );
  }
}

运行程序,从上往下分别点击1、2、3次,得到红1黄2蓝3

此时再把黄色的box注释掉,hot reload一下,就会得到红1蓝3,符合我们的预期

这是因为加了key之后,widget和element不再通过顺序匹配了,而是通过key来匹配,所以这次是key=2的element被释放了

相关推荐
程序员老刘·12 小时前
从短剧App的bug看响应式编程在AI时代的优势
flutter·ai编程·跨平台开发
传奇开心果编程1 天前
【Flutter踩坑排雷】第7课:巨型 build 函数坑|全部 UI 写在同一个 build,维护难、隐性异常
android·flutter·ios·移动开发·跨平台·dart·声明式ui
程序员老刘1 天前
从短剧App的bug看响应式编程在AI时代的优势
flutter·ai编程·客户端
恋猫de小郭1 天前
最近 Android 有什么更新:Android Dev Summit 2026 技术解读
android·前端·flutter
好的佩奇2 天前
Dart 之类修饰符
android·flutter·dart
好的佩奇012 天前
Dart 之类修饰符
android·flutter·dart
Jewel1052 天前
文字为什么总是不居中!记录一个bug
android·flutter·ios
2601_949847753 天前
Form-data 跨端三方库仓颉适配全流程
前端·javascript·flutter
大龄秃头程序员3 天前
Flutter 中我为什么把 GetX 定位为高效率方案,而不是默认架构方案
flutter
传奇开心果编程3 天前
组合优先开发范式:从汉字造字原理到声明式UI组件组合思想
android·flutter·ios·swiftui·移动开发·harmonyos·android jetpack