Home > Enterprise >  What exactly are GlobalKeys and keys in flutter?
What exactly are GlobalKeys and keys in flutter?

Time:12-21

I have tried to learn this concept for over a month and I just can't seem to figure out what exactly is GlobalKeys and various types of keys in simpler terms, Is it just for form validator or does it serve more purpose,There aren't clear basic example of this in youtube too or perhaps may be there is a book somewhere ?

Here in basic flutter app


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),
      ),
    );
  }
}

Here there are lines like

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);

What does Key /super keyword have to do with it or how do we use it?

CodePudding user response:

Global keys are for Form validations. Other than global keys there is value,object,and unique key in Flutter. You get clear idea about all this through below link.

Keys in Flutter

CodePudding user response:

  • Multiple widgets of the same type and at the same level in a widget tree may not update as expected unless they have unique keys, given that these widgets hold some state.
  • Explicitly setting a key to a widget helps Flutter understand which widget it needs to update when state changes.
  • keys also store and restore the current scroll position in a list of widgets.

Global key usually use to change parent widget from any portion of app considering state unchanged. Gobal keys are broad than keys in short.

CodePudding user response:

I found this very helpful: https://medium.com/flutter/keys-what-are-they-good-for-13cb51742e7d

I found two uses for GlobalKeys (I'm sure there's more):

First one: this is what the article above discusses: how to link a state object to a widget. I'm going to oversimplify few things here.

Normally, flutter will not use Global Keys. When you create a stateful widget, two object get created: a widget, and it's state. The idea is that the widget itself will be destroyed at the end of the build (or after it is painted on the screen). Once you initiate the build again (through setState() for exmaple) - a widget will be recreated.

Of course - you want your state object to persist between the changes - this is where you store your data after all. And you want Flutter to link back your state object to newly created Widget object instance.

Most of the time, this is an easy thing for Flutter - it will just find the same widget in the same position, link it to it's state object and it's done.

In case your widget moves around in the next build - this will not work, and new state object will be created and you will lose your state.

Take a look at this modified Flutter Counter app - I moved the counter into a separate Widget, but also linked it back to the the original _MyHomePageState. Each time we rebuild the main widget we will flip the order of the button and text box in the column - and you will see that the count never changes: it always show zero.

Since the widget changed the position, it's state is dropped and new one created.

You can run this in dartpad quickly:

// 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'),
      )
    ]);
  }
}

Now simply by adding a GlobalKey to Counter widget (in both calls), we help flutter link the state object. Note that we created the _key value once in the state object - since we want to pass the same key each time (otherwise what's the point...)

Counter(onPressed: _incrementCounter, key: _key),

And after this change, even when Widget changes the place and order on screen - your state is kept.

Now you would ask: why wouldn't Flutter do this by default for all the Widgets? Turns out - GlobalKeys are very expensive to maintain, and if you want to render 60 frames per second (16ms each frame) - you want to optimize every single step. By 'no keys by default' approach, everything is optimized, and works in large majority of the cases - but in few situations when you need global keys - you need to learn a little bit about it, and it is very simple to use.

Second use I found for it: you need global key to find the your exact Widget position on screen after it renders. Widget itself will not know where it will end up, you only know this after the rendering is done. You need this if you want to do custom animation or something like that.

This is a simple function that will tell you where your widget is:

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;
  }
  • Related