motionFromJson function

MotionDocument motionFromJson(
  1. Map<String, Object?> json, {
  2. required String sceneClassName,
  3. required SceneDocument scene,
})

The inverse of MotionDocumentJson.toJson.

scene is required because a group holds the NODE it animates, and JSON can only carry the name — so this is the one place a name becomes a node again, and it is a read of something the editor wrote.

Implementation

MotionDocument motionFromJson(
  Map<String, Object?> json, {
  required String sceneClassName,
  required SceneDocument scene,
}) {
  var doc = MotionDocument(sceneClassName: sceneClassName);
  for (var raw in (json['params'] as List? ?? const [])) {
    var p = (raw as Map).cast<String, Object?>();
    var kind = SceneParamKind.values.byName(p['kind']! as String);
    doc.params.add(
      SceneParamDecl(
        p['name']! as String,
        kind,
        _paramValue(kind, p['default']),
      ),
    );
  }
  for (var raw in (json['groups'] as List? ?? const [])) {
    var g = (raw as Map).cast<String, Object?>();
    var target = g['target']! as String;
    var node = scene.nodeNamed(target);
    if (node == null) {
      throw ArgumentError(
        'the motion animates "$target", which this scene does not declare',
      );
    }
    var group = AnimateGroup(node, name: g['name']! as String);
    for (var e in ((g['tracks'] as Map?) ?? const {}).entries) {
      group.tracks['${e.key}'] = _trackFromJson(
        (e.value as Map).cast<String, Object?>(),
      );
    }
    // A payload written before the maps became one carried the argument
    // tracks under their bare names in a map of their own.
    for (var e in ((g['args'] as Map?) ?? const {}).entries) {
      group.tracks['$sceneArgsPrefix${e.key}'] = _trackFromJson(
        (e.value as Map).cast<String, Object?>(),
      );
    }
    doc.groups.add(group);
  }
  doc.timeline = _exprFromJson(
    (json['timeline']! as Map).cast<String, Object?>(),
    doc,
  );
  return doc;
}