Implementation
static String dartCode(Module module, UniAPIOptions options) {
final moduleHasCallback = hasUniCallback(module.methods);
return CodeTemplate(children: [
CommentUniAPI(),
EmptyLine(),
DartImport(fullClassName: 'dart:async'),
DartImport(fullClassName: 'package:flutter/services.dart'),
if (moduleHasCallback) ...[
DartImport(
fullClassName: relative(
'${options.dartOutput}/$projectName/$dotDartFileUniCallback',
from: dirname(
productPath(options.dartOutput, module.inputFile.path)))),
DartImport(
fullClassName: relative(
'${options.dartOutput}/$projectName/$dotDartFileUniCallbackManager',
from: dirname(
productPath(options.dartOutput, module.inputFile.path)))),
DartImport(
fullClassName: relative(
'${options.dartOutput}/$projectName/$dotDartFileCaches',
from: dirname(
productPath(options.dartOutput, module.inputFile.path)))),
],
DartImport(fullClassName: 'package:flutter/foundation.dart'),
DartImport(
fullClassName: relative(
'${options.dartOutput}/$projectName/$dotDartFileprojectNameSnake',
from: dirname(
productPath(options.dartOutput, module.inputFile.path)))),
EmptyLine(),
DartCustomNestedImports(module.inputFile, options,
methods: module.methods, excludeImports: [typeUniCallback]),
EmptyLine(),
Comment(
comments: [uniNativeModuleDesc, ...module.codeComments],
commentType: CommentType.commentTripleBackSlash),
DartClass(
className: module.name,
methods: module.methods,
injectedJavaCodes: (depth) {
String generateCallbackName(
Module module, Method method, Variable param) =>
'${module.name}_${method.name}_${param.name}';
String channelName(Module module, Method method) =>
'$channelPrefix.${module.name}.${method.name}';
final ret = <CodeUnit>[];
for (final method in module.methods) {
if (method.name == module.name) {
continue;
}
// 生成 UniNativeModule 接口方法
ret.add(DartFunction(
depth: depth + 1,
functionName: method.name,
params: method.parameters,
isAsync: true,
isStatic: true,
returnType: AstCustomType(typeFuture, generics: [
method.returnType.realType()
], genericsMaybeNull: [
method.returnType.realType() is! AstVoid
]),
body: (depth) {
final funcBody = <CodeUnit>[];
final callbackParams = method.parameters
.where(
(param) => param.type.astType() == typeUniCallback)
.toList();
String? callbackName;
String? callbackInstance;
if (callbackParams.isNotEmpty) {
final callback = callbackParams.first;
callbackName =
generateCallbackName(module, method, callback);
callbackInstance =
'${callbackName}_\${${callback.name}.hashCode}';
funcBody.add(OneLine(
depth: depth + 1,
body:
"${callback.name}.callbackName = '$callbackInstance';"));
funcBody.add(OneLine(
depth: depth + 1,
body:
"uniCallbackCache['$callbackInstance'] = ${callback.name};"));
}
if (method.parameters.isNotEmpty) {
funcBody.add(OneLine(
depth: depth + 1,
body: 'final Map<String, dynamic> encoded = {};'));
}
for (final param in method.parameters) {
if (param.type.astType() == typeUniCallback) {
assert(callbackName != null);
assert(callbackInstance != null);
funcBody.add(OneLine(
depth: depth + 1,
body:
'encoded["${param.name}"] = \'$callbackInstance\';'));
} else {
final isNullable = param.type.maybeNull;
funcBody.add(OneLine(
depth: depth + 1,
body:
'${isNullable ? 'if (${param.name} != null) ' : ''}encoded["${param.name}"] = ${param.type.convertDartObj2Json(param.name)};'));
}
}
funcBody.add(OneLine(
depth: depth + 1,
body: kEnableNullSafety
? 'const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?> ('
: 'const BasicMessageChannel<Object> channel = BasicMessageChannel<Object> ('));
funcBody.add(OneLine(
depth: depth + 2,
body:
"'${channelName(module, method)}', StandardMessageCodec());"));
if (callbackParams.isNotEmpty) {
funcBody.add(OneLine(
depth: depth + 1,
body: 'UniCallbackManager.getInstance();'));
}
funcBody.add(OneLine(
depth: depth + 1,
body: kEnableNullSafety
? 'final Map<Object?, Object?>? replyMap ='
: 'final Map<Object, Object> replyMap ='));
if (method.parameters.isEmpty) {
funcBody.add(OneLine(
depth: depth + 2,
body: kEnableNullSafety
? 'await channel.send(null) as Map<Object?, Object?>?;'
: 'await channel.send(null) as Map<Object, Object>;'));
} else {
funcBody.add(OneLine(
depth: depth + 2,
body: kEnableNullSafety
? 'await channel.send(encoded) as Map<Object?, Object?>?;'
: 'await channel.send(encoded) as Map<Object, Object>;'));
}
String connectChannelError() =>
'Unable to establish connection on channel : "${channelName(module, method)}" .';
String trackErrorExpression(String msg) =>
"UniApi.trackError('${module.name}', '${channelName(module, method)}', '$msg');";
String ifErrorExpression(String msg) =>
"if (error.containsKey('$msg')) errorMsg += '[ \${error['$msg']?.toString() ?? ''} ]';";
funcBody.add(OneLine(
depth: depth + 1, body: 'if (replyMap == null) {'));
funcBody.add(OneLine(
depth: depth + 2,
body: trackErrorExpression(connectChannelError())));
funcBody.add(OneLine(
depth: depth + 2, body: 'if (!kReleaseMode) {'));
funcBody.add(OneLine(
depth: depth + 3, body: 'throw PlatformException('));
funcBody.add(OneLine(
depth: depth + 4, body: "code: 'channel-error',"));
funcBody.add(OneLine(
depth: depth + 4,
body: "message: '${connectChannelError()}',"));
funcBody
.add(OneLine(depth: depth + 4, body: 'details: null,'));
funcBody.add(OneLine(depth: depth + 3, body: ');'));
funcBody.add(OneLine(depth: depth + 2, body: '}'));
funcBody.add(OneLine(
depth: depth + 1,
body: "} else if (replyMap['error'] != null) {"));
funcBody.add(OneLine(
depth: depth + 2,
body: kEnableNullSafety
? "final Map<Object?, Object?> error = (replyMap['${Keys.error}'] as Map<Object?, Object?>);"
: "final Map<Object, Object> error = (replyMap['${Keys.error}'] as Map<Object, Object>);"));
funcBody.add(OneLine(
depth: depth + 2, body: "String errorMsg = '';"));
funcBody.add(OneLine(
depth: depth + 2,
body: ifErrorExpression(Keys.errorCode)));
funcBody.add(OneLine(
depth: depth + 2,
body: ifErrorExpression(Keys.errorMessage)));
funcBody.add(OneLine(
depth: depth + 2,
body: ifErrorExpression(Keys.errorDetails)));
funcBody.add(OneLine(
depth: depth + 2,
body: trackErrorExpression(
'${method.name}: \$errorMsg);')));
funcBody.add(OneLine(
depth: depth + 2, body: 'if (!kReleaseMode) {'));
funcBody.add(OneLine(
depth: depth + 3, body: 'throw PlatformException('));
funcBody.add(OneLine(
depth: depth + 4,
body: "code: error['${Keys.errorCode}'] as String,"));
funcBody.add(OneLine(
depth: depth + 4,
body:
"message: error['${Keys.errorMessage}'] as String,"));
funcBody.add(OneLine(
depth: depth + 4,
body: "details: error['${Keys.errorDetails}'],"));
funcBody.add(OneLine(depth: depth + 3, body: ');'));
funcBody.add(OneLine(depth: depth + 2, body: '}'));
funcBody.add(OneLine(depth: depth + 1, body: '} else {'));
var ternaryOperatorExpr =
method.returnType.realType().maybeNull == true
? '$replyPlaceholder == null ? null : '
: '';
funcBody.add(OneLine(
depth: depth + 2,
body:
'${method.returnType.realType() is AstVoid ? '' : 'return $ternaryOperatorExpr'}${method.returnType.realType().convertDartJson2Obj()};'));
funcBody.add(OneLine(depth: depth + 1, body: '}'));
if (method.returnType is! AstVoid) {
funcBody.add(OneLine(
depth: depth + 1,
body:
"throw Exception('方法 \"${method.name}\" : 返回类型错误!');"));
}
return funcBody;
}));
ret.add(EmptyLine());
}
return ret;
})
]).build();
}