DEV Community

kanta13jp1
kanta13jp1

Posted on

Flutter Web Rendering Complete Guide — CanvasKit vs HTML Renderer

Flutter Web Rendering Complete Guide — CanvasKit vs HTML Renderer

Flutter Web supports two renderers. Understanding CanvasKit vs HTML renderer helps you pick the right one for your app.

Two Renderers

HTML Renderer

  • How it works: HTML / CSS / Canvas 2D API
  • Bundle size: Small (~1MB)
  • Initial load: Fast
  • Fidelity: May differ slightly from Flutter desktop
  • Best for: Text-heavy, document-style apps

CanvasKit Renderer

  • How it works: Skia compiled to WebAssembly
  • Bundle size: Large (~4MB with Skia WASM)
  • Initial load: Slower (WASM download required)
  • Fidelity: Pixel-perfect match with native
  • Best for: Graphics-heavy apps, games, custom drawing

Switching Renderers

# Build with HTML renderer
flutter build web --web-renderer html

# Build with CanvasKit
flutter build web --web-renderer canvaskit

# Auto (default): mobile → HTML / desktop → CanvasKit
flutter build web --web-renderer auto

# Dev server
flutter run -d chrome --web-renderer canvaskit
Enter fullscreen mode Exit fullscreen mode

Checking the Active Renderer

import 'package:flutter/foundation.dart';

void checkRenderer() {
  if (kIsWeb) {
    debugPrint('Renderer: ${RendererBinding.instance.rendererType}');
  }
}
Enter fullscreen mode Exit fullscreen mode

Custom Painting

class ChartPainter extends CustomPainter {
  final List<double> data;
  ChartPainter(this.data);

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = const Color(0xFF4F46E5)
      ..strokeWidth = 2
      ..style = PaintingStyle.stroke;

    final path = Path();
    for (int i = 0; i < data.length; i++) {
      final x = size.width * i / (data.length - 1);
      final y = size.height * (1 - data[i]);
      i == 0 ? path.moveTo(x, y) : path.lineTo(x, y);
    }
    canvas.drawPath(path, paint);

    final fillPath = Path.from(path)
      ..lineTo(size.width, size.height)
      ..lineTo(0, size.height)
      ..close();

    canvas.drawPath(
      fillPath,
      Paint()
        ..color = const Color(0xFF4F46E5).withOpacity(0.1)
        ..style = PaintingStyle.fill,
    );
  }

  @override
  bool shouldRepaint(ChartPainter old) => old.data != data;
}
Enter fullscreen mode Exit fullscreen mode

Web-Specific Optimizations

import 'package:flutter/foundation.dart';

class WebOptimizedImage extends StatelessWidget {
  final String url;
  const WebOptimizedImage(this.url);

  @override
  Widget build(BuildContext context) {
    if (kIsWeb) {
      return Image.network(
        url,
        cacheWidth: 800,
        loadingBuilder: (context, child, progress) {
          if (progress == null) return child;
          return const CircularProgressIndicator();
        },
      );
    }
    return CachedNetworkImage(imageUrl: url);
  }
}
Enter fullscreen mode Exit fullscreen mode

SEO with HTML Renderer

import 'package:url_strategy/url_strategy.dart';

void main() {
  setPathUrlStrategy(); // Remove hash (#) from URLs
  runApp(const App());
}
Enter fullscreen mode Exit fullscreen mode
<!-- web/index.html -->
<meta name="description" content="AI Life Management App">
<meta property="og:title" content="Jibun AI">
<link rel="canonical" href="https://my-web-app-b67f4.web.app/">
Enter fullscreen mode Exit fullscreen mode

PWA Configuration

{
  "name": "Jibun AI",
  "short_name": "Jibun AI",
  "start_url": ".",
  "display": "standalone",
  "background_color": "#1e1b4b",
  "theme_color": "#4f46e5",
  "icons": [
    {"src": "icons/Icon-192.png", "sizes": "192x192", "type": "image/png"},
    {"src": "icons/Icon-512.png", "sizes": "512x512", "type": "image/png"}
  ]
}
Enter fullscreen mode Exit fullscreen mode

Comparison Table

HTML renderer CanvasKit
Bundle size ~1MB ~4MB
Initial load Fast Slower
Fidelity Approximate Pixel-perfect
SEO Good Needs work
Best for Docs / LP Graphics / Games

Start with Auto mode and switch based on your specific requirements.


Building an AI Life Management app with Flutter × Supabase at 自分株式会社. Sharing indie dev insights every week.

Top comments (0)