新建Flutter工程的几种方式
1.Flutter Application
Application的目标是最终产出一个apk或者ipa包
->android:Android平台相关代码(其实就是一个完整的android项目)
->ios:iOS平台相关代码(也是一个完整的IOS项目)
->lib:flutter相关代码(lib包下的代码文件最终会被渲染到android和ios两个平台)
->pubspec.yaml: 整个Flutter项目的配置文件,配置FlutterSDK,图片、字体、插件等
2.Flutter Plugin
plugin是产出一个抽象层的组件,会包含原生代码
3.Flutter Package
package是产出一个dart组件
4.Flutter Module
Module最终是产出一个library
->.android:Android的宿主工程
->.ios:iOS的宿主工程
->lib:flutter相关代码
->pubspec.yaml:配置文件
因为宿主工程的存在,这个flutter工程是可以独立运行的,
通过安装了flutter与dart插件的Androidstudio 打开这个flutter_module项目,并且是可以运行的
理解flutter package
纯Dart插件工程,仅包含Dart层的实现,往往定义一些公共Widget和工具组件
1.在pubspec.yaml 添加描述信息
name:组件名字
description: 组件描述
version: 版本号(如:0.0.1)
author:作者-邮箱
homepage:github地址
2.README.md 添加使用说明 (非必要操作LICENSE摘抄)
3.CHANGELOG.md 添加版本变更记录
4.发布插件 flutter package pub publish --dry-run(预检查)
flutter package pub publish(发布命令,第一次发布可能需要账号)
理解flutter plugin(介绍AndroidLog案例)
<table>
<tr>
<td bgcolor="black">
<font color="white">
当你在开发flutter应用的时候,有时会需要调用native的api,往往遇到flutter并没有相应的package
这时候flutter plugin就开始发挥作用了
</font>
</td>
</tr>
</table>
```
1.lib文件夹添加 FlutterNativeLogPlugin.dart文件
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
enum Log { DEBUG, WARNING, ERROR }
class FlutterNativeLogPlugin {
static const MethodChannel _channel =
const MethodChannel('flutter_native_log_plugin');
static Future<String> printLog({Log logType, String tag, String msg}) async {
String log = "debug";
if (logType == Log.WARNING) {
log = "warning";
} else if (logType == Log.ERROR) {
log = "error";
} else {
log = "debug";
}
final Map<String, dynamic> params = <String, dynamic>{
'tag': tag,
'msg': msg,
'logType': log
};
//如果params为null 则用 params ?? {}
final String result = await _channel.invokeMethod('printLog', params);
return result;
}
}
2.Android端Module需要依赖Flutter的方式 在该Module的build.gradle中添加
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found, Define location with flutter.sdk in the local.properties file.")
}
//找到该脚本 然后可以依赖上flutter sdk
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
flutter {
source '../..'
}
3.在该Android的module中 新增FlutterNativePlugin.java
package com.xxx.xxx;
import android.util.Log;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
//FlutterNativeLogPlugin需要实现flutter中的MethodCallHandler类
public class FlutterNativeLogPlugin implements MethodCallHandler {
//注册插件
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(),"flutter_native_log_plugin");
channel.setMethodCallHandler(new FlutterNativeLogPlugin());
}
@Override
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("printLog")) {
String msg = call.argument("msg");
String tag = call.argument("tag");
String logType = call.argument("logType");
if (logType.equals("warning")) {
Log.w(tag, msg);
} else if (logType.equals("error")) {
Log.e(tag, msg);
} else {
Log.d(tag, msg);
}
result.success("Logged Successfully!");
} else {
result.notImplemented();
}
}
}
4.注册插件
在GeneratedPluginRegistant.registerWith(this); (注册pubspec里面的插件)下面
//注意registerFor方法
FlutterNativeLogPlugin.registerWith(registerFor("包名.FlutterNativeLogPlugin"))
```
Android工程与Flutter Module融合
1.新建一个Android Project(目录地址../flutter/projects/AndroidBindingFlutter)
2.新建一个Flutter Module(目录地址../flutter/projects/flutter_module)
3.在Android工程的setting.gradle配置关联flutter_module信息
//主module名是‘app’,如果当前项目主module名不是‘app’,那就找不到目录,无法将相应产物打进apk中
//改成 setBinding(new Binding([gradle: this, mainModuleName: '主module名']))即可
setBinding(new Binding([gradle: this]))
//File文件路径表示flutter_module的绝对路径
evaluate(new File(settingsDir.parentFile, 'flutter_module/.android/include_flutter.groovy'))
4.app的build.gradle添加配置信息
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implemention project(':flutter')
}
Android与Flutter混合开发
1.原生页面<font color=#ff4800>跳转</font>Flutter页面
**现在清单文件注册FlutterActivity**
<manifest>
<application>
<activity
android:name="io.flutter.embedding.android.FlutterActivitu
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection| fontScale|screenLayout|density|uiMode"
android:hardworeAccelerated="true"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize"/>
</application>
</manifest>
**启动方式 FutterActivity默认路由名称为"/",默认打开main.dart的main方法**
//使用的默认路由"/"
startActivity(
FlutterActivity.createDefaultIntent(this)
);
//FutterActivity路由名称为“route”,创建一个新的FlutterEngine对象
startActivity(
FlutterActivity
.withNewEngine()
.initialRoute("route1")
.build(this)
);
//FutterActivity 使用缓存的引擎对象
startActivity(
FlutterActivity
.withCachedEngine("engine_id")
.build(this)
);
**flutter接收路由**
//在runApp里面获取路由
void main() => runApp(_widgetForRoute(window.defaultRouteName));
Widget _widgetForRoute(String Route) {
根据不同Route返回对应的Widget
}
2.原生页面<font color=#ff4800>嵌套</font>Flutter页面
<table>
<tr>
<td bgcolor="#cccccc">
<font color="white">
新版本的FlutterSDK已经没有Flutter类
不再支持诸如Flutter.createView()、Flutter.createFragment()等用法
</font>
</td>
</tr>
</table>
**FlutterView 案例**
public class DemoFlutterViewActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.demo_flutter_view_layout);
FrameLayout frameLayout=findViewById(R.id.flutter_view_container);
ProgressBar progress=findViewById(R.id.progress);
//创建FlutterView
FlutterView flutterView = new FlutterView(this);
//创建FlutterView首帧渲染完成监听
flutterView.addOnFirstFrameRenderedListener(new FlutterUiDisplayListener() {
@Override
public void onFlutterUiDisplayed() {
//显示FlutterView
frameLayout.setVisibility(View.VISIBLE);
}
@Override
public void onFlutterUiNoLongerDisplayed() {
//隐藏进度条
progress.setVisibility(View.GONE);
}
});
//将FlutterView添加到布局中
ViewGroup.LayoutParams layoutParams=new LinearLayout.LayoutParam(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
frameLayout.addView(flutterView, layoutParams);
//关键代码 将Flutter页面显示到FlutterView中
//一个engine只能加载一次flutter界面
//一个flutteractivity需要一个flutterengine来加载flutter界面,
//可以指定activity初始化flutterengine的模式:使用缓存或新建
flutterView.attachToFlutterEngine(需要创建一个FlutterEngine);
}
}
//布局文件 demo_flutter_view_layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/flutter_view_container"
android:visibility="invisible"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ProgressBar
android:id="@+id/progress"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:visibility="visible"/>
</RelativeLayout>
**FlutterFragment 案例**
public class DemoFlutterFragmentActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.demo_flutter_fragment_layout);
FlutterFragment fragment = FlutterFragment.withNewEngine().initialRoute("home").build();
getSupportFragmentManager()
.beginTransaction()
.add(R.id.flutter_fragment_container, fragment)
.commit();
}
}
//布局文件 demo_flutter_fragment_layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/flutter_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
3.Flutter页面<font color=#ff4800>跳转</font>原生页面
案例:使用plugin的方式
//别忘记注册插件
//FlutterPluginJumpToAct.registerWith(registrarFor(FlutterPluginNavigator.CHANNEL));
public class FlutterPluginNavigator implements MethodChannel.MethodCallHandler {
public static String CHANNEL = "com.flutter.plugin/activity";
static MethodChannel channel;
private Activity activity;
private FlutterPluginNavigator(Activity activity) {
this.activity = activity;
}
public static void registerWith(PluginRegistry.Registrar registrar) {
channel = new MethodChannel(registrar.messenger(), CHANNEL);
FlutterPluginNavigator instance = new FlutterPluginNavigator(registrar.activity());
//setMethodCallHandler在此通道上接收方法调用的回调
channel.setMethodCallHandler(instance);
}
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
if (call.method.equals("定义的方法标识")) {
//跳转到指定Activity
Intent intent = new Intent(activity, XXXActivity.class);
activity.startActivity(intent);
//返回给flutter的参数
result.success("success");
} else {
result.notImplemented();
}
}
}
4.Flutter的路由与导航
管理多个页面时有两个核心概念和类:Route和 Navigator
Flutter中通过定义Route, 使用Navigator来跳转界面(通过route入栈和出栈来实现页面之间的跳转)
一个route是一个屏幕或页面的抽象、Navigator是管理route的Widget
>导航到新页面
**显示跳转**
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
//指定跳转的页面
return new Demo1();
},));
**隐式跳转**
需要先定义,
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
body: ...,
),
//定义路由
routes: <String,WidgetBuilder>{
"/xxx": (BuildContext context)=>new XXXPage(),
},
);
后使用
Navigator.of(context).pushNamed("/xxx");
**打开路由映射页面,并销毁当前页**
Navigator.of(context).pushReplacementNamed('/路由标识');
>移除页面
Navigator.pop(...)
**表示当前页面是否可以退出**
Navigator.of(context).canPop()返回一个bool值
**一种很友善的退出方式,如果能退出就退出**
Navigator.of(context).maybePop() 《等价于 maybePop() => canPop() == true?pop() : do nothing》
**点击退出当前页面,并将 路由映射的页面 压入栈中**
Navigator.of(context).popAndPushNamed('/路由标识');
**一直退出直到某一个页面 在路由页面上的Page都退出**
Navigator.of(context).popUntil(ModalRoute.withName('/路由标识'));
**案例**
例如我们的跳转顺序是Screen1—>Screen2—>Screen3—>Screen4
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (context) => Screen4()),
ModalRoute.withName('/screen1'));
表示跳转到screen4,并且移除所有的页面直到screen1
newRoute和参数表示将要加入栈中的页面,predicate参数表示栈中要保留的页面底线
Flutter与Native通信
1.MethodChannel:用于传递方法调用
实现flutter与原生 双向通信
[图片上传失败...(image-898c07-1614331541410)]
Flutter端
**定义MethodChannel**
static const MethodChannel methodChannel = MethodChannel("包名/test");
**异步方法调用methodChannel中的invokeMethod方法,指定要调用的原生中的方法**
Future<void> _getActivityResult() async {
//结果值
String result;
try {
final int level = await methodChannel.invokeMethod('方法标志');
result = 'success msg';
} on PlatformException {
result = 'error msg';
}
setState(() {
_result = result;
});
}
Android端
**定义MethodChannel**
public static String CHANNEL = "包名/test";
**创建 MethodChannel 并通过 setMethodCallHandler 方法来区分 Flutter 的不同调用方法名和返回对应的回调**
//绑定对应的FlutterView
MethodChannel channel = new MethodChannel((FlutterView)flutterView, CHANNEL)
.setMethodCallHandler(new MethodChannel.MethodCallHandler(){
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
if(methodCall.equals("方法标志")){
//回调给flutter的参数
result.success("success");
}
}
});
//Android端也可以调用Flutter端方法
//channel.invoke(String method, Object arguments, Result callback)
//Flutter端 methodChannel.setMethodCallHandler(Future<dynamic> handler(MethodCall call)) { ... }
2.EventChannel:用于数据流信息通信
描述:native到flutter的单向调用(支持一对多调用)
案例:把电池状态变化由Native"推送"给Flutter
public class FlutterEventChannel implements EventChannel.StreamHandler {
private static final String EVENT_CHANNEL_NAME = 包名/event名称";
private FlutterEventChannel(FlutterView flutterView) {
EventChannel eventChannel = new EventChannel(flutterView, EVENT_CHANNEL_NAME);
eventChannel.setStreamHandler(this);
}
//创建EventChannel 把FlutterView传进来
public static FlutterEventChannel create(FlutterView flutterView) {
return new FlutterEventChannel(flutterView);
}
private EventChannel.EventSink eventSink;
//onListen则代表通道已经建好,Native可以发送数据了
@Override
public void onListen(Object o, EventChannel.EventSink eventSink) {
this.eventSink = eventSink;
}
//onCancel代表对面不再接收 应该做一些clean up的事情
@Override
public void onCancel(Object o) {
eventSink = null;
}
}
private BroadcastReceiver createChargingStateChangeReceiver(final EventSink events) {
return new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
if (status == BatteryManager.BATTERY_STATUS_UNKNOWN) {
events.error("UNAVAILABLE", "Charging status unavailable", null);
} else {
boolean isCharging =
status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
// 把电池状态发给Flutter
// 在Native这里转化为约定好的字符串
events.success(isCharging ? "charging" : "discharging");
}
}
};
}
//Flutter端
static const EventChannel eventChannel = const EventChannel('com.meetyou.flutter/event');
@override
void initState() {
super.initState();
//接受native推送的消息
eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
}
//events.success(isCharging ? "charging" : "discharging");的消息
void _onEvent(Object event) {
setState(() {
_chargingStatus = "Battery status: ${event == 'charging' ? '' : 'dis'}charging.";
});
}
//events.error("UNAVAILABLE", "Charging status unavailable", null);的消息
void _onError(Object error) {
setState(() {
_chargingStatus = 'Battery status: unknown.';
});
}
3.BasicMessageChannel:用于传递字符串和半结构化的信息
实现 Flutter 与 原生(Android 、iOS)双向通信
**Flutter端**
//创建 BasicMessageChannel
// flutter_and_native_100 为通信标识
// StandardMessageCodec() 为参数传递的 编码方式
static const messageChannel = const BasicMessageChannel('flutter_and_native_100', StandardMessageCodec());
//发送消息
Future<Map> sendMessage(Map arguments) async {
Map reply = await messageChannel.send(arguments);
//解析 原生发给 Flutter 的参数
int code = reply["code"];
String message = reply["message"];
//更新 Flutter 中页面显示
setState(() {
recive = "code:$code message:$message";
});
return reply;
}
sendMessage({"method": "test", "ontent": "flutter 中的数据", "code": 100});
**Android端**
private BasicMessageChannel<Object> mMessageChannel;
private void messageChannelFunction() {
//消息接收监听
//BasicMessageChannel (主要是传递字符串和一些半结构体的数据)
//创建通道
mMessageChannel = new BasicMessageChannel<Object>(getFlutterView(), "flutter_and_native_100", StandardMessageCodec.INSTANCE);
// 接收消息监听
mMessageChannel.setMessageHandler(new BasicMessageChannel.MessageHandler<Object>() {
@Override
public void onMessage(Object o, BasicMessageChannel.Reply<Object> reply) {
Map<Object, Object> arguments = (Map<Object, Object>) o;
//方法名标识
String lMethod = (String) arguments.get("method");
//测试 reply.reply()方法 发消息给Flutter
if (lMethod.equals("test")) {
Toast.makeText(mContext, "flutter 调用到了 android test", Toast.LENGTH_SHORT).show();
//回调Flutter
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("message", "reply.reply 返回给flutter的数据");
resultMap.put("code", 200);
//回调 此方法只能使用一次
reply.reply(resultMap);
}
}
});
}
Android中相关Flutter类理解
1.分析FlutterApplication
public class FlutterApplication extends Application {
public void onCreate() {
super.onCreate();
//1. 检查方法必须在主线程中执行,否则抛异常
//2. 初始化配置,通过从manifest XML文件获取Flutter配置值来初始化这些值
//3. 初始化资源
//4. 加载flutter.so动态库
FlutterMain.startInitialization(this);
}
}
2.分析FlutterActivity(android/io/flutter/embedding/android/)
//android/io/flutter/app/FlutterActivity.java已经废弃
public class FlutterActivity extends Activity
implements FlutterActivityAndFragmentDelegate.Host, LifecycleOwner {
//主要职责
//同步Activity生命周期(Lifecycle)
//选择Flutter的初始化路由
//提供子类钩子,提供和配置FlutterEngine
}
3.分析FlutterFragment
Flutter UI的一种方式
4.分析FlutterView
FlutterView的作用是将Flutter UI通过对应的FlutterEngine绘制后,显示在Android设备上
5.分析FlutterEngine
FlutterEngine是一个独立的Flutter运行环境,是Dart代码运行在Android应用的容器
可以通过FlutterEngineCache来管理FlutterEngine
FlutterEngineCache
.getInstance()
.apply {
put(engineId, FlutterEngineXXX)
put(engineId, FlutterEngineXXX)
}
FlutterEngineCache的onDestroy
需要手动释放 并且清空缓存
创建FlutterUI的时候需要重新创建