flutter特别篇:flutter嵌入h5页面
Posted LCQ_CG
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了flutter特别篇:flutter嵌入h5页面相关的知识,希望对你有一定的参考价值。
博客学习
学习的博客地址
- 主要是通过webViewScaffold来进行页面的渲染
- 属性有:
- url,页面跳转的url
- appBar,页面的头表
- initialChild:还没有加载出来的时候显示的页面
- hidden:如果设置为true,将会出现默认的“转圈圈图标”
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
/// WebView使用flutter_webview_plugin插件
/// https://pub.dev/packages/flutter_webview_plugin
// 此dart在真机上可以正常运行,但是虚拟机没法联网就不能运行,为什么不能联网这个问题还没有解决
class EmbeddH5 extends StatefulWidget
String title = "百度";
String url = "https://www.baidu.com";
@override
_EmbeddH5State createState() =>_EmbeddH5State(title: title, url: url);
class _EmbeddH5State extends State<EmbeddH5>
String url;
String title;
_EmbeddH5State(required this.url, required this.title);
@override
Widget build(BuildContext context)
return MaterialApp(
///隐藏Debug标志
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Container(
child: WebviewScaffold(
url: url,
appBar: AppBar(
title: Text(title),
),
//当WebView没加载出来前显示
initialChild: Container(
color: Colors.white,
child: Center(
child: Text("正在加载中...."),
),
),
),
)),
);
调用该怎么调用呢?下面这样再main.dart中调用就行
WidgetsFlutterBinding.ensureInitialized();
runApp(EmbeddH5('百度','https://www.baidu.com'));
Flutter中所有的运转都是在各种Binding中调度的,也正是这些绑定器的存在彻底解耦了Widget、 Element 、RenderObject 对 Platform端的依赖
官方学习
- webView加载出来的页面在最顶部,没有其他的flutter widget能覆盖
官方代码如下:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
const kandroidUserAgent =
'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/62.0.3202.94 Mobile Safari/537.36';
String selectedUrl = 'https://flutter.io';
// ignore: prefer_collection_literals
final Set<javascriptChannel> jsChannels = [
JavascriptChannel(
name: 'Print',
onMessageReceived: (JavascriptMessage message)
print(message.message);
),
].toSet();
class EmbeddH5OfficeDemo extends StatelessWidget
final flutterWebViewPlugin = FlutterWebviewPlugin();
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Flutter WebView Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routes:
'/': (_) => const MyHomePage(title: 'Flutter WebView Demo'),
'/widget': (_)
return WebviewScaffold(
url: selectedUrl,
javascriptChannels: jsChannels,
mediaPlaybackRequiresUserGesture: false,
appBar: AppBar(
title: const Text('Widget WebView'),
),
withZoom: true,
withLocalStorage: true,
hidden: true,
initialChild: Container(
color: Colors.redAccent,
child: const Center(
child: Text('Waiting.....'),
),
),
bottomNavigationBar: BottomAppBar(
child: Row(
children: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: ()
flutterWebViewPlugin.goBack();
,
),
IconButton(
icon: const Icon(Icons.arrow_forward_ios),
onPressed: ()
flutterWebViewPlugin.goForward();
,
),
IconButton(
icon: const Icon(Icons.autorenew),
onPressed: ()
flutterWebViewPlugin.reload();
,
),
],
),
),
);
,
,
);
class MyHomePage extends StatefulWidget
const MyHomePage(Key? key, required this.title) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
class _MyHomePageState extends State<MyHomePage>
// Instance of WebView plugin
final flutterWebViewPlugin = FlutterWebviewPlugin();
// On destroy stream
late StreamSubscription _onDestroy;
// On urlChanged stream
late StreamSubscription<String> _onUrlChanged;
// On urlChanged stream
late StreamSubscription<WebViewStateChanged> _onStateChanged;
late StreamSubscription<WebViewHttpError> _onHttpError;
late StreamSubscription<double> _onProgressChanged;
late StreamSubscription<double> _onScrollYChanged;
late StreamSubscription<double> _onScrollXChanged;
final _urlCtrl = TextEditingController(text: selectedUrl);
final _codeCtrl = TextEditingController(text: 'window.navigator.userAgent');
final _scaffoldKey = GlobalKey<ScaffoldState>();
final _history = [];
@override
void initState()
super.initState();
flutterWebViewPlugin.close();
_urlCtrl.addListener(()
selectedUrl = _urlCtrl.text;
);
// Add a listener to on destroy WebView, so you can make came actions.
_onDestroy = flutterWebViewPlugin.onDestroy.listen((_)
if (mounted)
// Actions like show a info toast.
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(content: Text('Webview Destroyed')));
);
// Add a listener to on url changed
_onUrlChanged = flutterWebViewPlugin.onUrlChanged.listen((String url)
if (mounted)
setState(()
_history.add('onUrlChanged: $url');
);
);
_onProgressChanged =
flutterWebViewPlugin.onProgressChanged.listen((double progress)
if (mounted)
setState(()
_history.add('onProgressChanged: $progress');
);
);
_onScrollYChanged =
flutterWebViewPlugin.onScrollYChanged.listen((double y)
if (mounted)
setState(()
_history.add('Scroll in Y Direction: $y');
);
);
_onScrollXChanged =
flutterWebViewPlugin.onScrollXChanged.listen((double x)
if (mounted)
setState(()
_history.add('Scroll in X Direction: $x');
);
);
_onStateChanged =
flutterWebViewPlugin.onStateChanged.listen((WebViewStateChanged state)
if (mounted)
setState(()
_history.add('onStateChanged: $state.type $state.url');
);
);
_onHttpError =
flutterWebViewPlugin.onHttpError.listen((WebViewHttpError error)
if (mounted)
setState(()
_history.add('onHttpError: $error.code $error.url');
);
);
@override
void dispose()
// Every listener should be canceled, the same should be done with this stream.
_onDestroy.cancel();
_onUrlChanged.cancel();
_onStateChanged.cancel();
_onHttpError.cancel();
_onProgressChanged.cancel();
_onScrollXChanged.cancel();
_onScrollYChanged.cancel();
flutterWebViewPlugin.dispose();
super.dispose();
@override
Widget build(BuildContext context)
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(24.0),
child: TextField(controller: _urlCtrl),
),
ElevatedButton(
onPressed: ()
flutterWebViewPlugin.launch(
selectedUrl,
rect: Rect.fromLTWH(
0.0, 0.0, MediaQuery.of(context).size.width, 300.0),
userAgent: kAndroidUserAgent,
invalidUrlRegex:
r'^(https).+(twitter)', // prevent redirecting to twitter when user click on its icon in flutter website
);
,
child: const Text('Open Webview (rect)'),
),
ElevatedButton(
onPressed: ()
flutterWebViewPlugin.launch(selectedUrl, hidden: true);
,
child: const Text('Open "hidden" Webview'),
),
ElevatedButton(
onPressed: ()
flutterWebViewPlugin.launch(selectedUrl);
,
child: const Text('Open Fullscreen Webview'),
),
ElevatedButton(
onPressed: ()
Navigator.of(context).pushNamed('/widget');
,
child: const Text('Open widget webview'),
),
Container(
padding: const EdgeInsets.all(24.0),
child: TextField(controller: _codeCtrl),
),
ElevatedButton(
onPressed: ()
final future =
flutterWebViewPlugin.evalJavascript(_codeCtrl.text);
future.then((String? result)
setState(()
_history.add('eval: $result');
);
);
,
child: const Text('Eval some javascript'),
),
ElevatedButton(
onPressed: ()
final future = flutterWebViewPlugin
.evalJavascript('alert("Hello World");');
future.then((String? result)
setState(()
_history.add('eval: $result');
);
);
,
child: const Text('Eval javascript alert()'),
),
ElevatedButton(
onPressed: ()
setState(()
_history.clear();
);
flutterWebViewPlugin.close();
,
child: const Text('Close'),
),
ElevatedButton(
onPressed: ()
flutterWebViewPlugin.getCookies().then((m)
setState(()
_history.add('cookies: $m');
);
);
,
child: const Text('Cookies'),
),
Text(_history.join('\\n'))
],
),
),
);
flutterWebViewPlugin.launch(url)可以实现H5页面的加载,也就是说既可以通过WebViewScaffold中的url来实现页面的加载,也可以通过button响应式这样的加载
launnch里的属性
Future<Null> launch(String url,
Map<String, String> headers: null,
Set<JavascriptChannel> javascriptChannels: null,
bool withJavascript: true,
bool clearCache: false,
bool clearCookies: false,
bool hidden: false,
bool enableAppScheme: true,
Rect rect: null,
String userAgent: null,
bool withZoom: false,
bool displayZoomControls: FlutterFlutter 混合开发 ( 关联 Android 工程与 Flutter 工程 | 安卓页面中嵌入 Flutter 页面 | 安卓中启动 Flutter 页面 )
文章目录
前言
在上一篇博客 【Flutter】Flutter 混合开发 ( 简介 | Flutter 混合开发集成步骤 | 创建 Flutter Module ) 中 , 创建了 Flutter Module 工程 ;
本篇博客开始创建 Android 工程 , 并将两个工程进行关联 ;
Flutter 混合开发集成步骤 :
- ① 在 Android Studio 中创建 Flutter Module ;
- ② 为 Native 应用添加 Flutter Module 依赖 ;
- ③ 在 Native 应用 ( Android / iOS 应用 ) 中 , 调用 Flutter Module 模块 ;
- ④ 编写 Flutter Module 中的 Dart 代码 ;
- ⑤ 运行 Flutter 混合应用 ;
- ⑥ 项目的 热重启 / 重新加载 ;
- ⑦ 调试 Dart 代码 ;
- ⑧ 应用发布 ;
一、创建 Android 项目
在 Android Studio 中 , 在菜单栏中 , 选择 " File -> New -> New Project … " 选项 ;
选择创建 " Empty Activity " ;
这里要特别注意路径的设置 ,
Flutter Module 工程的路径是 : D:\\002_Project\\002_Android_Learn\\flutter_hybrid\\flutter_module
Android 工程的路径是 : D:\\002_Project\\002_Android_Learn\\flutter_hybrid\\flutter_native
上面两个工程的路径都在 D:\\002_Project\\002_Android_Learn\\flutter_hybrid 目录下 ;
Android 应用创建完成 :
Android 工程 与 Flutter Module 工程 , 都在同一个目录中 ;
二、关联 Android 工程与 Flutter Module 工程
Android 工程的路径 与 Flutter Module 工程路径 , 否符合如下要求 :
- Flutter Module 工程的路径是 : D:\\002_Project\\002_Android_Learn\\flutter_hybrid\\flutter_module
- Android 工程的路径是 : D:\\002_Project\\002_Android_Learn\\flutter_hybrid\\flutter_native
- 上面两个工程的路径都在 D:\\002_Project\\002_Android_Learn\\flutter_hybrid 目录下 ;
1、配置 Flutter Module工程
在 Android 工程的 settings.gradle 进行如下配置 : 这样配置后 , Flutter 工程就会被编译成一个 Android Library Module , 名称是 flutter ;
rootProject.name = "flutter_native"
include ':app'
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'flutter_module/.android/include_flutter.groovy'
))
include ':flutter_module'
project(':flutter_module').projectDir = new File('../flutter_module')
2、配置 build.gradle
在 Android Module 下的 build.gradle 中 :
① 配置最低支持版本 minSdkVersion 16+ , 因为 Flutter 最低支持版本是 16 ;
// Flutter 最低支持版本是 16
minSdkVersion 18
② 添加工程依赖 :
// 在 settings.gradle 中配置的脚本 , 会自动关联到 Flutter 模块
implementation project(':flutter')
完整的配置文件如下 :
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.example.flutter_native"
// Flutter 最低支持版本是 16
minSdkVersion 18
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
// 在 settings.gradle 中配置的脚本 , 会自动关联到 Flutter 模块
implementation project(':flutter')
}
3、配置 AndroidManifest.xml
将 io.flutter.embedding.android.FlutterActivity 配置到 AndroidManifest.xml 清单文件中 ;
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize" />
完整配置文件如下 :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.flutter_native">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Flutter_native">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize" />
</application>
</manifest>
三、Activity 中嵌入 FlutterFragment 页面
在 Activity 中 , 将 Flutter 页面作为 Fragment , 嵌入到 Activity 中 ;
findViewById(R.id.flutter1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction();
// 创建 FlutterFragment
fragmentTransaction.replace(R.id.frame, FlutterFragment.createDefault());
fragmentTransaction.commit();
}
});
执行结果 :
四、Activity 中启动 FlutterActivity 页面
将 Flutter 页面当做一个新的 Activity 启动 ;
findViewById(R.id.flutter2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = FlutterActivity
.withNewEngine()
.initialRoute("Android 启动 FlutterActivity")
.build(MainActivity.this);
//intent.putExtra("initParams", "Android 中 Activity 启动 Flutter");
startActivity(intent);
}
});
执行结果 : 点击 方式二 按钮 , 弹出 FlutterActivity ;
五、完整代码示例
这里只贴出主界面完整代码 , 具体的配置参数 , 查看 GitHub 或者 CSDN 源码快照 ;
1、Android 主界面代码示例
package com.example.flutter_native;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentTransaction;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.android.FlutterFragment;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.flutter1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction();
// 创建 FlutterFragment
fragmentTransaction.replace(R.id.frame, FlutterFragment.createDefault());
fragmentTransaction.commit();
}
});
findViewById(R.id.flutter2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = FlutterActivity
.withNewEngine()
.initialRoute("Android 启动 FlutterActivity")
.build(MainActivity.this);
//intent.putExtra("initParams", "Android 中 Activity 启动 Flutter");
startActivity(intent);
}
});
}
}
2、Flutter 完整代码示例
import 'package:flutter/material.dart';
import 'dart:ui';
void main() => runApp(
/// 该构造方法中传入从 Android 中传递来的参数
MyApp(initParams: window.defaultRouteName,)
);
class MyApp extends StatelessWidget {
/// 这是从 Android 中传递来的参数
final String initParams;
/// 构造方法 , 获取从 Android 中传递来的参数
const MyApp({Key? key, required this.initParams}):super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: initParams),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
四、相关资源
参考资料 :
- Flutter 官网 : https://flutter.dev/
- Flutter 插件下载地址 : https://pub.dev/packages
- Flutter 开发文档 : https://flutter.cn/docs ( 强烈推荐 )
- 官方 GitHub 地址 : https://github.com/flutter
- Flutter 中文社区 : https://flutter.cn/
- Flutter 实用教程 : https://flutter.cn/docs/cookbook
- Flutter CodeLab : https://codelabs.flutter-io.cn/
- Dart 中文文档 : https://dart.cn/
- Dart 开发者官网 : https://api.dart.dev/
- Flutter 中文网 : https://flutterchina.club/ , http://flutter.axuer.com/docs/
- Flutter 相关问题 : https://flutterchina.club/faq/ ( 入门阶段推荐看一遍 )
- GitHub 上的 Flutter 开源示例 : https://download.csdn.net/download/han1202012/15989510
- Flutter 实战电子书 : https://book.flutterchina.club/chapter1/
- Dart 语言练习网站 : https://dartpad.dartlang.org/
重要的专题 :
- Flutter 动画参考文档 : https://flutterchina.club/animations/
博客源码下载 :
-
GitHub 地址 : ( 随博客进度一直更新 , 有可能没有本博客的源码 )
- Flutter Module 工程 : https://github.com/han1202012/flutter_module
- Android 应用 : https://github.com/han1202012/flutter_native
- 注意 : 上面两个工程要放在同一个目录中 , 否则编译不通过 ;
-
博客源码快照 : https://download.csdn.net/download/han1202012/21670919 ( 本篇博客的源码快照 , 可以找到本博客的源码 )
以上是关于flutter特别篇:flutter嵌入h5页面的主要内容,如果未能解决你的问题,请参考以下文章
FlutterFlutter 混合开发 ( 安卓端向 Flutter 传递数据 | FlutterFragment 数据传递 | FlutterActivity 数据传递 )
FlutterFlutter 混合开发 ( 关联 Android 工程与 Flutter 工程 | 安卓页面中嵌入 Flutter 页面 | 安卓中启动 Flutter 页面 )