Apply review comments from Yegor (#474)

diff --git a/customer_testing.sh b/customer_testing.sh
index 16147f0..2426eb1 100755
--- a/customer_testing.sh
+++ b/customer_testing.sh
@@ -21,6 +21,15 @@
 popd
 
 pushd packages/rfw
+
+# Update the examples packages so that the analysis doesn't get confused.
+pushd example/remote
+flutter packages get
+popd
+pushd example/wasm
+flutter packages get
+popd
+
 flutter analyze --no-fatal-infos
 if [[ "$OSTYPE" == "linux-gnu" ]]; then
     # We only run the full tests on Linux because golden files differ
diff --git a/packages/rfw/CHANGELOG.md b/packages/rfw/CHANGELOG.md
index 6f3c1e1..8eeacaa 100644
--- a/packages/rfw/CHANGELOG.md
+++ b/packages/rfw/CHANGELOG.md
@@ -1,5 +1,8 @@
-## NEXT
+## 1.0.1
 
+* Improve documentation.
+* Provide constants for the file signatures.
+* Minor efficiency improvements.
 * Fix `unnecessary_import` lint errors.
 
 ## 1.0.0
diff --git a/packages/rfw/example/README.md b/packages/rfw/example/README.md
new file mode 100644
index 0000000..07609c8
--- /dev/null
+++ b/packages/rfw/example/README.md
@@ -0,0 +1,13 @@
+There are several examples.
+
+* `hello` shows how `RemoteWidget` can render some widgets from a
+  description interpreted at runtime.
+
+* `local` shows how to create new local widget definitions for use in
+  remote widget descriptions.
+
+* `remote` is a proof-of-concept showing files being obtained from a
+  remote server and rendered by the application at runtime.
+
+* `wasm` shows how package:rfw can be combined with package:wasm to
+  configure logic as well as the user interface at runtime.
diff --git a/packages/rfw/lib/formats.dart b/packages/rfw/lib/formats.dart
index ae327e3..acf6f03 100644
--- a/packages/rfw/lib/formats.dart
+++ b/packages/rfw/lib/formats.dart
@@ -27,6 +27,6 @@
 /// For client-side code, import `package:rfw/rfw.dart` instead.
 library formats;
 
-export 'dart/binary.dart';
-export 'dart/model.dart';
-export 'dart/text.dart';
+export 'src/dart/binary.dart';
+export 'src/dart/model.dart';
+export 'src/dart/text.dart';
diff --git a/packages/rfw/lib/rfw.dart b/packages/rfw/lib/rfw.dart
index 23cfab7..7f5d370 100644
--- a/packages/rfw/lib/rfw.dart
+++ b/packages/rfw/lib/rfw.dart
@@ -19,7 +19,7 @@
 /// (e.g. [https://pub.dev/packages/hetu_script](hetu_script)) to run
 /// remotely-fetched logic locally:
 ///
-/// ![The Remote Flutter Widgets once again come from the server and follow the same path via the network to the Runtime. The Runtime combines this with the Data model to generate the Flutter Widgets, which send state updates directly back to the runtime and user input to the Hard-coded client logic. That logic updates the Client data which updates the Data model, but also sends messages to the Scripting engine which is also on the Client. The Scripting engine is configured from Scripts obtained over the network, and generates Script data that also populates the Data model.](https://raw.githubusercontent.com/flutter/packages/master/packages/rfw/images/overview2.png)
+/// ![The Remote Flutter Widgets once again come from the server and follow the same path via the network to the Runtime. The Runtime combines this with the Data model to generate the Flutter Widgets, which send state updates directly back to the Runtime and user input to the Hard-coded client logic. That logic updates the Client data which updates the Data model, but also sends messages to the Scripting engine which is also on the Client. The Scripting engine is configured from Scripts obtained over the network, and generates Script data that also populates the Data model.](https://raw.githubusercontent.com/flutter/packages/master/packages/rfw/images/overview2.png)
 ///
 ///
 /// ## Using the [RemoteWidget] widget
@@ -36,23 +36,23 @@
 /// The methods for parsing the text format are not exported by
 /// `package:rfw/rfw.dart` to discourage their use in client-side code.
 ///
-/// ## Server-side dart
+/// ## Server-side Dart
 ///
 /// This package can be used in non-Flutter environments by importing
 /// `package:rfw/formats.dart` rather than `package:rfw/rfw.dart`. In the
-/// `formats` mode, the [Runtime] and [DynamicContent] objects, as well as the
+/// [formats] mode, the [Runtime] and [DynamicContent] objects, as well as the
 /// [RemoteWidget] widget, are not available, but the [parseDataFile] and
 /// [parseLibraryFile] methods are. They can be used in conjunction with
 /// [encodeDataBlob] and [encodeLibraryBlob] (respectively) to generate the
 /// binary files used by client-side code.
 library rfw;
 
-export 'dart/binary.dart';
-export 'dart/model.dart';
-export 'dart/text.dart' hide parseDataFile, parseLibraryFile;
-export 'flutter/argument_decoders.dart';
-export 'flutter/content.dart';
-export 'flutter/core_widgets.dart';
-export 'flutter/material_widgets.dart';
-export 'flutter/remote_widget.dart';
-export 'flutter/runtime.dart';
+export 'src/dart/binary.dart';
+export 'src/dart/model.dart';
+export 'src/dart/text.dart' hide parseDataFile, parseLibraryFile;
+export 'src/flutter/argument_decoders.dart';
+export 'src/flutter/content.dart';
+export 'src/flutter/core_widgets.dart';
+export 'src/flutter/material_widgets.dart';
+export 'src/flutter/remote_widget.dart';
+export 'src/flutter/runtime.dart';
diff --git a/packages/rfw/lib/dart/binary.dart b/packages/rfw/lib/src/dart/binary.dart
similarity index 91%
rename from packages/rfw/lib/dart/binary.dart
rename to packages/rfw/lib/src/dart/binary.dart
index f2094db..76270b3 100644
--- a/packages/rfw/lib/dart/binary.dart
+++ b/packages/rfw/lib/src/dart/binary.dart
@@ -11,6 +11,26 @@
 
 import 'model.dart';
 
+/// The first four bytes of a Remote Flutter Widgets binary data blob.
+///
+/// This signature is automatically added by [encodeDataBlob] and is checked in
+/// [decodeDataBlob].
+///
+/// See also:
+///
+///  * [libraryBlobSignature], which is the signature for binary library blobs.
+const List<int> dataBlobSignature = <int>[0xFE, 0x52, 0x57, 0x44];
+
+/// The first four bytes of a Remote Flutter Widgets binary library blob.
+///
+/// This signature is automatically added by [encodeLibraryBlob] and is checked
+/// in [decodeLibraryBlob].
+///
+/// See also:
+///
+///  * [dataBlobSignature], which is the signature for binary data blobs.
+const List<int> libraryBlobSignature = <int>[0xFE, 0x52, 0x46, 0x57];
+
 /// Encode data as a Remote Flutter Widgets binary data blob.
 ///
 /// See also:
@@ -20,7 +40,7 @@
 ///    Remote Flutter Widgets binary library blobs.
 Uint8List encodeDataBlob(Object value) {
   final _BlobEncoder encoder = _BlobEncoder();
-  encoder.writeSignature(<int>[0xFE, 0x52, 0x57, 0x44]);
+  encoder.writeSignature(dataBlobSignature);
   encoder.writeValue(value);
   return encoder.bytes.toBytes();
 }
@@ -35,7 +55,8 @@
 /// ints, doubles, booleans, and strings. See [decodeLibraryBlob] for a
 /// description of the format.
 ///
-/// The first four bytes of the file (in hex) are FE 52 57 44.
+/// The first four bytes of the file (in hex) are FE 52 57 44; see
+/// [dataBlobSignature].
 ///
 /// See also:
 ///
@@ -45,7 +66,7 @@
 ///  * [parseDataFile], which parses the text variant of this format.
 Object decodeDataBlob(Uint8List bytes) {
   final _BlobDecoder decoder = _BlobDecoder(bytes.buffer.asByteData(bytes.offsetInBytes, bytes.lengthInBytes));
-  decoder.expectSignature(<int>[0xFE, 0x52, 0x57, 0x44]);
+  decoder.expectSignature(dataBlobSignature);
   final Object result = decoder.readValue();
   if (!decoder.finished) {
     throw const FormatException('Unexpected trailing bytes after value.');
@@ -63,7 +84,7 @@
 ///  * [parseLibraryFile], which parses the text variant of this format.
 Uint8List encodeLibraryBlob(RemoteWidgetLibrary value) {
   final _BlobEncoder encoder = _BlobEncoder();
-  encoder.writeSignature(<int>[0xFE, 0x52, 0x46, 0x57]);
+  encoder.writeSignature(libraryBlobSignature);
   encoder.writeLibrary(value);
   return encoder.bytes.toBytes();
 }
@@ -78,7 +99,8 @@
 /// using a one-byte tag to identify types when necessary, and using 64 bit
 /// integers to encode lengths when necessary.
 ///
-/// The first four bytes of the file (in hex) are FE 52 46 57.
+/// The first four bytes of the file (in hex) are FE 52 46 57; see
+/// [libraryBlobSignature].
 ///
 /// Primitives in this format are as follows:
 ///
@@ -93,7 +115,7 @@
 ///
 ///   For example, the string "Hello" would be encoded as:
 ///
-///    05 00 00 00 00 00 00 00 48 65 6C 6C 6F
+///       05 00 00 00 00 00 00 00  48 65 6C 6C 6F
 ///
 /// * Lists are encoded as an integer length, followed by that many values
 ///   back to back. When lists are of specific types (e.g. lists of imports),
@@ -102,13 +124,15 @@
 ///   followed by the value (tagged lists). For example, a list of integers with
 ///   the values 1 and 2 in that order would be encoded as:
 ///
-///    02 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00.
+///       02 00 00 00 00 00 00 00  01 00 00 00 00 00 00 00
+///       02 00 00 00 00 00 00 00
 ///
 ///   A list of arbitrary values that happens to contain one string "Hello"
 ///   would be encoded as follows; 0x04 is the tag for "String" (the full list
 ///   of tags is described below):
 ///
-///    01 00 00 00 00 00 00 00 04 05 00 00 00 00 00 00 00 48 65 6C 6C 6F
+///       01 00 00 00 00 00 00 00  04 05 00 00 00 00 00 00
+///       00 48 65 6C 6C 6F
 ///
 ///   A list of length zero is eight zero bytes with no additional payload.
 ///
@@ -123,7 +147,8 @@
 ///   strings, so they are untagged) is encoded as follows (0x02 is the tag for
 ///   integers):
 ///
-///    01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 61 02 0F 00 00 00 00 00 00 00
+///       01 00 00 00 00 00 00 00  01 00 00 00 00 00 00 00
+///       61 02 0F 00 00 00 00 00  00 00
 ///
 /// Objects are encoded as follows:
 ///
@@ -134,7 +159,8 @@
 ///   one of the subparts of the imported library name. For example, `import
 ///   a.b` is encoded as:
 ///
-///    02 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 61 01 00 00 00 00 00 00 00 62
+///       02 00 00 00 00 00 00 00  01 00 00 00 00 00 00 00
+///       61 01 00 00 00 00 00 00  00 62
 ///
 /// * Widget declarations are encoded as a string giving the declaration name,
 ///   an untagged map for the initial state, and finally the value that
@@ -204,11 +230,11 @@
 ///
 ///   ...is encoded as follows (including the tag for the switch itself):
 ///
-///    0F 0A 01 00 00 00 00 00  00 00 61 03 00 00 00 00
-///    00 00 00 02 00 00 00 00  00 00 00 00 04 01 00 00
-///    00 00 00 00 00 7A 02 01  00 00 00 00 00 00 00 04
-///    01 00 00 00 00 00 00 00  6F 10 04 01 00 00 00 00
-///    00 00 00 64
+///       0F 0A 01 00 00 00 00 00  00 00 61 03 00 00 00 00
+///       00 00 00 02 00 00 00 00  00 00 00 00 04 01 00 00
+///       00 00 00 00 00 7A 02 01  00 00 00 00 00 00 00 04
+///       01 00 00 00 00 00 00 00  6F 10 04 01 00 00 00 00
+///       00 00 00 64
 ///
 /// * Event handlers have the tag 0x0E, and are encoded as a string
 ///   ([EventHandler.eventName]) and an untagged map
@@ -227,7 +253,7 @@
 ///  * [parseDataFile], which parses the text variant of this format.
 RemoteWidgetLibrary decodeLibraryBlob(Uint8List bytes) {
   final _BlobDecoder decoder = _BlobDecoder(bytes.buffer.asByteData(bytes.offsetInBytes, bytes.lengthInBytes));
-  decoder.expectSignature(<int>[0xFE, 0x52, 0x46, 0x57]);
+  decoder.expectSignature(libraryBlobSignature);
   final RemoteWidgetLibrary result = decoder.readLibrary();
   if (!decoder.finished) {
     throw const FormatException('Unexpected trailing bytes after constructors.');
@@ -293,9 +319,9 @@
     return bytes.getInt64(byteOffset, _blobEndian);
   }
 
-  double _readDouble() {
+  double _readBinary64() {
     final int byteOffset = _cursor;
-    _advance('double', 8);
+    _advance('binary64', 8);
     return bytes.getFloat64(byteOffset, _blobEndian);
   }
 
@@ -368,7 +394,7 @@
       case _msInt64:
         return _readInt64();
       case _msBinary64:
-        return _readDouble();
+        return _readBinary64();
       case _msString:
         return _readString();
       case _msList:
diff --git a/packages/rfw/lib/dart/model.dart b/packages/rfw/lib/src/dart/model.dart
similarity index 98%
rename from packages/rfw/lib/dart/model.dart
rename to packages/rfw/lib/src/dart/model.dart
index 13d8382..11cd965 100644
--- a/packages/rfw/lib/dart/model.dart
+++ b/packages/rfw/lib/src/dart/model.dart
@@ -281,7 +281,7 @@
 /// This class is an internal detail of the RFW [Runtime] and is generally not
 /// used directly.
 class BoundArgsReference extends Reference {
-  /// Wraps the given [parts] and [arguments] as an [ArgsReference].
+  /// Wraps the given [parts] and [arguments] as a [BoundArgsReference].
   ///
   /// The parameters must not be mutated after the object is created.
   ///
@@ -301,7 +301,7 @@
 /// Reference to the [DynamicContent] data that is passed into the widget (see
 /// [Runtime.build]'s `data` argument).
 class DataReference extends Reference {
-  /// Wraps the given [parts] as an [DataReference].
+  /// Wraps the given [parts] as a [DataReference].
   ///
   /// The [parts] must not be mutated after the object is created.
   const DataReference(List<Object> parts): super(parts);
@@ -349,7 +349,7 @@
   /// suppose that the widget itself has an [ArgsReference] that references
   /// "args.a.baz". The "args.a" part identifies the aforementioned
   /// [LoopReference], and so the resulting reference is actually to
-  /// "data.foo.bar.baz".
+  /// "loop0.foo.bar.baz".
   ///
   /// In this example, the [LoopReference] to "loop0.foo.bar" would have its
   /// [constructReference] method invoked by the runtime, with `["baz"]` as the
@@ -406,7 +406,7 @@
   /// Now suppose that the widget itself has an [ArgsReference] that references
   /// "args.a.baz". The "args.a" part identifies the aforementioned
   /// [BoundLoopReference], and so the resulting reference is actually to
-  /// "data.foo.bar.baz".
+  /// "loop0.foo.bar.baz".
   ///
   /// In this example, the [BoundLoopReference] to "loop0.foo.bar" would have
   /// its [constructReference] method invoked by the runtime, with `["baz"]` as
@@ -567,6 +567,8 @@
 ///
 /// Used to describe which libraries a remote widget libraries depends on. The
 /// identified libraries can be local or remote. Import loops are invalid.
+// TODO(ianh): eventually people will probably want a way to disambiguate imports
+// with a prefix.
 class Import extends BlobNode {
   /// Wraps the given library [name] in an [Import] object.
   const Import(this.name);
diff --git a/packages/rfw/lib/dart/text.dart b/packages/rfw/lib/src/dart/text.dart
similarity index 99%
rename from packages/rfw/lib/dart/text.dart
rename to packages/rfw/lib/src/dart/text.dart
index da596b7..531442b 100644
--- a/packages/rfw/lib/dart/text.dart
+++ b/packages/rfw/lib/src/dart/text.dart
@@ -103,13 +103,17 @@
 /// of zero or more Unicode characters that do not include a newline, the quote
 /// character used, or the backslash character, mixed with zero or more escapes.
 ///
-/// Escapes are a backslash character followed another character, as follows:
+/// Escapes are a backslash character followed by another character, as follows:
 ///
 ///  * `\b`: represents U+0008
 ///  * `\f`: represents U+000C
 ///  * `\n`: represents U+000A
 ///  * `\r`: represents U+000D
 ///  * `\t`: represents U+0009
+///  * `"`: represents U+0022 (")
+///  * `'`: represents U+0027 (')
+///  * `/`: represents U+002F (/)
+///  * `\`: represents U+005C (\)
 ///  * `\uXXXX`: represents the UTF-16 codepoint with code XXXX.
 ///
 /// Characters outside the basic multilingual plane must be represented either
diff --git a/packages/rfw/lib/flutter/argument_decoders.dart b/packages/rfw/lib/src/flutter/argument_decoders.dart
similarity index 100%
rename from packages/rfw/lib/flutter/argument_decoders.dart
rename to packages/rfw/lib/src/flutter/argument_decoders.dart
diff --git a/packages/rfw/lib/flutter/content.dart b/packages/rfw/lib/src/flutter/content.dart
similarity index 97%
rename from packages/rfw/lib/flutter/content.dart
rename to packages/rfw/lib/src/flutter/content.dart
index e043ba6..e122aff 100644
--- a/packages/rfw/lib/flutter/content.dart
+++ b/packages/rfw/lib/src/flutter/content.dart
@@ -305,19 +305,19 @@
     _value = value;
     if (value is DynamicMap) {
       for (final Object childKey in _children.keys) {
-        if (childKey is String && value.containsKey(childKey)) {
-          _children[childKey]!.update(value[childKey]!);
-        } else {
-          _children[childKey]!.update(missing);
+        Object? childValue;
+        if (childKey is String) {
+          childValue = value[childKey];
         }
+        _children[childKey]!.update(childValue ?? missing);
       }
     } else if (value is DynamicList) {
       for (final Object childKey in _children.keys) {
+        Object? childValue;
         if (childKey is int && childKey >= 0 && childKey < value.length) {
-          _children[childKey]!.update(value[childKey]!);
-        } else {
-          _children[childKey]!.update(missing);
+          childValue = value[childKey];
         }
+        _children[childKey]!.update(childValue ?? missing);
       }
     } else {
       for (final _DynamicNode child in _children.values) {
diff --git a/packages/rfw/lib/flutter/core_widgets.dart b/packages/rfw/lib/src/flutter/core_widgets.dart
similarity index 99%
rename from packages/rfw/lib/flutter/core_widgets.dart
rename to packages/rfw/lib/src/flutter/core_widgets.dart
index f60897e..e0d05e4 100644
--- a/packages/rfw/lib/flutter/core_widgets.dart
+++ b/packages/rfw/lib/src/flutter/core_widgets.dart
@@ -11,7 +11,8 @@
 import 'dart:ui' show FontFeature;
 
 import 'package:flutter/gestures.dart' show DragStartBehavior;
-import 'package:flutter/material.dart';
+import 'package:flutter/material.dart' show Icons;
+import 'package:flutter/widgets.dart';
 
 import 'argument_decoders.dart';
 import 'runtime.dart';
@@ -229,7 +230,7 @@
 Map<String, LocalWidgetBuilder> get _coreWidgetsDefinitions => <String, LocalWidgetBuilder>{
 
   // Keep these in alphabetical order.
-  
+
   'AnimationDefaults': (BuildContext context, DataSource source) {
     return AnimationDefaults(
       duration: ArgumentDecoders.duration(source, ['duration'], context),
diff --git a/packages/rfw/lib/flutter/material_widgets.dart b/packages/rfw/lib/src/flutter/material_widgets.dart
similarity index 100%
rename from packages/rfw/lib/flutter/material_widgets.dart
rename to packages/rfw/lib/src/flutter/material_widgets.dart
diff --git a/packages/rfw/lib/flutter/remote_widget.dart b/packages/rfw/lib/src/flutter/remote_widget.dart
similarity index 94%
rename from packages/rfw/lib/flutter/remote_widget.dart
rename to packages/rfw/lib/src/flutter/remote_widget.dart
index 34d05fa..7dd0d5c 100644
--- a/packages/rfw/lib/flutter/remote_widget.dart
+++ b/packages/rfw/lib/src/flutter/remote_widget.dart
@@ -22,7 +22,7 @@
   /// The [onEvent] argument is optional. When omitted, events are discarded.
   const RemoteWidget({ Key? key, required this.runtime, required this.widget, required this.data, this.onEvent }) : super(key: key);
 
-  /// The [Runtime] to use to render the widget specified by [library] and [name].
+  /// The [Runtime] to use to render the widget specified by [widget].
   ///
   /// This should update rarely (doing so is relatively expensive), but it is
   /// fine to update it. For example, a client could update this on the fly when
@@ -32,9 +32,9 @@
   final Runtime runtime;
 
   /// The name of the widget to display, and the library from which to obtain
-  /// in.
+  /// it.
   ///
-  /// The widget must be either declared in the specified library or one of its
+  /// The widget must be declared either in the specified library, or one of its
   /// dependencies.
   ///
   /// The data to show in the widget is specified using [data].
diff --git a/packages/rfw/lib/flutter/runtime.dart b/packages/rfw/lib/src/flutter/runtime.dart
similarity index 88%
rename from packages/rfw/lib/flutter/runtime.dart
rename to packages/rfw/lib/src/flutter/runtime.dart
index a0e594d..7a989c6 100644
--- a/packages/rfw/lib/flutter/runtime.dart
+++ b/packages/rfw/lib/src/flutter/runtime.dart
@@ -78,9 +78,11 @@
   /// Return the int, double, bool, or String value at the given path of the
   /// arguments to the widget.
   ///
-  /// If `T` is not [Object] and `T` does not match the type of the value
-  /// obtained, then the method returns null.
-  T? v<T>(List<Object> argsKey);
+  /// `T` must be one of [int], [double], [bool], or [String].
+  ///
+  /// If `T` does not match the type of the value obtained, then the method
+  /// returns null.
+  T? v<T extends Object>(List<Object> argsKey);
 
   /// Return true if the given key identifies a list, otherwise false.
   bool isList(List<Object> argsKey);
@@ -205,7 +207,7 @@
   /// [decodeLibraryBlob].
   ///
   /// To remove a library, the libraries must be cleared using [clearLibraries]
-  /// and then all the libraries must be readded.
+  /// and then the libraries being retained must be readded.
   void update(LibraryName name, WidgetLibrary library) {
     _libraries[name] = library;
     _clearCache();
@@ -243,13 +245,11 @@
   /// be updated independently, the widget will rebuild appropriately as it
   /// changes.
   ///
-  /// The `remoteEventTarget` argument is the callback to invoke whenever a
-  /// remote widget event handler is triggered.
+  /// The `remoteEventTarget` argument is the callback that the RFW runtime will
+  /// invoke whenever a remote widget event handler is triggered.
   Widget build(BuildContext context, FullyQualifiedWidgetName widget, DynamicContent data, RemoteEventHandler remoteEventTarget) {
-    final _CurriedWidget boundWidget;
-    if (_widgets.containsKey(widget)) {
-      boundWidget = _widgets[widget]!;
-    } else {
+    _CurriedWidget? boundWidget = _widgets[widget];
+    if (boundWidget == null) {
       _checkForImportLoops(widget.library);
       boundWidget = _applyConstructorAndBindArguments(widget, const <String, Object?>{}, -1, <FullyQualifiedWidgetName>{});
       _widgets[widget] = boundWidget;
@@ -285,8 +285,9 @@
   }
 
   _ResolvedConstructor? _findConstructor(FullyQualifiedWidgetName fullName) {
-    if (_cachedConstructors.containsKey(fullName)) {
-      return _cachedConstructors[fullName];
+    final _ResolvedConstructor? result = _cachedConstructors[fullName];
+    if (result != null) {
+      return result;
     }
     final WidgetLibrary? library = _libraries[fullName.library];
     if (library is RemoteWidgetLibrary) {
@@ -299,6 +300,8 @@
         final LibraryName dependency = import.name;
         final _ResolvedConstructor? result = _findConstructor(FullyQualifiedWidgetName(dependency, fullName.widget));
         if (result != null) {
+          // We cache the constructor under each name that we tried to look it up with, so
+          // that next time it takes less time to find it.
           return _cachedConstructors[fullName] = result;
         }
       }
@@ -329,9 +332,13 @@
     }
   }
 
-  /// Resolves argument references ([ArgsReference] objects) in the given
-  /// `node`, and applies [ConstructorCall]s so that all remaining widgets are
-  /// local widgets.
+  /// Resolves `fullName` to a [_ResolvedConstructor], then binds its arguments
+  /// to `arguments` (binding any [ArgsReference]s to [BoundArgsReference]s) and
+  /// expands any references to [ConstructorCall]s so that all remaining widgets
+  /// are [_CurriedWidget]s.
+  ///
+  /// Widgets can't reference each other recursively; this is enforced using the
+  /// `usedWidgets` argument.
   _CurriedWidget _applyConstructorAndBindArguments(FullyQualifiedWidgetName fullName, DynamicMap arguments, int stateDepth, Set<FullyQualifiedWidgetName> usedWidgets) {
     final _ResolvedConstructor? widget = _findConstructor(fullName);
     if (widget != null) {
@@ -341,7 +348,7 @@
         }
         usedWidgets = usedWidgets.toSet()..add(widget.fullName);
         final WidgetDeclaration constructor = widget.constructor as WidgetDeclaration;
-        int newDepth;
+        final int newDepth;
         if (constructor.initialState != null) {
           newDepth = stateDepth + 1;
         } else {
@@ -383,9 +390,11 @@
       );
     }
     if (node is DynamicList) {
-      return node.map<Object>(
-        (Object? value) => _bindArguments(context, value!, arguments, stateDepth, usedWidgets),
-      ).toList();
+      return List<Object>.generate(
+        node.length,
+        (int index) => _bindArguments(context, node[index]!, arguments, stateDepth, usedWidgets),
+        growable: false,
+      );
     }
     if (node is Loop) {
       final Object input = _bindArguments(context, node.input, arguments, stateDepth, usedWidgets);
@@ -455,9 +464,11 @@
       );
     }
     if (node is DynamicList) {
-      return node.map<Object>(
-        (Object? value) => _bindLoopVariable(value!, argument, depth),
-      ).toList();
+      return List<Object>.generate(
+        node.length,
+        (int index) => _bindLoopVariable(node[index]!, argument, depth),
+        growable: false,
+      );
     }
     if (node is Loop) {
       return Loop(_bindLoopVariable(node.input, argument, depth), _bindLoopVariable(node.output, argument, depth + 1));
@@ -516,56 +527,50 @@
 
   /// Look up the _index_th entry in `list`, expanding any loops in `list`.
   ///
-  /// If `index` is -1, this evaluates the entire list to ensure the length is available.
+  /// If `targetEffectiveIndex` is -1, this evaluates the entire list to ensure
+  /// the length is available.
   //
   // TODO(ianh): This really should have some sort of caching. Right now, evaluating a whole list
   // ends up being around O(N^2) since we have to walk the list from the start for every entry.
   static _ResolvedDynamicList _listLookup(DynamicList list, int targetEffectiveIndex, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver) {
-    if (list.any((Object? entry) => entry is Loop)) {
-      int currentIndex = 0; // where we are in `list` (some entries of which might represent multiple values, because they are themselves loops)
-      int effectiveIndex = 0; // where we are in the fully expanded list (the coordinate space in which we're aiming for `targetEffectiveIndex`)
-      while ((effectiveIndex <= targetEffectiveIndex || targetEffectiveIndex < 0) && currentIndex < list.length) {
-        final Object node = list[currentIndex]!;
-        if (node is Loop) {
-          Object inputList = node.input;
-          while (inputList is! DynamicList) {
-            if (inputList is BoundArgsReference) {
-              inputList = _resolveFrom(inputList.arguments, inputList.parts, stateResolver, dataResolver);
-            } else if (inputList is DataReference) {
-              inputList = dataResolver(inputList.parts);
-            } else if (inputList is BoundStateReference) {
-              inputList = stateResolver(inputList.parts, inputList.depth);
-            } else if (inputList is BoundLoopReference) {
-              inputList = _resolveFrom(inputList.value, inputList.parts, stateResolver, dataResolver);
-            } else if (inputList is Switch) {
-              inputList = _resolveFrom(inputList, const <Object>[], stateResolver, dataResolver);
-            } else {
-              // e.g. it's a map or something else that isn't indexable
-              inputList = DynamicList.empty();
-            }
-            assert(inputList is! _ResolvedDynamicList);
+    int currentIndex = 0; // where we are in `list` (some entries of which might represent multiple values, because they are themselves loops)
+    int effectiveIndex = 0; // where we are in the fully expanded list (the coordinate space in which we're aiming for `targetEffectiveIndex`)
+    while ((effectiveIndex <= targetEffectiveIndex || targetEffectiveIndex < 0) && currentIndex < list.length) {
+      final Object node = list[currentIndex]!;
+      if (node is Loop) {
+        Object inputList = node.input;
+        while (inputList is! DynamicList) {
+          if (inputList is BoundArgsReference) {
+            inputList = _resolveFrom(inputList.arguments, inputList.parts, stateResolver, dataResolver);
+          } else if (inputList is DataReference) {
+            inputList = dataResolver(inputList.parts);
+          } else if (inputList is BoundStateReference) {
+            inputList = stateResolver(inputList.parts, inputList.depth);
+          } else if (inputList is BoundLoopReference) {
+            inputList = _resolveFrom(inputList.value, inputList.parts, stateResolver, dataResolver);
+          } else if (inputList is Switch) {
+            inputList = _resolveFrom(inputList, const <Object>[], stateResolver, dataResolver);
+          } else {
+            // e.g. it's a map or something else that isn't indexable
+            inputList = DynamicList.empty();
           }
-          final _ResolvedDynamicList entry = _listLookup(inputList, targetEffectiveIndex >= 0 ? targetEffectiveIndex - effectiveIndex : -1, stateResolver, dataResolver);
-          if (entry.result != null) {
-            final Object boundResult = _bindLoopVariable(node.output, entry.result!, 0);
-            return _ResolvedDynamicList(null, boundResult, null);
-          }
-          effectiveIndex += entry.length!;
-        } else { // list[currentIndex] is not a Loop
-          if (effectiveIndex == targetEffectiveIndex) {
-            return _ResolvedDynamicList(null, list[currentIndex], null);
-          }
-          effectiveIndex += 1;
+          assert(inputList is! _ResolvedDynamicList);
         }
-        currentIndex += 1;
+        final _ResolvedDynamicList entry = _listLookup(inputList, targetEffectiveIndex >= 0 ? targetEffectiveIndex - effectiveIndex : -1, stateResolver, dataResolver);
+        if (entry.result != null) {
+          final Object boundResult = _bindLoopVariable(node.output, entry.result!, 0);
+          return _ResolvedDynamicList(null, boundResult, null);
+        }
+        effectiveIndex += entry.length!;
+      } else { // list[currentIndex] is not a Loop
+        if (effectiveIndex == targetEffectiveIndex) {
+          return _ResolvedDynamicList(null, list[currentIndex], null);
+        }
+        effectiveIndex += 1;
       }
-      return _ResolvedDynamicList(list, null, effectiveIndex);
-    } else {
-      if (targetEffectiveIndex < 0 || targetEffectiveIndex >= list.length) {
-        return _ResolvedDynamicList(list, null, list.length);
-      }
-      return _ResolvedDynamicList(list, list[targetEffectiveIndex]!, list.length);
+      currentIndex += 1;
     }
+    return _ResolvedDynamicList(list, null, effectiveIndex);
   }
 
   static Object _resolveFrom(Object root, List<Object> parts, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver) {
@@ -616,12 +621,15 @@
         current = value;
         continue;
       } else if (index >= parts.length) {
+        // We've reached the end of the line.
+        // We handle some special leaf cases that still need processing before we return.
         if (current is EventHandler) {
           current = EventHandler(current.eventName, _fix(current.eventArguments, stateResolver, dataResolver) as DynamicMap);
         } else if (current is SetStateHandler) {
           current = SetStateHandler(current.stateReference, _fix(current.value, stateResolver, dataResolver));
         }
-        break;
+        // else `current` is nothing special, and we'll just return it below.
+        break; // This is where the loop ends.
       } else if (current is DynamicMap) {
         if (parts[index] is! String) {
           return missing;
@@ -723,12 +731,11 @@
 
   @override
   Widget buildChild(BuildContext context, DataSource source, DynamicContent data, RemoteEventHandler remoteEventTarget, List<_WidgetState> states,  _StateResolverCallback stateResolver, _DataResolverCallback dataResolver) {
-    Object result = _CurriedWidget._resolveFrom(root, const <Object>[], stateResolver, dataResolver);
-    if (result is _CurriedWidget) {
-      result = result.build(context, data, remoteEventTarget, states);
-      return result as Widget;
+    final Object resolvedWidget = _CurriedWidget._resolveFrom(root, const <Object>[], stateResolver, dataResolver);
+    if (resolvedWidget is _CurriedWidget) {
+      return resolvedWidget.build(context, data, remoteEventTarget, states);
     }
-    return _buildErrorWidget('Switch in $fullName did not resolve to a widget (got $result).');
+    return _buildErrorWidget('Switch in $fullName did not resolve to a widget (got $resolvedWidget).');
   }
 
   @override
@@ -849,10 +856,10 @@
   }
 
   @override
-  T? v<T>(List<Object> argsKey) {
-    assert(T == Object || T == int || T == double || T == bool || T == String);
+  T? v<T extends Object>(List<Object> argsKey) {
+    assert(T == int || T == double || T == bool || T == String);
     final Object value = _fetch(argsKey, expandLists: false);
-    return value is T ? value as T : null;
+    return value is T ? value : null;
   }
 
   @override
@@ -905,12 +912,16 @@
     if (value is _ResolvedDynamicList) {
       assert(value.length != null);
       final DynamicList fullList = _fetchList(argsKey, value.length!);
-      return fullList.map<Widget>((Object? node) {
-        if (node is _CurriedWidget) {
-          return node.build(context, widget.data, widget.remoteEventTarget, _states);
-        }
-        return _buildErrorWidget('Not a widget at $argsKey (got $node) for ${widget.curriedWidget.fullName}.');
-      }).toList();
+      return List<Widget>.generate(
+        fullList.length,
+        (int index) {
+          final Object? node = fullList[index];
+          if (node is _CurriedWidget) {
+            return node.build(context, widget.data, widget.remoteEventTarget, _states);
+          }
+          return _buildErrorWidget('Not a widget at $argsKey (got $node) for ${widget.curriedWidget.fullName}.');
+        },
+      );
     }
     if (value == missing) {
       return const <Widget>[];
diff --git a/packages/rfw/pubspec.yaml b/packages/rfw/pubspec.yaml
index 37dfdbf..373f89f 100644
--- a/packages/rfw/pubspec.yaml
+++ b/packages/rfw/pubspec.yaml
@@ -1,8 +1,8 @@
 name: rfw
-description: Remote Flutter widgets
+description: "Remote Flutter widgets: a library for rendering declarative widget description files at runtime."
 repository: https://github.com/flutter/packages/tree/master/packages/rfw
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+rfw%22
-version: 1.0.0
+version: 1.0.1
 
 environment:
   sdk: ">=2.13.0 <3.0.0"
diff --git a/packages/rfw/run_tests.sh b/packages/rfw/run_tests.sh
index abf2398..4b205c5 100755
--- a/packages/rfw/run_tests.sh
+++ b/packages/rfw/run_tests.sh
@@ -6,7 +6,7 @@
 # Please update these targets when you update this package.
 # Please ensure that test coverage continues to be 100%.
 
-TARGET_LINES=2125
+TARGET_LINES=2121
 TARGET_PERCENT=100
 LAST_UPDATE="2021-08-30"