How to read flutter/dart class constructor argument types?

42 views Asked by At

I'm experimenting with possibly validating the plain data class against JsonSchema. First I tried to read the declared arguments of a given class constructor. Such as argument name, type, nullability, or required.

The dart analyzer package seems promising. But I can't find a way to determine the argument type.

This is the example code I created. This code gives me correct argument names and required or not. But I can't find the type of information.

import 'dart:io';

import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:flutter/cupertino.dart';

class Data {
  const Data({
    required this.id,
    required this.name,
    this.codename,
    this.value = 0,
  });
  final int id;
  final String name;
  final String? codename;
  final int value;
}

class PropertyAnalyzer extends RecursiveAstVisitor<void> {

  @override
  void visitConstructorDeclaration(ConstructorDeclaration node) {
    debugPrint("constructor: ${node.name}");
    final parameters = node.parameters.parameters;
    for (final parameter in parameters) {
      final paramName = parameter.name;
      final paramType = parameter.declaredElement;
      final required = parameter.isRequired;
      final defaultValue = parameter is DefaultFormalParameter ? parameter.defaultValue : null;
      debugPrint("$paramName\t$paramType\t$required\t$defaultValue");
    }
  }
}

String getSdkPath() {
  final exePath = Platform.resolvedExecutable;
  final i = exePath.indexOf("/flutter/bin");
  return "${exePath.substring(0, i)}/flutter/bin/cache/dart-sdk";
}

void main() {
  const filePath = "path/to/data.dart";
  final collection = AnalysisContextCollection(includedPaths: [filePath], sdkPath: getSdkPath());
  for (final c in collection.contexts) {
    final currentSession = c.currentSession;
    final result = currentSession.getParsedUnit(filePath);
    if (result is ParsedUnitResult) {
      result.unit.accept(PropertyAnalyzer());
    }
  }
}


My question is Am I going in the right direction? If this is the right direction then how to I determine the type information?

1

There are 1 answers

1
Hamed On

You can use mirrors to inspect and read the constructor argument types of a class at runtime:

import 'dart:mirrors';

class SampleClass {
  final String name;
  final int age;
  final bool isMarried;

  SampleClass({required this.name, required this.age, required this.isMarried});
}

void main() {
  final classMirror = reflectClass(SampleClass);

  final constructorMirror = classMirror.declarations.values.firstWhere(
    (declaration) => declaration is MethodMirror && declaration.isConstructor,
  ) as MethodMirror;

  for (var parameterMirror in constructorMirror.parameters) {
    final paramName = MirrorSystem.getName(parameterMirror.simpleName);
    final paramType = parameterMirror.type.reflectedType;
    
    print('$paramName: $paramType');
  }
}

Output:

name: String
age: int
isMarried: bool

Note that mirrors is not supported in Flutter because it relies on runtime reflection, which Flutter's tree shaking and AOT (Ahead-Of-Time) compilation do not allow.