【问题标题】:What exactly are GlobalKeys and keys in flutter?Flutter 中的 GlobalKeys 和 key 到底是什么?
【发布时间】:2021-12-21 08:49:04
【问题描述】:

我已经尝试学习这个概念一个多月了,但我似乎无法用更简单的术语弄清楚 GlobalKeys 和各种类型的键到底是什么,它只是用于表单验证器还是用于更多目的, youtube 上也没有明确的基本示例,或者可能某处有一本书?

在基本的颤振应用中


void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

这里有类似的行

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
\\\\
class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

Key /super关键字和它有什么关系或者我们如何使用它?

【问题讨论】:

标签: flutter dart


【解决方案1】:

我觉得这很有帮助:https://medium.com/flutter/keys-what-are-they-good-for-13cb51742e7d

我发现 GlobalKeys 有两种用途(我相信还有更多用途):

第一个:这就是上面文章讨论的内容:如何将状态对象链接到小部件。我将在这里过度简化一些事情。

通常情况下,flutter 不会使用全局键。当您创建一个有状态的小部件时,会创建两个对象:一个小部件和它的状态。这个想法是小部件本身将在构建结束时(或在屏幕上绘制之后)被销毁。一旦您再次启动构建(例如通过 setState()) - 将重新创建一个小部件。

当然 - 您希望您的状态对象在更改之间保持不变 - 毕竟这是您存储数据的地方。并且您希望 Flutter 将您的状态对象链接回新创建的 Widget 对象实例。

大多数时候,这对 Flutter 来说是一件容易的事——它只会在相同的位置找到相同的小部件,将其链接到它的状态对象,然后就完成了。

如果您的小部件在下一次构建中移动 - 这将不起作用,并且将创建新的状态对象并且您将丢失您的状态。

看看这个修改后的 Flutter Counter 应用程序 - 我将计数器移动到一个单独的 Widget 中,但也将它链接回原来的 _MyHomePageState。每次我们重建主小部件时,我们都会翻转列中按钮和文本框的顺序 - 您会看到计数永远不会改变:它始终显示为零。

由于小部件改变了位置,它的状态被删除并创建了新的状态。

您可以在 dartpad 中快速运行它:

// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String title;

  const MyHomePage({
    Key? key,
    required this.title,
  }) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> {
  
  var _key=GlobalKey();
  
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
    
  }
  
  

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (_counter.isOdd) Counter(onPressed: _incrementCounter),
            const Text(
              'You have pushed the button this many times:',
            ),
            if (_counter.isEven) Counter(onPressed: _incrementCounter),
          ],
        ),
      ),
    );
  }
}

class Counter extends StatefulWidget {
  final VoidCallback onPressed;
  const Counter({Key? key, required this.onPressed}) : super(key: key);

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

class _CounterState extends State<Counter> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
    widget.onPressed();
  }

  @override
  Widget build(BuildContext context) {
    return Column(children: [
        Text(
          '$_counter',
          style: Theme.of(context).textTheme.headline4,
        ),
      ElevatedButton(
        style:
            ElevatedButton.styleFrom(textStyle: const TextStyle(fontSize: 20)),
        onPressed: _incrementCounter,
        child: const Text('Increment'),
      )
    ]);
  }
}

现在只需将 GlobalKey 添加到 Counter 小部件(在两个调用中),我们就可以帮助颤振链接状态对象。请注意,我们在状态对象中创建了一次 _key 值 - 因为我们希望每次都传递相同的键(否则有什么意义......)

Counter(onPressed: _incrementCounter, key: _key),

在此更改之后,即使 Widget 更改了屏幕上的位置和顺序 - 您的状态也会保留。

现在你会问:为什么 Flutter 不默认对所有的 Widget 都这样做?事实证明——GlobalKeys 的维护成本非常高,如果你想每秒渲染 60 帧(每帧 16 毫秒)——你想优化每一步。通过“默认无键”的方法,一切都得到了优化,并且在大多数情况下都能正常工作——但在少数情况下,当你需要全局键时——你需要了解一点,而且使用起来非常简单。

我发现它的第二个用途:你需要全局键来在它渲染后找到你在屏幕上的确切 Widget 位置。小部件本身不知道它会在哪里结束,只有在渲染完成后你才知道。如果你想做自定义动画或类似的东西,你需要这个。

这是一个简单的函数,它会告诉你你的小部件在哪里:

Rect getRectFromKey(GlobalKey key) {
    RenderBox renderBox = (key.currentContext!.findRenderObject())! as RenderBox;
    var targetPosition = renderBox.localToGlobal(Offset.zero);
    var targetSize = renderBox.size;
    // A Rect can be created with one its constructors or from an Offset and a Size using the & operator:
    Rect rect = targetPosition & targetSize;
    return rect;
  }

【讨论】:

    【解决方案2】:

    全局键用于表单验证。除了全局键之外,Flutter 中还有值、对象和唯一键。您可以通过以下链接清楚地了解这一切。

    Keys in Flutter

    【讨论】:

      【解决方案3】:
      • 考虑到这些小部件保持某种状态,小部件树中相同类型和同一级别的多个小部件可能不会按预期更新,除非它们具有唯一键。
      • 为小部件显式设置键有助于 Flutter 了解在状态更改时需要更新哪个小部件。
      • 键还可以存储和恢复小部件列表中的当前滚动位置。

      全局键通常用于从应用程序的任何部分更改父小部件,考虑到状态未更改。全局键比简而言之键更广泛。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-07-03
        • 2021-06-23
        • 2010-10-25
        • 1970-01-01
        • 2019-07-24
        • 2018-10-14
        • 2013-05-29
        相关资源
        最近更新 更多