Agent skill

webview-integration

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/webview-integration

SKILL.md

🌐 Skill: WebView Integration

📋 Metadata

Atributo Valor
ID flutter-webview
Nivel 🟡 Intermedio
Versión 1.0.0
Keywords webview, flutter-inappwebview, javascript, web-integration, hybrid
Referencia flutter_inappwebview

🔑 Keywords para Invocación

  • webview
  • flutter-inappwebview
  • javascript
  • web-integration
  • hybrid-app
  • @skill:webview

Ejemplos de Prompts

Implementa webview con javascript bridge
Setup flutter-inappwebview con comunicación bidireccional
Configura webview para contenido HTML dinámico
@skill:webview - Integración completa de WebView

📖 Descripción

Este skill cubre la integración de WebViews en Flutter usando flutter_inappwebview. Incluye carga de contenido HTML, JavaScript channels para comunicación Flutter ↔ Web, manejo de navegación, cookies, y storage.

✅ Cuándo Usar Este Skill

  • Mostrar contenido web en la app
  • Integrar legacy web features
  • OAuth flows externos
  • Rich text content (blog posts)
  • Web-based forms
  • Terms & Conditions / Privacy Policy
  • Hybrid apps
  • JavaScript bridge para web SDKs

❌ Cuándo NO Usar Este Skill

  • Native Flutter widgets funcionan mejor
  • Contenido estático simple (usa Text/RichText)
  • Performance crítica (WebViews son más lentos)

🏗️ Estructura del Proyecto

my_app/
├── lib/
│   ├── services/
│   │   └── webview/
│   │       ├── webview_service.dart
│   │       ├── javascript_handler.dart
│   │       └── webview_controller_manager.dart
│   │
│   ├── screens/
│   │   └── webview_screen.dart
│   │
│   └── main.dart
│
└── assets/
    └── web/
        ├── index.html
        └── bridge.js

📦 Dependencias

yaml
dependencies:
  flutter:
    sdk: flutter

  # WebView
  flutter_inappwebview: ^6.0.0

  # URL launcher (for external links)
  url_launcher: ^6.2.2

dev_dependencies:
  flutter_test:
    sdk: flutter

💻 Implementación

1. Basic WebView

dart
// lib/screens/webview_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

class WebViewScreen extends StatefulWidget {
  final String url;
  final String? title;

  const WebViewScreen({
    Key? key,
    required this.url,
    this.title,
  }) : super(key: key);

  @override
  State<WebViewScreen> createState() => _WebViewScreenState();
}

class _WebViewScreenState extends State<WebViewScreen> {
  InAppWebViewController? _webViewController;
  double _progress = 0;
  String _currentUrl = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title ?? 'WebView'),
        actions: [
          IconButton(
            icon: const Icon(Icons.refresh),
            onPressed: () => _webViewController?.reload(),
          ),
          IconButton(
            icon: const Icon(Icons.arrow_back),
            onPressed: () async {
              if (await _webViewController?.canGoBack() ?? false) {
                _webViewController?.goBack();
              }
            },
          ),
          IconButton(
            icon: const Icon(Icons.arrow_forward),
            onPressed: () async {
              if (await _webViewController?.canGoForward() ?? false) {
                _webViewController?.goForward();
              }
            },
          ),
        ],
      ),
      body: Column(
        children: [
          if (_progress < 1.0)
            LinearProgressIndicator(value: _progress),
          Expanded(
            child: InAppWebView(
              initialUrlRequest: URLRequest(url: WebUri(widget.url)),
              initialSettings: InAppWebViewSettings(
                useShouldOverrideUrlLoading: true,
                mediaPlaybackRequiresUserGesture: false,
                allowsInlineMediaPlayback: true,
                iframeAllow: 'camera; microphone',
                iframeAllowFullscreen: true,
              ),
              onWebViewCreated: (controller) {
                _webViewController = controller;
              },
              onLoadStart: (controller, url) {
                setState(() {
                  _currentUrl = url.toString();
                });
              },
              onLoadStop: (controller, url) async {
                setState(() {
                  _currentUrl = url.toString();
                });
              },
              onProgressChanged: (controller, progress) {
                setState(() {
                  _progress = progress / 100;
                });
              },
              shouldOverrideUrlLoading: (controller, navigationAction) async {
                final url = navigationAction.request.url;

                // Handle specific URLs
                if (url != null && url.scheme == 'tel') {
                  // Open phone dialer
                  return NavigationActionPolicy.CANCEL;
                }

                return NavigationActionPolicy.ALLOW;
              },
              onConsoleMessage: (controller, consoleMessage) {
                print('Console: ${consoleMessage.message}');
              },
            ),
          ),
        ],
      ),
    );
  }
}

2. JavaScript Bridge

dart
// lib/services/webview/javascript_handler.dart
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

class JavaScriptHandler {
  final InAppWebViewController controller;

  JavaScriptHandler(this.controller);

  // Register JavaScript handlers
  void registerHandlers() {
    // Handler: Send message from Flutter to JS
    controller.addJavaScriptHandler(
      handlerName: 'flutterHandler',
      callback: (args) {
        // Handle message from JavaScript
        print('Message from JS: $args');
        
        // Return response to JavaScript
        return {'status': 'success', 'message': 'Received in Flutter'};
      },
    );

    // Handler: Get user info
    controller.addJavaScriptHandler(
      handlerName: 'getUserInfo',
      callback: (args) async {
        // Get user info from Flutter
        final user = await AuthService.getCurrentUser();
        return {
          'userId': user.id,
          'name': user.name,
          'email': user.email,
        };
      },
    );

    // Handler: Make API call
    controller.addJavaScriptHandler(
      handlerName: 'apiCall',
      callback: (args) async {
        final endpoint = args[0] as String;
        final method = args[1] as String;
        final body = args.length > 2 ? args[2] : null;

        try {
          final response = await ApiService.request(
            endpoint: endpoint,
            method: method,
            body: body,
          );
          return {'success': true, 'data': response};
        } catch (e) {
          return {'success': false, 'error': e.toString()};
        }
      },
    );

    // Handler: Navigation
    controller.addJavaScriptHandler(
      handlerName: 'navigate',
      callback: (args) {
        final route = args[0] as String;
        
        // Navigate in Flutter app
        Navigator.pushNamed(context, route);
        
        return {'status': 'navigated'};
      },
    );
  }

  // Call JavaScript function from Flutter
  Future<dynamic> callJavaScriptFunction(
    String functionName,
    List<dynamic> args,
  ) async {
    try {
      final result = await controller.evaluateJavascript(
        source: '$functionName(${args.map((arg) => jsonEncode(arg)).join(',')})',
      );
      return result;
    } catch (e) {
      print('Error calling JS function: $e');
      return null;
    }
  }

  // Inject JavaScript code
  Future<void> injectJavaScript(String code) async {
    await controller.evaluateJavascript(source: code);
  }

  // Load JavaScript file
  Future<void> loadJavaScriptFile(String path) async {
    final String jsCode = await rootBundle.loadString(path);
    await injectJavaScript(jsCode);
  }
}

3. WebView Service

dart
// lib/services/webview/webview_service.dart
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

class WebViewService {
  // Open URL in WebView
  static Future<void> openUrl(
    BuildContext context,
    String url, {
    String? title,
  }) async {
    await Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => WebViewScreen(url: url, title: title),
      ),
    );
  }

  // Open HTML content
  static Future<void> openHtmlContent(
    BuildContext context,
    String htmlContent, {
    String? title,
  }) async {
    await Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => HtmlWebViewScreen(
          htmlContent: htmlContent,
          title: title,
        ),
      ),
    );
  }

  // Clear cache
  static Future<void> clearCache() async {
    await InAppWebViewController.clearAllCache();
  }

  // Clear cookies
  static Future<void> clearCookies() async {
    await CookieManager.instance().deleteAllCookies();
  }

  // Get cookies for URL
  static Future<List<Cookie>> getCookies(String url) async {
    return await CookieManager.instance().getCookies(url: WebUri(url));
  }

  // Set cookie
  static Future<void> setCookie({
    required String url,
    required String name,
    required String value,
    String? domain,
    String? path,
    int? expiresDate,
    int? maxAge,
    bool? isSecure,
    bool? isHttpOnly,
  }) async {
    await CookieManager.instance().setCookie(
      url: WebUri(url),
      name: name,
      value: value,
      domain: domain,
      path: path,
      expiresDate: expiresDate,
      maxAge: maxAge,
      isSecure: isSecure,
      isHttpOnly: isHttpOnly,
    );
  }
}

4. HTML Content WebView

dart
// HTML content screen
class HtmlWebViewScreen extends StatefulWidget {
  final String htmlContent;
  final String? title;

  const HtmlWebViewScreen({
    Key? key,
    required this.htmlContent,
    this.title,
  }) : super(key: key);

  @override
  State<HtmlWebViewScreen> createState() => _HtmlWebViewScreenState();
}

class _HtmlWebViewScreenState extends State<HtmlWebViewScreen> {
  InAppWebViewController? _controller;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget.title ?? 'Content')),
      body: InAppWebView(
        initialData: InAppWebViewInitialData(
          data: widget.htmlContent,
          baseUrl: WebUri('https://localhost'),
          encoding: 'utf-8',
          mimeType: 'text/html',
        ),
        initialSettings: InAppWebViewSettings(
          transparentBackground: true,
        ),
        onWebViewCreated: (controller) {
          _controller = controller;
        },
        onLoadStop: (controller, url) async {
          // Inject CSS for styling
          await controller.evaluateJavascript(source: '''
            document.body.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto';
            document.body.style.padding = '16px';
            document.body.style.fontSize = '16px';
          ''');
        },
      ),
    );
  }
}

5. JavaScript Bridge HTML

html
<!-- assets/web/bridge.js -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flutter WebView Bridge</title>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
            padding: 20px;
            margin: 0;
        }
        button {
            padding: 12px 24px;
            margin: 8px;
            font-size: 16px;
            border-radius: 8px;
            border: none;
            background: #007AFF;
            color: white;
            cursor: pointer;
        }
        button:active {
            opacity: 0.8;
        }
        #result {
            margin-top: 20px;
            padding: 16px;
            background: #f5f5f5;
            border-radius: 8px;
        }
    </style>
</head>
<body>
    <h1>Flutter ↔ JavaScript Bridge</h1>
    
    <button onclick="sendToFlutter()">Send Message to Flutter</button>
    <button onclick="getUserInfo()">Get User Info</button>
    <button onclick="makeApiCall()">Make API Call</button>
    <button onclick="navigateInFlutter()">Navigate in Flutter</button>
    
    <div id="result"></div>

    <script>
        // Check if running in Flutter WebView
        const isFlutter = window.flutter_inappwebview !== undefined;

        function showResult(message) {
            document.getElementById('result').innerHTML = 
                '<strong>Result:</strong><br>' + JSON.stringify(message, null, 2);
        }

        // Send message to Flutter
        async function sendToFlutter() {
            if (!isFlutter) {
                showResult('Not running in Flutter');
                return;
            }

            try {
                const result = await window.flutter_inappwebview.callHandler(
                    'flutterHandler',
                    'Hello from JavaScript!',
                    { timestamp: Date.now() }
                );
                showResult(result);
            } catch (error) {
                showResult('Error: ' + error);
            }
        }

        // Get user info from Flutter
        async function getUserInfo() {
            if (!isFlutter) {
                showResult('Not running in Flutter');
                return;
            }

            try {
                const user = await window.flutter_inappwebview.callHandler('getUserInfo');
                showResult(user);
            } catch (error) {
                showResult('Error: ' + error);
            }
        }

        // Make API call through Flutter
        async function makeApiCall() {
            if (!isFlutter) {
                showResult('Not running in Flutter');
                return;
            }

            try {
                const result = await window.flutter_inappwebview.callHandler(
                    'apiCall',
                    '/api/data',
                    'GET'
                );
                showResult(result);
            } catch (error) {
                showResult('Error: ' + error);
            }
        }

        // Navigate in Flutter app
        async function navigateInFlutter() {
            if (!isFlutter) {
                showResult('Not running in Flutter');
                return;
            }

            try {
                const result = await window.flutter_inappwebview.callHandler(
                    'navigate',
                    '/profile'
                );
                showResult(result);
            } catch (error) {
                showResult('Error: ' + error);
            }
        }

        // Listen for messages from Flutter
        window.addEventListener('flutterMessage', (event) => {
            showResult('Message from Flutter: ' + event.detail);
        });

        // Notify Flutter when page is ready
        window.addEventListener('load', () => {
            if (isFlutter) {
                window.flutter_inappwebview.callHandler('pageReady', {
                    width: window.innerWidth,
                    height: window.innerHeight
                });
            }
        });
    </script>
</body>
</html>

6. Advanced WebView Controller

dart
// lib/services/webview/webview_controller_manager.dart
class WebViewControllerManager {
  final InAppWebViewController controller;

  WebViewControllerManager(this.controller);

  // Take screenshot
  Future<Uint8List?> takeScreenshot() async {
    return await controller.takeScreenshot();
  }

  // Print page
  Future<void> printPage() async {
    await controller.printCurrentPage();
  }

  // Find text in page
  Future<void> findText(String text) async {
    await controller.findAllAsync(find: text);
  }

  // Zoom
  Future<void> zoomIn() async {
    final currentZoom = await controller.getZoomScale();
    await controller.setZoomScale(zoomScale: currentZoom + 0.1);
  }

  Future<void> zoomOut() async {
    final currentZoom = await controller.getZoomScale();
    await controller.setZoomScale(zoomScale: currentZoom - 0.1);
  }

  // Get page content
  Future<String?> getPageContent() async {
    return await controller.getHtml();
  }

  // Get page title
  Future<String?> getPageTitle() async {
    return await controller.getTitle();
  }

  // Execute JavaScript and get result
  Future<dynamic> executeScript(String script) async {
    return await controller.evaluateJavascript(source: script);
  }
}

🎯 Mejores Prácticas

1. Security

DO: Sanitize user input

dart
// Never inject unsanitized user input
final sanitized = HtmlEscape().convert(userInput);
await controller.evaluateJavascript(source: 'showMessage("$sanitized")');

2. Error Handling

DO: Handle WebView errors

dart
onReceivedError: (controller, request, error) {
  print('WebView error: ${error.description}');
  // Show error page
},

3. Memory Management

DO: Dispose WebView properly

dart
@override
void dispose() {
  _webViewController?.dispose();
  super.dispose();
}

🚨 Troubleshooting

JavaScript Not Working

dart
// Enable JavaScript
initialSettings: InAppWebViewSettings(
  javaScriptEnabled: true,
),

CORS Issues

dart
// For development, disable web security (not for production!)
initialSettings: InAppWebViewSettings(
  allowUniversalAccessFromFileURLs: true,
),

SSL Certificate Errors

dart
onReceivedServerTrustAuthRequest: (controller, challenge) async {
  // In production, validate certificate properly
  return ServerTrustAuthResponse(
    action: ServerTrustAuthResponseAction.PROCEED,
  );
},

📚 Recursos


Versión: 1.0.0
Última actualización: Diciembre 2025
Total líneas: 1,100+

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results