writeFlutterApi method

  1. @override
void writeFlutterApi(
  1. DartOptions generatorOptions,
  2. Root root,
  3. Indent indent,
  4. Api api, {
  5. String channelNameFunc(
    1. Method
    )?,
  6. bool isMockHandler = false,
  7. required String dartPackageName,
})
override

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

abstract class Foo { static const MessageCodec<Object?> codec = FooCodec(); int add(int x, int y); static void setup(Foo api, {BinaryMessenger? binaryMessenger}) {...} }

Implementation

@override
void writeFlutterApi(
  DartOptions generatorOptions,
  Root root,
  Indent indent,
  Api api, {
  String Function(Method)? channelNameFunc,
  bool isMockHandler = false,
  required String dartPackageName,
}) {
  assert(api.location == ApiLocation.flutter);
  String codecName = _standardMessageCodec;
  if (getCodecClasses(api, root).isNotEmpty) {
    codecName = _getCodecName(api);
    _writeCodec(indent, codecName, api, root);
  }
  indent.newln();
  addDocumentationComments(
      indent, api.documentationComments, _docCommentSpec);

  indent.write('abstract class ${api.name} ');
  indent.addScoped('{', '}', () {
    if (isMockHandler) {
      indent.writeln(
          'static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance;');
    }
    indent.writeln(
        'static const MessageCodec<Object?> $_pigeonChannelCodec = $codecName();');
    indent.newln();
    for (final Method func in api.methods) {
      addDocumentationComments(
          indent, func.documentationComments, _docCommentSpec);

      final bool isAsync = func.isAsynchronous;
      final String returnType = isAsync
          ? 'Future<${_addGenericTypesNullable(func.returnType)}>'
          : _addGenericTypesNullable(func.returnType);
      final String argSignature = _getMethodParameterSignature(func);
      indent.writeln('$returnType ${func.name}($argSignature);');
      indent.newln();
    }
    indent.write(
        'static void setup(${api.name}? api, {BinaryMessenger? binaryMessenger}) ');
    indent.addScoped('{', '}', () {
      for (final Method func in api.methods) {
        indent.write('');
        indent.addScoped('{', '}', () {
          indent.writeln(
            'final BasicMessageChannel<Object?> ${_varNamePrefix}channel = BasicMessageChannel<Object?>(',
          );
          final String channelName = channelNameFunc == null
              ? makeChannelName(api, func, dartPackageName)
              : channelNameFunc(func);
          indent.nest(2, () {
            indent.writeln("'$channelName', $_pigeonChannelCodec,");
            indent.writeln(
              'binaryMessenger: binaryMessenger);',
            );
          });
          final String messageHandlerSetterWithOpeningParentheses = isMockHandler
              ? '_testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler<Object?>(${_varNamePrefix}channel, '
              : '${_varNamePrefix}channel.setMessageHandler(';
          indent.write('if (api == null) ');
          indent.addScoped('{', '}', () {
            indent.writeln(
                '${messageHandlerSetterWithOpeningParentheses}null);');
          }, addTrailingNewline: false);
          indent.add(' else ');
          indent.addScoped('{', '}', () {
            indent.write(
              '$messageHandlerSetterWithOpeningParentheses(Object? message) async ',
            );
            indent.addScoped('{', '});', () {
              final String returnType =
                  _addGenericTypesNullable(func.returnType);
              final bool isAsync = func.isAsynchronous;
              const String emptyReturnStatement =
                  'return wrapResponse(empty: true);';
              String call;
              if (func.parameters.isEmpty) {
                call = 'api.${func.name}()';
              } else {
                indent.writeln('assert(message != null,');
                indent.writeln("'Argument for $channelName was null.');");
                const String argsArray = 'args';
                indent.writeln(
                    'final List<Object?> $argsArray = (message as List<Object?>?)!;');
                String argNameFunc(int index, NamedType type) =>
                    _getSafeArgumentName(index, type);
                enumerate(func.parameters, (int count, NamedType arg) {
                  final String argType = _addGenericTypes(arg.type);
                  final String argName = argNameFunc(count, arg);
                  final String genericArgType =
                      _makeGenericTypeArguments(arg.type);
                  final String castCall = _makeGenericCastCall(arg.type);

                  final String leftHandSide = 'final $argType? $argName';
                  if (arg.type.isEnum) {
                    indent.writeln(
                        '$leftHandSide = $argsArray[$count] == null ? null : $argType.values[$argsArray[$count]! as int];');
                  } else {
                    indent.writeln(
                        '$leftHandSide = ($argsArray[$count] as $genericArgType?)${castCall.isEmpty ? '' : '?$castCall'};');
                  }
                  if (!arg.type.isNullable) {
                    indent.writeln('assert($argName != null,');
                    indent.writeln(
                        "    'Argument for $channelName was null, expected non-null $argType.');");
                  }
                });
                final Iterable<String> argNames =
                    indexMap(func.parameters, (int index, Parameter field) {
                  final String name = _getSafeArgumentName(index, field);
                  return '${field.isNamed ? '${field.name}: ' : ''}$name${field.type.isNullable ? '' : '!'}';
                });
                call = 'api.${func.name}(${argNames.join(', ')})';
              }
              indent.writeScoped('try {', '} ', () {
                if (func.returnType.isVoid) {
                  if (isAsync) {
                    indent.writeln('await $call;');
                  } else {
                    indent.writeln('$call;');
                  }
                  indent.writeln(emptyReturnStatement);
                } else {
                  if (isAsync) {
                    indent.writeln('final $returnType output = await $call;');
                  } else {
                    indent.writeln('final $returnType output = $call;');
                  }

                  const String returnExpression = 'output';
                  final String nullability =
                      func.returnType.isNullable ? '?' : '';
                  final String valueExtraction =
                      func.returnType.isEnum ? '$nullability.index' : '';
                  final String returnStatement = isMockHandler
                      ? 'return <Object?>[$returnExpression$valueExtraction];'
                      : 'return wrapResponse(result: $returnExpression$valueExtraction);';
                  indent.writeln(returnStatement);
                }
              }, addTrailingNewline: false);
              indent.addScoped('on PlatformException catch (e) {', '}', () {
                indent.writeln('return wrapResponse(error: e);');
              }, addTrailingNewline: false);

              indent.writeScoped('catch (e) {', '}', () {
                indent.writeln(
                    "return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));");
              });
            });
          });
        });
      }
    });
  });
}