Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GlobalKey not attaching to a widget #91557

Closed
lukaszdebowski opened this issue Oct 9, 2021 · 17 comments
Closed

GlobalKey not attaching to a widget #91557

lukaszdebowski opened this issue Oct 9, 2021 · 17 comments
Labels
r: solved Issue is closed as solved

Comments

@lukaszdebowski
Copy link

lukaszdebowski commented Oct 9, 2021

Hi!

I am trying to understand the problem of not attaching a GlobalKey to a widget.
The problem occurs only when the widgets are built using a for loop like in the example.
Building normal widgets (without for loop) correctly attaches the GlobalKey and the context can be found without problems.

Steps to Reproduce

  1. Open this dartpad
  2. Click on the FloatingActionButton and see container0: true printed in the console
  3. Copy the code to your IDE (latest flutter, 2.5.2) and run locally
  4. Click the same button and see container0: false printed instead

Expected results: see container0: true printed in both cases

Actual results: running the exact same code on local IDE in the simulator gives false

Logs

flutter doctor -v output

[✓] Flutter (Channel stable, 2.5.2, on macOS 11.6 20G165 darwin-x64, locale en-PL)
    • Flutter version 2.5.2 at /Users/lukaszdebowski/flutter
    • Upstream repository git@github.com:flutter/flutter.git
    • Framework revision 3595343e20 (9 days ago), 2021-09-30 12:58:18 -0700
    • Engine revision 6ac856380f
    • Dart version 2.14.3

[✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0-rc1)
    • Android SDK at /Users/lukaszdebowski/Library/Android/sdk
    • Platform android-31, build-tools 31.0.0-rc1
    • ANDROID_HOME = /Users/lukaszdebowski/Library/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7281165)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 13.0, Build version 13A233
    • CocoaPods version 1.11.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2020.3)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7281165)

[✓] VS Code (version 1.61.0)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.27.0

[✓] Connected device (2 available)
    • Android SDK built for x86 (mobile) • emulator-5554 • android-x86    • Android 11 (API 30) (emulator)
    • Chrome (web)                       • chrome        • web-javascript • Google Chrome 94.0.4606.81
    ! Error: Łukasz’s iPhone is not connected. Xcode will continue when Łukasz’s iPhone is connected. (code -13)

• No issues found!

Edit

After some further investigation I've found that the problem lays in the way GlobalObjectKeys are created.
It has something to do with string interpolation:

Creating the keys as strings with int values such as GlobalObjectKey('abc1') cannot find the context, while GlobalObjectKey(1) can.

The identical function seems to have some problem -> #91557 (comment)

Still don't know why the same code has different behaviour in the dartpad and locally

@Vardiak
Copy link

Vardiak commented Oct 9, 2021

Looks like the correct behavior to me. To access the context of a Widget with a GlobalKey, you have to use the exact same instance, here you are instancing two differents objects. You should use a ValueKey instead, which works like you would like.

@iapicca
Copy link
Contributor

iapicca commented Oct 9, 2021

@lukaszdebowski
do you experience the issue with the code below as well?

import 'package:flutter/material.dart';

final keys = [for (var i = 0; i < 5; ++i) GlobalObjectKey(i)];

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        body: ListView(
          children: [for (final key in keys) FlutterLogo(key: key)],
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => print(keys.first.currentContext != null),
        ),
      ),
    ),
  );
}

@Vardiak
imho whether or not the approach is correct the behavior should be consistent across platforms

@lukaszdebowski
Copy link
Author

Looks like the correct behavior to me. To access the context of a Widget with a GlobalKey, you have to use the exact same instance, here you are instancing two differents objects. You should use a ValueKey instead, which works like you would like.

According to GlobalObjectKey documentation and what I've tested, it works like that.

final key1 = GlobalObjectKey('some_key');
final key2 = GlobalObjectKey('some_key');

print(key1 == key2); // true

@lukaszdebowski
Copy link
Author

@iapicca The code from your answer works correctly, which is even more confusing to me

@iapicca
Copy link
Contributor

iapicca commented Oct 9, 2021

@iapicca The code from your answer works correctly, which is even more confusing to me

worse case you spotted an inconsistent behavior, which can still be valuable

@lukaszdebowski
Copy link
Author

@iapicca The following also works, so I'm not really sure what is going on here

import 'package:flutter/material.dart';

final keys = [for (var i = 0; i < 5; ++i) GlobalObjectKey(i)];

void main() {
  runApp(
    MaterialApp(
      home: Scaffold(
        body: ListView(
          children: [
            for (int i = 0; i < 5; i++) FlutterLogo(key: GlobalObjectKey(i)),
          ],
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            final key = GlobalObjectKey(1);
            print(key.currentContext != null); // true
          },
        ),
      ),
    ),
  );
}

@lukaszdebowski
Copy link
Author

lukaszdebowski commented Oct 9, 2021

@iapicca Okay I've spotted the issue. It has something to do with string interpolation and how the identical function works (which is used within the GlobalObjectKey).

  var string1 = 'abc1';
  var i = 1;
  var string2 = 'abc$i';

  print(string1 == string2); // true
  print(identical(string1, string2)); // false

  var string3 = 'abc1';
  var string4 = 'abc1';

  print(identical(string3, string4)); // true

@marcglasberg
Copy link

marcglasberg commented Oct 9, 2021

@lukaszdebowski

Here is the solution for you: https://pub.dev/packages/assorted_layout_widgets

Then use the provided GlobalValueKey instead of the GlobalObjectKey.

@lukaszdebowski
Copy link
Author

@marcglasberg Thank you. Although I do have a solution, I still think that this is an issue in the language - it should either return true for both cases (dartpad and local) or return false. It is also not clear why creating the strings from interpolation and as a string value ('abc1') gives different results from the 'identical' function (if it compares the objects in memory, shouldn't it return false in both cases as the strings are not created as 'const'?)

@iapicca
Copy link
Contributor

iapicca commented Oct 10, 2021

Here is the solution for you: https://pub.dev/packages/global_keys

@marcglasberg
seems to me a workaround at best (self promotion at worse)

@marcglasberg
Copy link

@lukaszdebowski It's NOT an issue in the language. The GlobalObjectKey is working as expected. It compares by identity. So yes, strings from interpolation should give different results because they are NOT the same object instance. You are using the wrong key for the job. You should use a GlobalValueKey (value key, not object key) instead, which compares by equality, and which is what I have provided you in that package. The only thing you may complain about is that, maybe, the language could provide a GlobalValueKey out of the box, instead of you having to add a package or create one yourself. In that case I would agree with you. They should provide it, in my opinion. But they have a different opinion and don't provide it. It doesn't mean there is something wrong with the language.

@iapicca No, it is NOT a workaround. The GlobalObjectKey is working as expected, and should not be used in this case at all. The actual solution to Lukas problem is using a key that compares by equality. I have provided the correct key that solves the problem. Another alternative is that he can refactor his code as not to depend on string interpolation. There are many ways he can change his code in that regard. But if he keeps it that way the only correct solution is to GlobalValueKey I have provided, or something very similar. So no, it's not a workaround and it's not self promotion. Stop making a fool of yourself in the internet.

@marcglasberg
Copy link

@lukaszdebowski Sorry, I forgot to say, also, that Strings are automatically const. When you write "abc" it's as if you have written something like const String("abc"). That's why when you have no interpolation they are equal.

@lukaszdebowski
Copy link
Author

@lukaszdebowski Sorry, I forgot to say, also, that Strings are automatically const. When you write "abc" it's as if you have written something like const String("abc"). That's why when you have no interpolation they are equal.

Oh I didn't know that, that would explain the identical behavior. But if that's the case, them the dartpad example should work the same way, which it doesn't - so if you are correct, that means the web part is implemented wrong (the identical implementation).

I'm fine with either result, it's just that it should be consistent across platforms

@marcglasberg
Copy link

@lukaszdebowski

Problem is dartpad is Dart compiled to Javascript, and Javascript doesn't make the distinction. Some details of Dart are not the same in all platforms. The language specification allows for that (not being consistent in all platforms).

@lukaszdebowski
Copy link
Author

I've found other comment that confirms the behavior: https://stackoverflow.com/a/16261723

So I guess it's solved, even tho it kinda doesn't feel right about the identical on the dartpad 😄

@danagbemava-nc danagbemava-nc added the in triage Presently being triaged by the triage team label Oct 11, 2021
@danagbemava-nc
Copy link
Member

Based on the conversation thus far and #91557 (comment) I feel it is safe to close this issue.

As such, I will be closing this issue.

Thank you

@danagbemava-nc danagbemava-nc added r: solved Issue is closed as solved and removed in triage Presently being triaged by the triage team labels Oct 11, 2021
@github-actions
Copy link

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 25, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
r: solved Issue is closed as solved
Projects
None yet
Development

No branches or pull requests

5 participants