1. 概要
今回はタイトル通りにPlatformviewとWebViewを使ってNative(Android)側のViewをFlutterに描画して表示する内容です。
対象としては開発を1年程やってて自分で最初から開発してみたい方になります。そのため細かい用語などの説明はしません。
2. FlutterとNativeの連携方法
こちらを参考
3. プロジェクトの準備
3-1. プロジェクトを作成
4. ソースコード(Kotlin)
4-1. android/app/src/main/kotlin/com/example/platformview_web/FlutterWebView.kt
package com.example.platformview_web
import android.content.Context
import android.view.View
import android.webkit.WebView
import android.webkit.WebViewClient
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.platform.PlatformView
class FlutterWebView(context: Context?, messenger: BinaryMessenger, id: Int, creationParams: Map<String?, Any?>?) : PlatformView, MethodChannel.MethodCallHandler {
private val webView : WebView = WebView(context!!)
override fun getView(): View {
return webView
}
override fun dispose() {}
init {
webView.webViewClient = WebViewClient()
webView.apply {
settings.apply {
javaScriptEnabled = true
}
}
val initialUrl = creationParams?.get("initialUrl") as String
initialUrl.let {
webView.loadUrl(it)
}
MethodChannel(messenger, "samples.flutter.dev/webview").also {
it.setMethodCallHandler(this)
}
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when(call.method) {
"loadUrl" -> {
val arguments = call.arguments as Map<String?, String?>
val url = arguments["url"] ?: ""
loadUrl(url)
result.success(null)
}
"goBack" -> {
goBack()
result.success(null)
}
"goForward" -> {
goForward()
result.success(null)
}
else -> result.notImplemented()
}
}
private fun loadUrl(url: String) {
webView.loadUrl(url)
}
private fun goBack() {
webView.goBack()
}
private fun goForward() {
webView.goForward()
}
}
- PlatformView (flutter.dev)
- MethodChannel.MethodCallHandler (flutter.dev)
- 「samples.flutter.dev/webview」
- Flutter側との通路
The client and host sides of a channel are connected through a channel name passed in the channel constructor. All channel names used in a single app must be unique; prefix the channel name with a unique ‘domain prefix’, for example:
https://docs.flutter.dev/development/platform-integration/platform-channels?tab=type-mappings-kotlin-tabsamples.flutter.dev/battery
.
4-2. android/app/src/main/kotlin/com/example/platformview_web/FlutterWebViewFactory.kt
package com.example.platformview_web
import android.content.Context
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.StandardMessageCodec
import io.flutter.plugin.platform.PlatformView
import io.flutter.plugin.platform.PlatformViewFactory
class FlutterWebViewFactory(private val messenger: BinaryMessenger) : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context?, id: Int, args: Any?): PlatformView {
val creationParams = args as Map<String?, Any?>?
return FlutterWebView(context, messenger, id, creationParams)
}
}
4-3. android/app/src/main/kotlin/com/example/platformview_web/PlatformviewWebPlugin.kt
package com.example.platformview_web
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
class PlatformviewWebPlugin: FlutterPlugin {
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
flutterPluginBinding.platformViewRegistry
.registerViewFactory("samples.flutter.dev/webview/plugin", FlutterWebViewFactory(flutterPluginBinding.binaryMessenger))
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {}
}
- FlutterPlugin
- PlatformViewRegistry (flutter.dev)
- 「samples.flutter.dev/webview/plugin」
- Platform Viewタイプの識別子
- 「samples.flutter.dev/webview/plugin」
4-4. android/app/src/main/kotlin/com/example/platformview_web/MainActivity.kt
- このファイルはFlutterプロジェクトを作成すると、自動生成されるので内容を変更
package com.example.platformview_web
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
flutterEngine.plugins.add(PlatformviewWebPlugin())
}
}
5. ソースコード(Dart)
5-1. lib/main.dart
import 'package:flutter/material.dart';
import 'platform_webview_controller.dart';
import 'platform_webview_widget.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final controller = PlatformWebviewController();
String initialUrl = 'https://www.google.com';
@override
void initState() {
super.initState();
}
void goForward() {
controller.goForward();
}
void goBack() {
controller.goBack();
}
void onSubmittedUrl(String url) {
controller.loadUrl(url);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Platformview example app'),
),
body: Center(
child: Column(
children: [
TextField(
controller: TextEditingController(text: initialUrl),
onSubmitted: onSubmittedUrl,
maxLines: 1,
decoration: const InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 8.0),
border: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.transparent)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.transparent)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.blue))),
),
Expanded(
child: PlatformWebviewWidget(
initialUrl: initialUrl,
)),
Row(
children: [
IconButton(
onPressed: goBack, icon: const Icon(Icons.arrow_back)),
IconButton(
onPressed: goForward,
icon: const Icon(Icons.arrow_forward)),
],
),
],
),
),
),
);
}
}
5-2. lib/platform_webview_controller.dart
import 'package:flutter/services.dart';
class PlatformWebviewController {
final MethodChannel _channel =
const MethodChannel('samples.flutter.dev/webview');
Future<void> loadUrl(String url) async {
return await _channel.invokeMethod('loadUrl', {'url': url});
}
Future<void> goBack() async {
return await _channel.invokeMethod('goBack');
}
Future<void> goForward() async {
return await _channel.invokeMethod('goForward');
}
}
Native側のメソッドを呼び出す。
- 「MethodChannel」
- samples.flutter.dev/webview
- Native側との通路
- 「loadUrl」「goBack」「goForward」
- Native側に用意されているメソッド名
5-3. lib/platform_webview_widget.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class PlatformWebviewWidget extends StatelessWidget {
final String initialUrl;
// ignore: use_key_in_widget_constructors
const PlatformWebviewWidget({required this.initialUrl});
@override
Widget build(BuildContext context) {
const viewType = 'samples.flutter.dev/webview/plugin';
final creationParams = <String, dynamic>{
'initialUrl': initialUrl,
};
return AndroidView(
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}
}
- AndroidView constructor – AndroidView class – widgets library – Dart API (flutter.dev)
- 「samples.flutter.dev/webview/plugin」
- 上記Kotlinにて作成したPlatform Viewタイプの識別子
- 「samples.flutter.dev/webview/plugin」
6. 結果




7. 参考
- Writing custom platform-specific code | Flutter
- 現場で使える Flutter開発入門 | マイナビブックス (mynavi.jp)
- 6-4. PlatformViewの実装
投稿者プロフィール

-
開発好きなシステムエンジニアです。
卓球にハマってます。
最新の投稿
【Next.js】2025年2月9日【NextJS】View and Download PDF
【AWS】2025年2月1日【AWS】Github ActionsやAWS SAMを使ってAWS S3・CloudFrontにウェブコンテンツをデプロイし、サブドメインにアクセスできるようにする
【AWS】2025年1月25日【AWS】Deploy Serverless NextJS app with AWS Lambda Web Adapter using AWS SAM
【Next.js】2025年1月16日【NextJS】Access nextjs app launched on WSL2 from an external terminal