Flutter插件分享

新建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案例)







当你在开发flutter应用的时候,有时会需要调用native的api,往往遇到flutter并没有相应的package


这时候flutter plugin就开始发挥作用了



    ```
    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 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 params = {
                '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.原生页面跳转Flutter页面

**现在清单文件注册FlutterActivity**

    
        
    

**启动方式 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.原生页面嵌套Flutter页面







新版本的FlutterSDK已经没有Flutter类


不再支持诸如Flutter.createView()、Flutter.createFragment()等用法

**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


    
    


**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


    

3.Flutter页面跳转原生页面

案例:使用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: {
                "/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 _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 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 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 mMessageChannel;

private void messageChannelFunction() {
    //消息接收监听
    //BasicMessageChannel (主要是传递字符串和一些半结构体的数据)
    //创建通道
    mMessageChannel = new BasicMessageChannel(getFlutterView(), "flutter_and_native_100", StandardMessageCodec.INSTANCE);
    // 接收消息监听
    mMessageChannel.setMessageHandler(new BasicMessageChannel.MessageHandler() {
        @Override
        public void onMessage(Object o, BasicMessageChannel.Reply reply) {
            
            Map arguments = (Map) 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 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的时候需要重新创建

你可能感兴趣的:(Flutter插件分享)