一个例子直观的告诉你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被释放了

相关推荐
奋斗的小青年!!1 天前
Flutter浮动按钮在OpenHarmony平台的实践经验
flutter·harmonyos·鸿蒙
程序员老刘1 天前
一杯奶茶钱,PicGo + 阿里云 OSS 搭建永久稳定的个人图床
flutter·markdown
奋斗的小青年!!1 天前
OpenHarmony Flutter 拖拽排序组件性能优化与跨平台适配指南
flutter·harmonyos·鸿蒙
小雨下雨的雨1 天前
Flutter 框架跨平台鸿蒙开发 —— Stack 控件之三维层叠艺术
flutter·华为·harmonyos
行者961 天前
OpenHarmony平台Flutter手风琴菜单组件的跨平台适配实践
flutter·harmonyos·鸿蒙
小雨下雨的雨1 天前
Flutter 框架跨平台鸿蒙开发 —— Flex 控件之响应式弹性布局
flutter·ui·华为·harmonyos·鸿蒙系统
cn_mengbei1 天前
Flutter for OpenHarmony 实战:CheckboxListTile 复选框列表项详解
flutter
cn_mengbei1 天前
Flutter for OpenHarmony 实战:Switch 开关按钮详解
flutter
奋斗的小青年!!1 天前
OpenHarmony Flutter实战:打造高性能订单确认流程步骤条
flutter·harmonyos·鸿蒙
Coder_Boy_1 天前
Flutter基础介绍-跨平台移动应用开发框架
spring boot·flutter