flutter key的使用 和 原理

 Flutter Key 的作用:

当你有一系列的 widget    使用的是同样的类; 不给Flutter 传 Key的话,Flutter 有时候就会分不清楚他们的对应关系。尤其是widget 顺序发生改变的时候。这个是就需要我们通过 Key 来标识 Widget

在flutter 中 运行中的 widget 是不能发生改变的,只能通过  改变State 丢弃之前的 widget rebuild 重绘得到新的Widget ,   运行中的三棵树,我们先讲前两棵

➥ Widget Tree    类似蓝图描述布局

➥ Element Tree  WidgetTree 实例对象, 管理 widget 中的 State 信息,实现动态改变


如果通过直接赋值 修改的Widget 属性,可以直接发生变化(在热加载阶段如果依赖 Element 的 State (通过变量赋值的属性)则 要通过 Widget key 在 同级别 Element Tree 中寻找对应的 State 才能发生变化,若没有 Key 则  通过 类型判断寻找 State 变量,(在同级有同类型Widget 是 容易出错;

此外:

  • StateLessWidget 没有 State 状态
  • StateFullWidget   是有 State 状态的

总得来说Flutter 可以借助 Key 来追踪 Widget (同类型 同级别),  同级没有 key,  则通过类型匹配 若同级有多个同类型 则可能会出错


Flutter Key 的类型:

Ⅰ.  Local Key  :     (局部key):   同级别的唯一性,

  • ValueKey        可以是 字符数值等等 值相等
  • ObjectKey       引用地址
  • UniqueKey      不需要参数

Ⅱ.  Global Key:     (全局可以):  全局 唯一             相对慢

  • GlobalKey

简单的使用:通过 key 找到对应的 widget,并调用方法


final widget = _globalKey2.currentWidget as Counter;
//widget 访问变量

final render =
    element.currentContext!.findRenderObject() as RenderBox;
print('render.size :${render.size}');
print('render.size :${render.localToGlobal(Offset.zero)}');


final state = _globalKey2.currentState as _CounterState;
state.setState(() {
  state.count--;
});

完整脚本 :

import 'dart:math';

import 'package:flutter/material.dart';

class PageKey extends StatefulWidget {
  const PageKey({Key? key}) : super(key: key);

  @override
  _PageKeyState createState() => _PageKeyState();
}

class _PageKeyState extends State<PageKey> {
  final GlobalKey _globalKey1 = GlobalKey();
  final GlobalKey _globalKey2 = GlobalKey();

  @override
  Widget build(BuildContext context) {
    final Map args = ModalRoute.of(context)!.settings.arguments as Map;
    return Scaffold(
      appBar: AppBar(
        title: Text(args['title']),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Counter(key: _globalKey1),
            Counter(key: _globalKey2),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
          onPressed: () {
            final state = _globalKey2.currentState as _CounterState;
            state.setState(() {
              state.count--;
            });
            for (var element in [_globalKey2, _globalKey2]) {
              final render =
                  element.currentContext!.findRenderObject() as RenderBox;
              print('render.size :${render.size}');
              print('render.size :${render.localToGlobal(Offset.zero)}');
            }

            final widget = _globalKey2.currentWidget as Counter;
            //widget 访问变量

            // final render =
            //     _globalKey2.currentContext!.findRenderObject() as RenderBox;
            // print('render.size :${render.size}');
            // print('render.size :${render.localToGlobal(Offset.zero)}');
          },
          child: const ClipOval(
            child: Icon(
              Icons.add_circle_outline,
              size: 20,
            ),
          )),
    );
  }
}

class Counter extends StatefulWidget {
  Counter({Key? key}) : super(key: key);
  final color = Colors.primaries[Random().nextInt(Colors.primaries.length)];

  @override
  _CounterState createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int count = 0;
  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () {
        setState(() {
          ++count;
        });
      },
      child: Card(
        elevation: 0,
        child: Container(
          width: 88,
          height: 88,
          color: widget.color,
          child: Center(
            child: Text(
              '$count',
              style: TextStyle(fontSize: 25),
            ),
          ),
        ),
      ),
    );
  }
}

Flutter  中 ReorderableListView 的使用 key的实际使用1

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

class PageKeyExample extends StatefulWidget {
  const PageKeyExample({Key? key}) : super(key: key);

  @override
  _PageKeyExampleState createState() => _PageKeyExampleState();
}

class _PageKeyExampleState extends State<PageKeyExample> {
  List<Widget> boxs = [
    Box(key: UniqueKey(), color: Colors.blue.shade100),
    Box(key: UniqueKey(), color: Colors.blue.shade200),
    Box(key: UniqueKey(), color: Colors.blue.shade300),
    Box(key: UniqueKey(), color: Colors.blue.shade400),
    Box(key: UniqueKey(), color: Colors.blue.shade500),
    Box(key: UniqueKey(), color: Colors.blue.shade600),
  ];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('AppBar'),
      ),
      body: ReorderableListView(
        onReorder: (int oldIndex, int newIndex) {
          if (newIndex > oldIndex) newIndex--;
          Widget box = boxs.removeAt(oldIndex);
          boxs.insert(newIndex, box);
        },
        children: boxs,
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            boxs.shuffle();
          });
        },
        child: const Icon(
          Icons.refresh,
          size: 40,
        ),
      ),
    );
  }
}

class Box extends StatelessWidget {
  final Color color;
  const Box({required Key key, required this.color}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
      width: 40,
      height: 88,
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(3),
        color: color,
      ),
    );
  }
}

拖动组件的使用:Draggable key的实际使用2

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

class PageKeyExample extends StatefulWidget {
  const PageKeyExample({Key? key}) : super(key: key);

  @override
  _PageKeyExampleState createState() => _PageKeyExampleState();
}

class _PageKeyExampleState extends State<PageKeyExample> {
  final List<Color> _colors = [
    Colors.blue.shade100,
    Colors.blue.shade200,
    Colors.blue.shade300,
    Colors.blue.shade400,
    Colors.blue.shade500,
    Colors.blue.shade600,
  ];
  int _slot = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('AppBar'),
      ),
      body: Listener(
        onPointerMove: (PointerMoveEvent event) {
          //print('${event.position}');
          final y = event.position.dy;
          // print('position: $y');
          if (y > (_slot + 1) * 88) {
            // print("------------> $_slot");
            if (_slot == _colors.length - 1) return;
            setState(() {
              final c = _colors[_slot];
              _colors[_slot] = _colors[_slot + 1];
              _colors[_slot + 1] = c;
              _slot++;
            });
          } else if (y < _slot * 88) {
            // print("------------> $_slot");
            if (_slot == 0) return;
            setState(() {
              final c = _colors[_slot];
              _colors[_slot] = _colors[_slot - 1];
              _colors[_slot - 1] = c;
              _slot--;
            });
          }
        },
        child: Stack(
            children: List.generate(_colors.length, (index) {
          return Box(
            color: _colors[index],
            index: index,
            onDragEnd: (DraggableDetails details) {
              print("$index  is onDragEnd  ${details.offset}");

              // Future.delayed(Duration(seconds: 2), () {
              //   setState(() {
              //     final c = _colors[2];
              //     _colors[2] = _colors[3];
              //     _colors[3] = c;
              //   });
              // });
            },
            onDragStarted: (color) {
              _slot = _colors.indexOf(color);
              print(
                  "----------------------${_colors.indexOf(color)}  is onDragStarted");
            },
            key: ValueKey(_colors[index]),
          );
        })),
      ),
      // body: ReorderableListView(
      //   onReorder: (int oldIndex, int newIndex) {
      //     if (newIndex > oldIndex) newIndex--;
      //     Widget box = boxs.removeAt(oldIndex);
      //     boxs.insert(newIndex, box);
      //   },
      //   children: boxs,
      // ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            _colors.shuffle();
          });
        },
        child: const Icon(
          Icons.refresh,
          size: 40,
        ),
      ),
    );
  }
}

// class Box extends StatelessWidget {
//   final Color color;
//   const Box({required Key key, required this.color}) : super(key: key);
//
//   @override
//   Widget build(BuildContext context) {
//     return Container(
//       margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
//       width: 88,
//       height: 88,
//       decoration: BoxDecoration(
//         borderRadius: BorderRadius.circular(3),
//         color: color,
//       ),
//     );
//   }
// }

class Box extends StatelessWidget {
  final Color color;
  final int index;
  final Function(Color) onDragStarted;
  final Function(DraggableDetails details) onDragEnd;
  const Box(
      {required Key key,
      required this.color,
      required this.index,
      required this.onDragStarted,
      required this.onDragEnd})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return AnimatedPositioned(
      left: 188,
      top: index * 88,
      child: Draggable(
        onDragStarted: () {
          onDragStarted(color);
        },
        onDragEnd: onDragEnd,
        //被拖动的时候 和手势一起移动的
        feedback: Container(
          margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
          width: 88,
          height: 88,
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(8),
            color: color,
          ),
        ),

        //被拖动的时候 之前位置所显示的
        childWhenDragging: Container(
          margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
          width: 88,
          height: 88,
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(8),
            // color: Colors.grey,
            // color: color.withOpacity(0.5),
          ),
        ),
        child: Container(
          margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
          width: 88,
          height: 88,
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(8),
            color: color,
          ),
        ),
      ),
      duration: Duration(milliseconds: 250),
    );
  }
}

猜你喜欢

转载自blog.csdn.net/nicepainkiller/article/details/122447157