writeHostApi method

  1. @override
void writeHostApi(
  1. DartOptions generatorOptions,
  2. Root root,
  3. Indent indent,
  4. Api api, {
  5. required String dartPackageName,
})
override

Writes the code for host Api, api. Example: class FooCodec extends StandardMessageCodec {...}

class Foo { Foo(BinaryMessenger? binaryMessenger) {} static const MessageCodec<Object?> codec = FooCodec(); Future

Messages will be sent and received in a list.

If the message received was successful, the result will be contained at the 0'th index.

If the message was a failure, the list will contain 3 items: a code, a message, and details in that order.

Implementation

@override
void writeHostApi(
  DartOptions generatorOptions,
  Root root,
  Indent indent,
  Api api, {
  required String dartPackageName,
}) {
  assert(api.location == ApiLocation.host);
  String codecName = _standardMessageCodec;
  if (getCodecClasses(api, root).isNotEmpty) {
    codecName = _getCodecName(api);
    _writeCodec(indent, codecName, api, root);
  }
  final List<String> customEnumNames =
      root.enums.map((Enum x) => x.name).toList();
  indent.newln();
  bool first = true;
  addDocumentationComments(
      indent, api.documentationComments, _docCommentSpec);
  indent.write('class ${api.name} ');
  indent.addScoped('{', '}', () {
    indent.format('''
/// Constructor for [${api.name}].  The [binaryMessenger] named argument is
/// available for dependency injection.  If it is left null, the default
/// BinaryMessenger will be used which routes to the host platform.
${api.name}({BinaryMessenger? binaryMessenger})
\t\t: _binaryMessenger = binaryMessenger;
final BinaryMessenger? _binaryMessenger;
''');

    indent
        .writeln('static const MessageCodec<Object?> codec = $codecName();');
    indent.newln();
    for (final Method func in api.methods) {
      if (!first) {
        indent.newln();
      } else {
        first = false;
      }
      addDocumentationComments(
          indent, func.documentationComments, _docCommentSpec);
      String argSignature = '';
      String sendArgument = 'null';
      if (func.arguments.isNotEmpty) {
        String argNameFunc(int index, NamedType type) =>
            _getSafeArgumentName(index, type);
        final Iterable<String> argExpressions =
            indexMap(func.arguments, (int index, NamedType type) {
          final String name = argNameFunc(index, type);
          if (root.enums
              .map((Enum e) => e.name)
              .contains(type.type.baseName)) {
            return '$name${type.type.isNullable ? '?' : ''}.index';
          } else {
            return name;
          }
        });
        sendArgument = '<Object?>[${argExpressions.join(', ')}]';
        argSignature = _getMethodArgumentsSignature(func, argNameFunc);
      }
      indent.write(
        'Future<${_addGenericTypesNullable(func.returnType)}> ${func.name}($argSignature) async ',
      );
      indent.addScoped('{', '}', () {
        final String channelName =
            makeChannelName(api, func, dartPackageName);
        indent.writeln(
            'final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(');
        indent.nest(2, () {
          indent.writeln("'$channelName', codec,");
          indent.writeln('binaryMessenger: _binaryMessenger);');
        });
        final String returnType = _makeGenericTypeArguments(func.returnType);
        final String genericCastCall = _makeGenericCastCall(func.returnType);
        const String accessor = 'replyList[0]';
        // Avoid warnings from pointlessly casting to `Object?`.
        final String nullablyTypedAccessor =
            returnType == 'Object' ? accessor : '($accessor as $returnType?)';
        final String nullHandler = func.returnType.isNullable
            ? (genericCastCall.isEmpty ? '' : '?')
            : '!';
        String returnStatement = 'return';
        if (customEnumNames.contains(returnType)) {
          if (func.returnType.isNullable) {
            returnStatement =
                '$returnStatement ($accessor as int?) == null ? null : $returnType.values[$accessor! as int]';
          } else {
            returnStatement =
                '$returnStatement $returnType.values[$accessor! as int]';
          }
        } else if (!func.returnType.isVoid) {
          returnStatement =
              '$returnStatement $nullablyTypedAccessor$nullHandler$genericCastCall';
        }
        returnStatement = '$returnStatement;';

        indent.format('''
final List<Object?>? replyList =
\t\tawait channel.send($sendArgument) as List<Object?>?;
if (replyList == null) {
\tthrow PlatformException(
\t\tcode: 'channel-error',
\t\tmessage: 'Unable to establish connection on channel.',
\t);
} else if (replyList.length > 1) {
\tthrow PlatformException(
\t\tcode: replyList[0]! as String,
\t\tmessage: replyList[1] as String?,
\t\tdetails: replyList[2],
\t);''');
        // On iOS we can return nil from functions to accommodate error
        // handling.  Returning a nil value and not returning an error is an
        // exception.
        if (!func.returnType.isNullable && !func.returnType.isVoid) {
          indent.format('''
} else if (replyList[0] == null) {
\tthrow PlatformException(
\t\tcode: 'null-error',
\t\tmessage: 'Host platform returned null value for non-null return value.',
\t);''');
        }
        indent.format('''
} else {
\t$returnStatement
}''');
      });
    }
  });
}