Flutter——踩坑之旅(现有Android项目和现有iOS项目引入同一个Flutter项目)

本文详细介绍如何将Flutter模块集成到现有的Android和iOS项目中,包括升级AndroidStudio、配置Gradle、解决常见错误、实现热更新及原生与Flutter页面间跳转。同时,提供了实用的工具类和代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

程序猿日常

flutter填坑——现有Android项目和现有iOS项目引入同一个Flutter项目

一.现有Android项目 引入 Flutter 项目(源码Module方法引入)

项目目录
在这里插入图片描述
flutter_module是AndLangBase(Android现有项目)和iOSLangBase(iOS现有项目)引用的同一个flutter module

1.Android Stdio升级至4.0.1版本

2.现有项目升级AndroidX

	distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
	
	classpath 'com.android.tools.build:gradle:3.5.3'
	//butterknife版本升级
	classpath 'com.jakewharton:butterknife-gradle-plugin:9.0.0-rc2'
	
	使用自动升级AndroidX功能 Refactor->Migrate to AndroidX

3.创建Flutter Module(flutter_module)

参考链接[https://flutter.cn/docs/development/add-to-app](https://flutter.cn/docs/development/add-to-app)

4.跳转Flutter页面报错

Add-to-app mode got NullPointerException when use BackgroundMode.transparent 

解决:
不使用backgroundMode,注视掉下面代码,跳转flutter页面成功
.backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)

5.Flutter继续热更新开发

在flutter module项目的目录下执行命令flutter attach,之后运行app项目,成功之后如下图所示,更新flutter代码时,只有在命令行输入R,就可以实现热更新
在这里插入图片描述

6.原生跳转Flutter页面

两种方式:
1.FlutterEngineCache 在application里提前注册

public static final String FLUTTER_ENGINE_ID="AndLangFlutter";
public static void initFlutter(Context context){
    // Instantiate a FlutterEngine.
    FlutterEngine flutterEngine = new FlutterEngine(context);
    // Configure an initial route.
    flutterEngine.getNavigationChannel().setInitialRoute("/");
    // Start executing Dart code to pre-warm the FlutterEngine.
    flutterEngine.getDartExecutor().executeDartEntrypoint(
            DartExecutor.DartEntrypoint.createDefault()
    );
    // Cache the FlutterEngine to be used by FlutterActivity or FlutterFragment.
    FlutterEngineCache
            .getInstance()
            .put(FLUTTER_ENGINE_ID, flutterEngine);
}

之后调用方法(注:只能跳转至setInitialRoute的路由地址,无法修改路由)

   activity.startActivity(
                FlutterActivity
                        .withCachedEngine(FLUTTER_ENGINE_ID)
                        .build(activity)
    	);

2.withNewEngine 跳转

	activity.startActivity(
                FlutterActivity
                        .withNewEngine()
                        .initialRoute(route)
                        .build(activity)
        );

工具类FlutterUtil

import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.android.FlutterActivityLaunchConfigs;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterEngineCache;
import io.flutter.embedding.engine.dart.DartExecutor;

public class FlutterUtil {
 	public static final String FLUTTER_ENGINE_ID="AndLangFlutter";
	public static void initFlutter(Context context){
    // Instantiate a FlutterEngine.
    FlutterEngine flutterEngine = new FlutterEngine(context);
    // Configure an initial route.
    flutterEngine.getNavigationChannel().setInitialRoute("/");
    // Start executing Dart code to pre-warm the FlutterEngine.
    flutterEngine.getDartExecutor().executeDartEntrypoint(
            DartExecutor.DartEntrypoint.createDefault()
    );
    // Cache the FlutterEngine to be used by FlutterActivity or FlutterFragment.
    FlutterEngineCache
            .getInstance()
            .put(FLUTTER_ENGINE_ID, flutterEngine);
	}
	public static void startFlutterActivity(Activity activity,String route){
        if(!BBCUtil.isEmpty(route)){
            activity.startActivity(
                    new FlutterActivity.NewEngineIntentBuilder(FlutterSelfActivity.class)
                            .initialRoute(route)
                            .build(activity)
            );
        }else {
            activity.startActivity(
                    FlutterSelfActivity
                            .withCachedEngine(FLUTTER_ENGINE_ID)
                            .build(activity)
            );
        }
    }
}

FlutterSelfActivity 是继承FlutterActivity的用于flutter与Android进行交互
需要在AndroidManifest.xml中注册FlutterSelfActivity

FlutterSelfActivity这里采用的kotlin 代码如下:

import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.example.test.andlang.util.LogUtil
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
import lombok.NonNull

class FlutterSelfActivity : FlutterActivity() {
    private val CHANNEL = "com.lang.shop"
    var methodChannel_callFlutter: MethodChannel? = null

@SuppressLint("NewApi")
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    methodChannel_callFlutter = MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, CHANNEL)
    setcallHandler()
}

override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
    GeneratedPluginRegistrant.registerWith(flutterEngine);
}

//flutter 调用 原生方法
fun setcallHandler() {
    methodChannel_callFlutter!!.setMethodCallHandler(){
        call, result ->
        LogUtil.d("0.0","flutter 调用原生方法:"+call.method+",参数:"+call.arguments)
        if ("goBack" == call.method){
            finish()
        }else{
            result.notImplemented()
        }
    }
}

}

二.现有iOS项目 引入 Flutter 项目(源码Module方法引入)

1.引入之前Android生成的Flutter Module

Podfile 中添加下面代码:

flutter_application_path = '../my_flutter'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')

每个需要集成 Flutter 的 [Podfile target][],执行 install_all_flutter_pods(flutter_application_path)

target 'MyApp' do
	install_all_flutter_pods(flutter_application_path)
end

运行 pod install
这时候可能会报错

[!] No podspec found for `Flutter` in `../flutter_module/.ios/Flutter/engine`

解决办法:
在AndroidStdio中把Flutter Module选择iOS设备运行一次,再pod install 即可解决。

2.踩坑

运行报错: Could not parse callback cache, aborting restore

AppDelegate : FlutterAppDelegate

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions

return true;
改为:
return [super application:application didFinishLaunchingWithOptions:launchOptions];

3.原生跳转Flutter页面 同Android

工具类FlutterUtil

#import "AppDelegate.h"
#import "FlutterUtil.h"
#import <Flutter/Flutter.h>
#import "FlutterVC.h"
#import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h>
static NSString *FLUTTER_ENGINE_ID=@"iOSLangFlutter";
@implementation FlutterUtil
//获取url中的key的值
+(void)initFlutter{
    ((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine= [[FlutterEngine alloc] initWithName:FLUTTER_ENGINE_ID];
    FlutterEngine *flutterEngine =
    ((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine;
    // Runs the default Dart entrypoint with a default Flutter route.
    [[flutterEngine navigationChannel] invokeMethod:@"setInitialRoute"arguments:@"/"];
    [flutterEngine run];
    // Used to connect plugins (only if you have plugins with iOS platform code).
     [GeneratedPluginRegistrant registerWithRegistry:flutterEngine];
}

+(void)pushFlutterVC:(UINavigationController *)vc withRoute:(NSString *)route{
    FlutterVC *flutterViewController =nil;
    if(!IsStrEmpty(route)){
        //设置flutter 路由
//        flutterViewController=[[FlutterVC alloc]init];
//        [flutterViewController setInitialRoute:route];
//        flutterViewController.splashScreenView=[[UIView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
//        [GeneratedPluginRegistrant registerWithRegistry:[flutterViewController pluginRegistry]];
    
    FlutterEngine *flutterEngine =
    ((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine;
    flutterViewController = [[FlutterVC alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];
    [flutterViewController pushRoute:route];
    
}else{
    FlutterEngine *flutterEngine =
    ((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine;
    flutterViewController = [[FlutterVC alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];
}
//导航控制器入栈的方式切换页面
 [vc pushViewController:flutterViewController animated:YES];
//模态切换的方式切换页面
//[vc presentViewController:flutterViewController animated:YES completion:nil];
}
@end

FlutterVC 是继承FlutterViewController的用于flutter与iOS进行交互
代码如下:

#import "FlutterVC.h"
@interface FlutterVC ()

@end
@implementation FlutterVC
- (instancetype)init{
    
    [self initMethodChannel];
    return [super init];
}
- (instancetype)initWithEngine:(FlutterEngine*)engine
                       nibName:(nullable NSString*)nibName
                        bundle:(nullable NSBundle*)nibBundle{
    [self initMethodChannel];
    return [super initWithEngine:engine nibName:nibName bundle:nibBundle];
}
-(void)initMethodChannel{
	    FlutterEngine *flutterEngine =
	    ((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine;
	    
	    FlutterMethodChannel *messageChannel = [FlutterMethodChannel methodChannelWithName:@"com.lang.shop" binaryMessenger:flutterEngine.binaryMessenger];
	    
	    //创建flutter 回调iOS的channel对象
	    [messageChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
	        // call.method 获取 flutter 给回到的方法名,要匹配到 channelName 对应的多个 发送方法名,一般需要判断区分
	        // call.arguments 获取到 flutter 给到的参数,(比如跳转到另一个页面所需要参数)
	        // result 是给flutter的回调, 该回调只能使用一次
	        NSLog(@"flutter 调用原生方法 method=%@ \narguments = %@", call.method, call.arguments);
        
        // method和WKWebView里面JS交互很像
        if ([call.method isEqualToString:@"goBack"]) {
            [self.navigationController popViewControllerAnimated:YES];
        }else{
            //调用了未知方法,则报告该方法
            result(FlutterMethodNotImplemented);
        }
    
	}];
}
@end

三.看下flutter_module的代码

1.main.dart文件部分代码

import 'package:flutter/material.dart';
import 'dart:ui';
import 'package:flutter_module/common/LogUtil.dart';
import 'package:flutter_module/common/RouteUtil.dart';
void main() {
  LogUtil.showLog("原生传递的参数:"+window.defaultRouteName);
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or press Run > Flutter Hot Reload in a Flutter IDE). Notice that the
        // counter didn't reset back to zero; the application is not restarted.
        primarySwatch: Colors.blue,
      ),
      initialRoute:"/",
      routes:RouteUtil.initRouteMap(context),
      home:initRoute(window.defaultRouteName),
    );
  }
}

方法initRoute中处理接收到Native的参数,返回对应的Widget

2.Flutter页面退出返回到Native页面

static void exitFlutter(BuildContext context){
    if(Platform.isAndroid) {
      // SystemChannels.platform.invokeMethod('SystemNavigator.pop');
    }else {
      // exit(0);
      // 创建一个给native的channel (类似iOS的通知)
    }
    nativeFun('goBack', '');
}

  static void nativeFun(String funName,String params){
	    const methodChannel = const MethodChannel(Const.NATIVE_CHANNEL_NAME);
	    methodChannel.invokeMethod(funName, params);
  }

nativeFun方法调用原生方法goBack进行返回

四.flutter module源码不显示了

在项目里.idea目录下modules.xml中添加module标签 flutter_***为自己的flutter module名称

<module fileurl="file://$PROJECT_DIR$/../flutter_***/flutter_***.iml" filepath="$PROJECT_DIR$/../flutter_***/flutter_***.iml" />
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五个木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值