renderToNative method

Future<String?> renderToNative(
  1. DCFComponentNode node, {
  2. String? parentViewId,
  3. int? index,
})

Render a node to native UI

Implementation

Future<String?> renderToNative(DCFComponentNode node,
    {String? parentViewId, int? index}) async {
  await isReady;

  try {
    // Handle Fragment nodes
    if (node is DCFFragment) {
      // Mount the fragment
      if (!node.isMounted) {
        node.mount(node.parent);
      }

      // Check if this fragment is a portal placeholder
      if (node.metadata != null && node.metadata!['isPortalPlaceholder'] == true) {
        // This is a portal placeholder fragment - handle portal logic
        final targetId = node.metadata!['targetId'] as String?;
        final portalId = node.metadata!['portalId'] as String?;

        if (targetId != null && portalId != null) {
          // Portal placeholder fragments don't render anything here
          // The portal manager handles rendering the children to the target

          if (kDebugMode) {
            print('🎯 VDom: Portal placeholder fragment for target: $targetId');
          }

          return null; // Portal placeholders have no native view
        }
      }

      // Regular fragment - render children directly to parent
      int childIndex = index ?? 0;
      final childIds = <String>[];

      for (final child in node.children) {
        final childId = await renderToNative(
          child,
          parentViewId: parentViewId,
          index: childIndex++,
        );

        if (childId != null && childId.isNotEmpty) {
          childIds.add(childId);
        }
      }

      // Store child IDs for cleanup later
      node.childViewIds = childIds;

      return null; // Fragments don't have their own native view ID
    }

    // Handle Component nodes
    if (node is StatefulComponent || node is StatelessComponent) {
      try {
        // Register the component
        registerComponent(node);

        // Get the rendered content
        final renderedNode = node.renderedNode;
        if (renderedNode == null) {
          throw Exception('Component rendered null');
        }

        // Set parent relationship
        renderedNode.parent = node;

        // Render the content
        final viewId = await renderToNative(renderedNode, parentViewId: parentViewId, index: index);

        // Store the view ID
        node.contentViewId = viewId;

        // Call lifecycle method if not already mounted
        if (node is StatefulComponent && !node.isMounted) {
          node.componentDidMount();
        } else if (node is StatelessComponent && !node.isMounted) {
          node.componentDidMount();
        }

        // Run effects for stateful components
        if (node is StatefulComponent) {
          node.runEffectsAfterRender();
        }

        return viewId;
      } catch (error, stackTrace) {
        // Try to find nearest error boundary
        final errorBoundary = _findNearestErrorBoundary(node);
        if (errorBoundary != null) {
          errorBoundary.handleError(error, stackTrace);
          return null; // Error handled by boundary
        }

        // No error boundary, propagate error
        rethrow;
      }
    }
    // Handle Element nodes
    else if (node is DCFElement) {
      return await _renderElementToNative(node, parentViewId: parentViewId, index: index);
    }
    // Handle EmptyVDomNode
    else if (node is EmptyVDomNode) {
      return null; // Empty nodes don't create native views
    }

    return null;
  } catch (e, stack) {
    if (kDebugMode) {
      developer.log('Error rendering node: $e',
          name: 'VDom', error: e, stackTrace: stack);
    }
    return null;
  }
}