Change GlobalObjectKey.toString to strip away State<StatefulWidget>. (#14558)

This allows const GlobalObjectKey(0) to be concisely formatted as
[GlobalObjectKey int#0000] in both Dart 2 and Dart 1 modes.

Without this change it would be formatted as
[GlobalObjectKey<State<StatefulWidget>> int#0000] because in Dart 2
types are instantiated to bounds.

In addition to retaining general readability this also fixes few
tests that rely on this short string representation (see
test/widgets/global_keys_duplicated_test.dart).
diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart
index af63ffa..a1a81b6 100644
--- a/packages/flutter/lib/src/widgets/framework.dart
+++ b/packages/flutter/lib/src/widgets/framework.dart
@@ -312,7 +312,17 @@
   int get hashCode => identityHashCode(value);
 
   @override
-  String toString() => '[$runtimeType ${describeIdentity(value)}]';
+  String toString() {
+    String selfType = runtimeType.toString();
+    // const GlobalObjectKey().runtimeType.toString() returns 'GlobalObjectKey<State<StatefulWidget>>'
+    // because GlobalObjectKey is instantiated to its bounds. To avoid cluttering the output
+    // we remove the suffix.
+    const String suffix = '<State<StatefulWidget>>';
+    if (selfType.endsWith(suffix)) {
+      selfType = selfType.substring(0, selfType.length - suffix.length);
+    }
+    return '[$selfType ${describeIdentity(value)}]';
+  }
 }
 
 /// This class is a work-around for the "is" operator not accepting a variable value as its right operand
diff --git a/packages/flutter/test/widgets/framework_test.dart b/packages/flutter/test/widgets/framework_test.dart
index e464d2f..63ac6dc 100644
--- a/packages/flutter/test/widgets/framework_test.dart
+++ b/packages/flutter/test/widgets/framework_test.dart
@@ -11,6 +11,11 @@
   Widget build(BuildContext context) => null;
 }
 
+@optionalTypeArgs
+class _MyGlobalObjectKey<T extends State<StatefulWidget>> extends GlobalObjectKey<T> {
+  const _MyGlobalObjectKey(Object value) : super(value);
+}
+
 void main() {
   testWidgets('UniqueKey control test', (WidgetTester tester) async {
     final Key key = new UniqueKey();
@@ -31,6 +36,18 @@
     expect(keyA, isNot(equals(keyB)));
   });
 
+  testWidgets('GlobalObjectKey toString test', (WidgetTester tester) async {
+    final GlobalObjectKey one = const GlobalObjectKey(1);
+    final GlobalObjectKey<TestState> two = const GlobalObjectKey<TestState>(2);
+    final GlobalObjectKey three = const _MyGlobalObjectKey(3);
+    final GlobalObjectKey<TestState> four = const _MyGlobalObjectKey<TestState>(4);
+
+    expect(one.toString(), equals('[GlobalObjectKey ${describeIdentity(1)}]'));
+    expect(two.toString(), equals('[GlobalObjectKey<TestState> ${describeIdentity(2)}]'));
+    expect(three.toString(), equals('[_MyGlobalObjectKey ${describeIdentity(3)}]'));
+    expect(four.toString(), equals('[_MyGlobalObjectKey<TestState> ${describeIdentity(4)}]'));
+  });
+
   testWidgets('GlobalObjectKey control test', (WidgetTester tester) async {
     final Object a = new Object();
     final Object b = new Object();
@@ -575,4 +592,4 @@
 
   @override
   void performRebuild() { }
-}
\ No newline at end of file
+}