deve-se seguir exatamente o modo de transformação e texturização do código alvo(claro que, esse código renderiza uma cena, queremos renderizar apenas um objeto, que é a esfera, então algumas partes não são tão necessárias assim:


código alvo para ser replicado:
  void render(Canvas canvas, Size size) {
    // check if texture needs to update
    if (_needsUpdateTexture) {
      _needsUpdateTexture = false;
      _updateTexture();
    }

    // create render mesh from objects
    final renderMesh = _makeRenderMesh();
    _renderObject(renderMesh, world, Matrix4.identity(), camera.lookAtMatrix, camera.projectionMatrix);

    // remove the culled faces and recreate list.
    final List<Polygon> renderIndices = <Polygon>[];
    final List<Polygon?> rawIndices = renderMesh.indices;
    for (int i = 0; i < rawIndices.length; i++) {
      final Polygon? p = rawIndices[i];
      if (p != null) renderIndices.add(p);
    }
    if (renderIndices.length == 0) return;

    // sort the faces by z
    renderIndices.sort((Polygon a, Polygon b) {
      // return b.sumOfZ.compareTo(a.sumOfZ);
      final double az = a.sumOfZ;
      final double bz = b.sumOfZ;
      if (bz > az) return 1;
      if (bz < az) return -1;
      return 0;
    });

    // convert Polygon list to Uint16List
    final int indexCount = renderIndices.length;
    final Uint16List indices = Uint16List(indexCount * 3);
    for (int i = 0; i < indexCount; i++) {
      final int index0 = i * 3;
      final int index1 = index0 + 1;
      final int index2 = index0 + 2;
      final Polygon polygon = renderIndices[i];
      indices[index0] = polygon.vertex0;
      indices[index1] = polygon.vertex1;
      indices[index2] = polygon.vertex2;
    }

    final vertices = Vertices.raw(
      VertexMode.triangles,
      renderMesh.positions,
      textureCoordinates: renderMesh.texture == null ? null : renderMesh.texcoords,
      colors: renderMesh.colors,
      indices: indices,
    );

    final paint = Paint();
    if (renderMesh.texture != null) {
      Float64List matrix4 = new Matrix4.identity().storage;
      final shader = ImageShader(renderMesh.texture!, TileMode.mirror, TileMode.mirror, matrix4);
      paint.shader = shader;
    }
    paint.blendMode = blendMode;
    canvas.drawVertices(vertices, textureBlendMode, paint);
  }

  void _renderObject(RenderMesh renderMesh, Object o, Matrix4 model, Matrix4 view, Matrix4 projection) {
    if (!o.visiable) return;
    model *= o.transform;
    final Matrix4 transform = projection * view * model;

    // apply transform and add vertices to renderMesh
    final double viewportWidth = camera.viewportWidth;
    final double viewportHeight = camera.viewportHeight;
    final Float32List positions = renderMesh.positions;
    final Float32List positionsZ = renderMesh.positionsZ;
    final List<Vector3> vertices = o.mesh.vertices;
    final int vertexOffset = renderMesh.vertexCount;
    final int vertexCount = vertices.length;
    final Vector4 v = Vector4.identity();
    for (int i = 0; i < vertexCount; i++) {
      // Conver Vector3 to Vector4
      final Float64List storage3 = vertices[i].storage;
      v.setValues(storage3[0], storage3[1], storage3[2], 1.0);
      // apply "model => world => camera => perspective" transform
      v.applyMatrix4(transform);
      // transform from homonegenous coordinates to the normalized device coordinates，
      final int xIndex = (vertexOffset + i) * 2;
      final int yIndex = xIndex + 1;
      final Float64List storage4 = v.storage;
      final double w = storage4[3]; //v.w;
      positions[xIndex] = storage4[0] / w; //v.x;
      positions[yIndex] = storage4[1] / w; //v.y;
      positionsZ[vertexOffset + i] = storage4[2] / w; //v.z;
    }
    renderMesh.vertexCount += vertexCount;


    for (int i = 0; i < vertexCount; i++) {
      final int x = (vertexOffset + i) * 2;
      final int y = x + 1;
      // remaps coordinates from [-1, 1] to the [0, viewport] space.
      positions[x] = (1.0 + positions[x]) * viewportWidth / 2;
      positions[y] = (1.0 - positions[y]) * viewportHeight / 2;
    }

    // add texture coordinates to renderMesh
    final int texcoordCount = o.mesh.vertices.length;
    final Float32List renderTexcoords = renderMesh.texcoords;
    if (o.mesh.texture != null && o.mesh.texcoords.length == texcoordCount) {
      final int imageWidth = o.mesh.textureRect.width.toInt();
      final int imageHeight = o.mesh.textureRect.height.toInt();
      final double imageLeft = o.mesh.textureRect.left;
      final double imageTop = o.mesh.textureRect.top;
      final List<Offset> texcoords = o.mesh.texcoords;
      for (int i = 0; i < texcoordCount; i++) {
        final Offset t = texcoords[i];
        final double x = t.dx * imageWidth + imageLeft;
        final double y = (1.0 - t.dy) * imageHeight + imageTop;
        final int xIndex = (vertexOffset + i) * 2;
        final int yIndex = xIndex + 1;
        renderTexcoords[xIndex] = x;
        renderTexcoords[yIndex] = y;
      }
    } else {
      for (int i = 0; i < texcoordCount; i++) {
        final int xIndex = (vertexOffset + i) * 2;
        final int yIndex = xIndex + 1;
        renderTexcoords[xIndex] = 0;
        renderTexcoords[yIndex] = 0;
      }
    }

 List<Object> children = o.children;
    for (int i = 0; i < children.length; i++) {
      _renderObject(renderMesh, children[i], model, view, projection);
    }


meu código:

import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:vector_math/vector_math_64.dart' hide Colors;
import 'dart:math' as math;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('3D Sphere with Fixed Texture')),
        body: Center(child: SphereWidget()),
        backgroundColor: const Color.fromARGB(255, 52, 13, 245),
      ),
    );
  }
}

class SphereWidget extends StatefulWidget {
  @override
  _SphereWidgetState createState() => _SphereWidgetState();
}

class _SphereWidgetState extends State<SphereWidget> {
  double angle = 0;
  ui.Image? texture;

  @override
  void initState() {
    super.initState();
    loadImageFromAsset('assets/nova.png');
    rotate();
  }

  void rotate() {
    Timer.periodic(Duration(milliseconds: 16), (timer) {
      setState(() {
        angle += 0.01;
      });
    });
  }

  Future<void> loadImageFromAsset(String path) async {
    final completer = Completer<ui.Image>();
    final image = AssetImage(path);
    image.resolve(ImageConfiguration()).addListener(
      ImageStreamListener((info, _) {
        completer.complete(info.image);
      }),
    );
    texture = await completer.future;
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    if (texture == null) {
      return Container();
    }
    return CustomPaint(
      size: const Size(200, 200),
      painter: SpherePainter(texture!, angle),
    );
  }
}

class SpherePainter extends CustomPainter {
  final ui.Image texture;
  final double angle;
  final Matrix4 transform = Matrix4.identity();
  final Vector3 position = Vector3.zero();
  final Vector3 rotation = Vector3.zero();
  final Vector3 scale = Vector3(1, 1, 1);
  final double radius = 100;
  final int latSegments = 12;
  final int lonSegments = 12;

  late List<Vector3> vertices;
  late List<Offset> texcoords;
  late List<List<int>> indices;

  SpherePainter(this.texture, this.angle) {
    vertices = List<Vector3>.filled(
        (latSegments + 1) * (lonSegments + 1), Vector3.zero());
    texcoords =
        List<Offset>.filled((latSegments + 1) * (lonSegments + 1), Offset.zero);
    indices = [];

    _generateSphere();
  }

  void _generateSphere() {
    int i = 0;
    for (int y = 0; y <= latSegments; ++y) {
      final double v = y / latSegments;
      final double sv = math.sin(v * math.pi);
      final double cv = math.cos(v * math.pi);
      for (int x = 0; x <= lonSegments; ++x) {
        final double u = x / lonSegments;
        vertices[i] = Vector3(
          radius * math.cos(u * math.pi * 2.0) * sv, // X
          radius * cv, // Y
          radius * math.sin(u * math.pi * 2.0) * sv, // Z
        );
        texcoords[i] = Offset(u, v); // Coordenadas UV normalizadas
        i++;
      }
    }

    for (int y = 0; y < latSegments; ++y) {
      final int base1 = (lonSegments + 1) * y;
      final int base2 = (lonSegments + 1) * (y + 1);
      for (int x = 0; x < lonSegments; ++x) {
        final int current = base1 + x;
        final int next = base1 + x + 1;
        final int below = base2 + x;
        final int belowNext = base2 + x + 1;

        indices.add([current, below, next]); // Triângulo inferior
        indices.add([next, below, belowNext]); // Triângulo superior
      }
    }
  }

  void updateTransform() {
    final Matrix4 model = Matrix4.identity()
      ..translate(position.x, position.y, position.z)
      ..rotateX(rotation.x)
      ..rotateY(rotation.y)
      ..rotateZ(rotation.z)
      ..scale(scale.x, scale.y, scale.z);

    final Matrix4 view = Matrix4.identity(); // Visualização
    final Matrix4 projection = Matrix4.identity(); // Projeção

    transform.setFrom(projection * view * model);
  }

  @override
  void paint(Canvas canvas, Size size) {
    canvas.translate(size.width / 2, size.height / 2);
    rotation.setValues(0, angle, angle);
    updateTransform();

    final Float32List positions = Float32List(vertices.length * 2);
    final Float32List renderTexcoords = Float32List(texcoords.length * 2);

    for (int i = 0; i < vertices.length; i++) {
      final Vector4 v = transform.transformed(
          Vector4(vertices[i].x, vertices[i].y, vertices[i].z, 1.0));
      positions[i * 2] = v.x / v.w; // X
      positions[i * 2 + 1] = v.y / v.w; // Y

      Offset t = texcoords[i];
      double u = t.dx * texture.width; // U
      double f = (1.0 - t.dy) * texture.height; // V

      renderTexcoords[i * 2] = u; // U
      renderTexcoords[i * 2 + 1] = f; // V
    }

    final Uint16List indexBuffer = Uint16List(indices.length * 3);
    int indexCount = 0;
    for (var triangle in indices) {
      indexBuffer[indexCount++] = triangle[0];
      indexBuffer[indexCount++] = triangle[1];
      indexBuffer[indexCount++] = triangle[2];
    }

    final verticesData = Vertices.raw(
      VertexMode.triangles,
      positions,
      textureCoordinates: renderTexcoords,
      indices: indexBuffer,
    );

    final Float64List matrix4 = Matrix4.identity().storage;
    final paint = Paint()
      ..shader = ImageShader(
        texture,
        TileMode.clamp,
        TileMode.clamp,
        matrix4,
      );

    canvas.drawVertices(verticesData, BlendMode.srcOver, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}