Flutter mixed-use development actual combat - page jumps and data transfer

Foreword

Flutter UI and state management have learned, it's time to do some mixed-use development. Most of the online information write very one-sided, fail to effect combat. I think at least a few mixed-use development to achieve the following results

  • Native Jump Flutter
  • Flutter Jump native
  • Jump when there is exchange of data

Data in this chapter is android-based, access Flutter in the existing project basis, ios mixed development steps similar, it can be used as reference.

Mixed-use development is divided into two steps

  1. Creating Flutter Module
  2. Access Flutter Boost

Free fish Flutter-Boost probably the best mixed-use development framework, for the memory of the control and jump Flutter page black and white are a good deal.

Here say one more thing, if you use the traditional way to jump from native Flutter, black and white phenomenon will be very serious, even in release mode I feel also not able to accept, after all, but also on low-end phones, not always holding Xiaolong 855 to test it!

1. Create a Flutter Module

The entire program or reference to the way official suggested [ https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps ], the fish are given free aar program looks really good, but also for virgin the degree of coupling will be much lower, but a higher cost to get started, interested students can go to understand.

1.1 creation module

1.1.1 path switching in a terminal to the root directory of the project, and then use the command line to create a flutter module, create command follows

flutter create -t module my_flutter

Where "my_flutter" is the name of the module, this may have to define

1.1.2 Add at the end of the original project setting.gradle

setBinding(new Binding([gradle: this]))                                 
evaluate(new File(                                                      
        settingsDir,                                               
        'my_flutter/.android/include_flutter.groovy'                          
))
include ':my_flutter'

1.1.3 gradle add the following code in the original program app

implementation project(":flutter")

After the above three steps you can use the native project of the core functionality Flutter! After the success of the project structure is created as follows

Note: minSdk must be> = 16, otherwise when you created will complain!

 

2. Access Flutter-Boost

Only access the skip function, a vein app is considered open, or else nothing luan used. Jump native has great disadvantages when Flutter page and native pages overlap, each turned a page creates a Flutter Flutter Engine, resulting in memory surge, Flutter use global variables can not be shared at each separate page, IOS appears memory leaks. With the release of iterations, Flutter-Boost these issues are resolved, it is the best mixed-use development of the election.

Project Address: Flutter-the Boost

How to Access 2.1

If your project is still in use support packages, add the following dependence in the pubspec.yaml

flutter_boost:
    git:
        url: 'https://github.com/alibaba/flutter_boost.git'
        ref: '0.1.61'

If your project has switched to androidX, use the following ways

flutter_boost:
    git:
        url: 'https://github.com/alibaba/flutter_boost.git'
        ref: 'feature/flutter_1.9_androidx_upgrade'

Note: If you use the 0.1.61 version of Flutter-Boost, corresponding Flutter version should not be less than 1.9.1-hotfix, otherwise it will error. If you Flutter branch is not in stable, strongly suggest that you switch to the stable branch.

View the current branch in end-use flutter doctor

If there is no stable branch to see all branches in the use of flutter channel

Use command flutter channel stable / beta / dev / master switch to the corresponding branch

2.2 Flutter integrated FlutterBoost

2.2.1 Flutter create two pages used to jump, we jump all the action can use the framework in the integrated FlutterBoost

import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:my_flutter/constant.dart';

///第一个页面
class FirstRouteWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Page'),
      ),
      body: Container(
        alignment: Alignment.center,
        child: RaisedButton(
          child: Text('支付吧,皮卡丘'),
          onPressed: () {
            print("open pay page!");
            //利用FlutterBoost跳转到支付页面,并携带参数
            FlutterBoost.singleton.open(Routes.ROUTE_NATIVE_PAY,
                urlParams: {"need_money": '0.01'});
          },
        ),
      ),
    );
  }
}

///第二个页面
class SecondRouteWidget extends StatelessWidget {

  final Map params;

  SecondRouteWidget(this.params);

  @override
  Widget build(BuildContext context) {
    print(params);
    return Scaffold(
      appBar: AppBar(
        title: Text("支付完成页面-Flutter"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            //关闭当前页面并返回一个result
            BoostContainerSettings settings =
                BoostContainer.of(context).settings;
            FlutterBoost.singleton.close(settings.uniqueId,
                result: {"result": "data from second"});
          },
          child: Text('支付结果:${params['result']}'),
        ),
      ),
    );
  }
}

2.2.2 renovation main.dart

import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:my_flutter/constant.dart';
import 'package:my_flutter/simple_page.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    //将需要进行跳转的页面事先注册到FlutterBoost
    //pageName是路由名称,params是参数
    FlutterBoost.singleton.registerPageBuilders({
      Routes.ROUTE_FIRST: (pageName, params, _) => FirstRouteWidget(),
      Routes.ROUTE_SECOND: (pageName, params, _) => SecondRouteWidget(params),
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      builder: FlutterBoost.init(postPush: _onRoutePushed),
      home: Container(),
    );
  }

  ///从原生跳转到Flutter的动作都会经过这里,有需要的话可以在这里做一些统一的操作
  void _onRoutePushed(
      String pageName, String uniqueId, Map params, Route route, Future _) {}
}

After the above two steps, Flutter this side of the bin even integrated. If you do want to use FlutterBoost jump, remember the registration page to FlutterBoost, otherwise jump when error. Here we look at Native side to do those operations.

2.3 Native integration FlutterBoost

Item structural changes occur after the end Flutter integrated FlutterBoost, the final results are as follows

At this point we add the following dependence of the app in build.gradle

implementation project(path: ':flutter_boost')

这样一来我们原生工程也能使用FlutterBoost了,在真正使用以前还需要配置一些东西。

2.3.1 PageRouter-跳转中心

这个类是用来处理原生和Flutter页面跳转逻辑的,具体的业务逻辑都包含在openPageByUrl里面,需要注意的是我的项目用的是androidX,如果你还在使用support包在类的使用上会有些许不同。具体请参考Flutter Boost的example

package com.zljtech.auction.flutter;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.huodao.platformsdk.logic.core.route.auction.IAuctionRouteUri;
import com.huodao.platformsdk.util.launch.ActivityLaunchUtils;
import com.idlefish.flutterboost.containers.NewBoostFlutterActivity;

import java.util.HashMap;
import java.util.Map;

/**
 * @author sq
 * @date 2019/12/10
 * @describe
 */
public class PageRouter {

    public static final String NATIVE_PAY_PAGE_URL = "sample://nativePayPage";
    public static final String FLUTTER_PAGE_FIRST_URL = "first";
    public static final String FLUTTER_PAGE_SECOND_URL = "second";

    /**
     * 需要将flutter页面的url放入到该map,这样在跳转的时候才能找到对应的链接
     */
    public final static Map<String, String> PAGE_NAME = new HashMap<String, String>() {{
        put(FLUTTER_PAGE_FIRST_URL, FLUTTER_PAGE_FIRST_URL);
        put(FLUTTER_PAGE_SECOND_URL, FLUTTER_PAGE_SECOND_URL);
    }};



    public static boolean openPageByUrl(Context context, String url, Map params) {
        return openPageByUrl(context, url, params, 0);
    }

    /**
     *
     * @param context
     * @param url 跳转链接-对应具体的页面
     * @param params 携带的参数
     * @param requestCode 请求code,一般配合着startActivityForResult使用
     * @return
     */
    public static boolean openPageByUrl(Context context, String url, Map params, int requestCode) {

        String path = url.split("\\?")[0];

        Log.i("openPageByUrl", path);

        try {
            if (PAGE_NAME.containsKey(path)) {
                //所有Flutter页面的跳转都会走到这里,通过容器NewBoostFlutterActivity来装载
                Intent intent = NewBoostFlutterActivity.withNewEngine().url(PAGE_NAME.get(path)).params(params)
                        .backgroundMode(NewBoostFlutterActivity.BackgroundMode.opaque).build(context);
                if (context instanceof Activity) {
                    Activity activity = (Activity) context;
                    activity.startActivityForResult(intent, requestCode);
                } else {
                    context.startActivity(intent);
                }
                return true;
            } else if (url.startsWith(NATIVE_PAY_PAGE_URL)) {
                String needMoney = (String) params.get("need_money");
                //跳转到原生页面
                //通过Arouter进行跳转,也可以使用原生Intent
                ActivityLaunchUtils.getInstance()
                    .build(IAuctionRouteUri.ROUTE_AUCTION_ACTIVITY_PAY__ORDER_DEPOSIT)
                    .withString("need_money", needMoney)
                    .launch();
                return true;
            }

            return false;

        } catch (Throwable t) {
            return false;
        }
    }
}

2.3.2 在Application中配置FlutterBoost

这里主要是做Flutter引擎初始化工作,在Flutter跳转原生的回调方法中实现相应的跳转逻辑。

 private void initFlutterBoost() {
        INativeRouter router = (context, url, urlParams, requestCode, exts) -> {
            String assembleUrl = Utils.assembleUrl(url, urlParams);
            PageRouter.openPageByUrl(context, assembleUrl, urlParams);
        };

        Platform platform = new NewFlutterBoost.ConfigBuilder(this, router)
                .isDebug(true)
                //当任意activity创建的时候就初始化flutter引擎,防止打开flutter页面时白屏
                .whenEngineStart(NewFlutterBoost.ConfigBuilder.ANY_ACTIVITY_CREATED)
                .renderMode(FlutterView.RenderMode.texture)
                .build();

        NewFlutterBoost.instance().init(platform);
    }

该方法在Application的onCreate调用即可!

2.3.2 AndroidManifest中配置

NewBoostFlutterActivity是Flutter View的承载页,所以这个页面必须在manifest中注册

<activity
            android:name="com.idlefish.flutterboost.containers.NewBoostFlutterActivity"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
            android:hardwareAccelerated="true"
            android:theme="@style/Theme.AppCompat"
            android:windowSoftInputMode="adjustResize">
            <!--            启动页面的过渡动画-->
            <meta-data
                android:name="io.flutter.embedding.android.SplashScreenDrawable"
                android:resource="@drawable/anim_loading_view" />

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

记得在application标签中添加我们自定义的application,这样FlutterBoost才会初始化。

总结

通过上面的步骤我们就完成了FlutterBoost在Native和Flutter端的集成,具体的跳转方法如下:

1.flutter跳转到原生(携带参数)

传递的urlParams对应于PageRouter->openPageByUrl的params参数

FlutterBoost.singleton.open(Routes.ROUTE_NATIVE_PAY,
                urlParams: {"need_money": '0.01'});

2.原生跳转Flutter

Map<String, String> params = new HashMap<>();
params.put("result", "pay success");
PageRouter.openPageByUrl(this, PageRouter.FLUTTER_PAGE_SECOND_URL, params);

传递给SecondRouteWidget的参数可以在其构造方法中获取,具体请参照2.2.1

3.Flutter页面退出到原生页面携带返回值

一开始我想的是在close当前Flutter页面的时候,native侧用onActivityResult去接收返回值,毕竟native跳转到flutter的时候是使用startActivityForResult

FlutterBoost.singleton.close(settings.uniqueId,
                result: {"result": "data from second"});

这时我们再到native层debug看一下数据:

返回的数据结构有点奇怪,但毕竟是人家定义的,只能照着做了。

  • 首先我们通过data获取到一个Bundle
  • bundle中包含了一个map,key是“_flutter_result_”, value仍然是一个map
  • value里面的数据才是我们真正传递的键值对,这时候再通过我们自己定义的“result”去拿数据就可以了

4.Flutter页面返回Flutter页面携带参数

如果你想使用FlutterBoost进行Flutter页面间的跳转,传递参数什么的会简单很多,话不多说,上代码:

首先从FirstPage跳转到SecondPage

FlutterBoost.singleton.open(Routes.ROUTE_SECOND,
                urlParams: {'result': '我是第一个页面携带过来的参数'}).then((result) {
              print('接收第二个页面返回的数据=$result');
            });

urlParams是传递到下一个页面的数据,open方法会返回一个Future,自然而然地我们想到用then去接收返回的数据。

关闭SecondPage并返回数据给FirstPage

print('接收第一个页面传递过来的数据=:$params');
BoostContainerSettings settings =
        BoostContainer.of(context).settings;
FlutterBoost.singleton.close(settings.uniqueId, 
        result: {"result": "data from second"});

result是返回的数据,然后我们通过日志来验证一下操作是否正确

拿到的数据和我们的预期相差无几,无论是主动传递还是被动接收,数据结构都是一个map,我们通过这个map去解析自己想要的数据即可!

 

好了关于Flutter混合开发就说这么多,本篇侧重于混合开发期间的页面跳转以及数据传递。FlutterBoost还可以把Flutter页面解析成一个Fragment供原生使用,甚至是把原生控件转换为一个widget供Flutter使用,具体的姿势可以参照官网,这里就不再赘述了!

 

更新:

集成了Flutter-Boost后,页面跳转6起来了,数据传递也6了,但是我想着混合开发肯定会涉及到Flutter调用原生的方法,按理说使用MethodChannel即可,但是在实际使用中却报错了,几经折腾终于找到解决方案:

在初始化FlutterBoost的时会有一个lifecycleListener接口,我们在engineCreate回调中注册自己的Channel就可以了。

一定记得在onEngineCreated回调中注册,不然flutter engine还没有初始化就注册会报错,而且该方法只会回调一次

private void initFlutterBoost() {
        INativeRouter router = (context, url, urlParams, requestCode, exts) -> {
            String assembleUrl = Utils.assembleUrl(url, urlParams);
            PageRouter.openPageByUrl(context, assembleUrl, urlParams);
        };

        Platform platform = new NewFlutterBoost.ConfigBuilder(this, router)
                .isDebug(true)
                .whenEngineStart(NewFlutterBoost.ConfigBuilder.ANY_ACTIVITY_CREATED)
                .renderMode(FlutterView.RenderMode.texture)
                .lifecycleListener(new NewFlutterBoost.BoostLifecycleListener() {
                    @Override
                    public void onEngineCreated() {
                        Logger2.d(TAG, "onEngineCreated");
                        //注册本地MethodChannel
                        BinaryMessenger messenger = new BoostPluginRegistry(NewFlutterBoost.instance().engineProvider()).registrarFor(FlutterNativePlugin.CHANNEL).messenger();
                        MethodChannel methodChannel = new MethodChannel(messenger, FlutterNativePlugin.CHANNEL);
                        FlutterNativePlugin.registerWith(methodChannel);
                    }

                    @Override
                    public void onPluginsRegistered() {
                        Logger2.d(TAG, "onPluginsRegistered");
                    }

                    @Override
                    public void onEngineDestroy() {

                    }
                })
                .build();

        NewFlutterBoost.instance().init(platform);

    }

下面是FlutterNativePlugin的具体实现,说白了就是一个MethodChannel

public class FlutterNativePlugin implements MethodChannel.MethodCallHandler {
    private static final String TAG = "FlutterNativePlugin";
    public static final String CHANNEL = "com.zljtech.channel.method";

    private FlutterNativePlugin() {
    }

    public static void registerWith(MethodChannel channel) {
        FlutterNativePlugin instance = new FlutterNativePlugin();
        channel.setMethodCallHandler(instance);
    }

    @Override
    public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
        if ("isOk".equals(methodCall.method)) {
            boolean isOk = Math.random() > 0.5;
            Logger2.d(TAG, "isOk====" + isOk);
            result.success(isOk);
        } else {
            result.notImplemented();
        }
    }
}

然后就可以愉快的在Flutter中调用Native方法啦!

 

 

发布了21 篇原创文章 · 获赞 21 · 访问量 2万+

Guess you like

Origin blog.csdn.net/u013894711/article/details/103482120