在Android和iOS中集成flutter

flutter可能是未來跨平臺開發的又一技術框架,那麼對於一個app,我們不可能完全用flutter來開發,那麼就意味着我們需要在已有的Android和iOS代碼中去集成flutter。目前這一技術還處於預覽狀態,並且還要切換flutter的channel爲mater分支。如下,官方原話:

0d44bde48e0f9e4b2a3b7fd1a4014e80014.jpg

那麼我們在集成之前需要查看現在flutter處於什麼渠道:

ef3c98ea69812513098c00dee88e0cddfb4.jpg

我的是處於master分支,如果你以前沒改過的話,應該是beta分支,那麼可以執行:

flutter channel master

進行切換。

下面正式開始集成Android和iOS。

Android

首先用Android studio創建一個Android工程,步驟不做介紹了。然後在Android工程的根目錄執行一下命令:

flutter create -t module my_flutter

來創建一個flutter的module,成功之後,目錄結構如下:f23cf41a43b5ff3ec2a51f3ef9bff0c98a2.jpg

接着我們來修改一下Android功能裏的gradle文件:

首先是app的setting.gradle文件,添加如下:

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

目的就是去加載指定目錄的include_flutter.groovy文件,那麼我們查看一下這個文件:

// Generated file. Do not edit.

def scriptFile = getClass().protectionDomain.codeSource.location.path
def flutterProjectRoot = new File(scriptFile).parentFile.parentFile

gradle.include ':flutter'
gradle.project(':flutter').projectDir = new File(flutterProjectRoot, '.android/Flutter')

def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot, '.flutter-plugins')
if (pluginsFile.exists()) {
    pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}

plugins.each { name, path ->
    def pluginDirectory = flutterProjectRoot.toPath().resolve(path).resolve('android').toFile()
    gradle.include ":$name"
    gradle.project(":$name").projectDir = pluginDirectory
}

gradle.getGradle().projectsLoaded { g ->
    g.rootProject.afterEvaluate { p ->
        p.subprojects { sp ->
            if (sp.name != 'flutter') {
                sp.evaluationDependsOn(':flutter')
            }
        }
    }
}

其中最重要的一段代碼,就是include ':flutter',意思就是flutter這個module要參與編譯。

接着在app層級(不是project層)的build.gradle文件中添加依賴:

dependencies {
  implementation project(':flutter')
  :
}

OK配置階段結束,我們開始先寫Android代碼,在activity中添加一個button,當我們點擊它時,將加載flutter佈局,代碼如下:

public class MainActivity extends AppCompatActivity {
    private TextView button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
                tx.replace(R.id.container, Flutter.createFragment("route1"));
                tx.commit();

//                View flutterView = Flutter.createView(MainActivity.this,getLifecycle(),"route1");
//                FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(100,100);
//                params.leftMargin = 100;
//                params.topMargin = 200;
//                addContentView(flutterView,params);
            }
        });
    }
}

這裏有兩種實現方式,一種是使用fragment,一種是使用FlutterView。代碼中的route1字符串則是flutter代碼中定義的,接下來就開始寫flutter代碼:

import 'dart:ui';
import 'package:flutter/material.dart';

void main() => runApp(_widgetForRoute(window.defaultRouteName));

Widget _widgetForRoute(String route) {
  switch (route) {
    case 'route1':
      return SomeWidget();
    case 'route2':
      return SomeWidget();
    default:
      return Center(
        child: Text('Unknown route: $route', textDirection: TextDirection.ltr),
      );
  }
}


class SomeWidget extends StatelessWidget{
  @override
    Widget build(BuildContext context) {
      // TODO: implement build
      return Container(
        
        width: 100,
        height: 100,
        color: Color(0xFF00FF00),
        child: Center(
          child: Text("hello",textDirection: TextDirection.ltr,),
        ),
      );
    }
}

這裏可以看到對rout1的定義。

寫到這裏代碼部分就完成了,然後運行android項目,就可以看到效果了。

ios

首先也是執行:

flutter create -t module my_flutter

生成一個flutter工程,由於在Android集成中已經做了這一步,故跳過。然後用Xcode創建一個iOS工程,創建完成之後,目錄如下:

918b373b06dc37fc5ffca3e4003f1135bef.jpg

下面爲工程添加flutter的依賴,這裏要使用cocoapods,若以前沒有安裝過,則執行命令:

sudo gem install cocoapods

然後在iOS工程的根目錄創建Podfile文件,命令爲:

touch Podfile 

然後修改podfile文件,如下:

target 'ios4Flutter' do
platform:ios,'8.0'
 
flutter_application_path = '../my_flutter/'
eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)
end

其中ios4Flutter爲我的iOS工程名,flutter_application_path爲flutter工程的根目錄。

最後執行:

pod install

完成項目的依賴,效果如下:

1ee63464662394a073dd82e32dee9655001.jpg

之後點擊.xcworkSpace文件打開iOS工程,找到Build Phases目錄,新建一個Script Phase,粘貼下面的命令:

"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed

到text area,如下圖:

d9cde1ae6d2ca159543ffe8499cbea64a55.jpg

配置完成之後,⌘B來build工程。如果沒有報錯,那麼部署成功。下面開始寫代碼:

在AppDelegate.h中:

#import <UIKit/UIKit.h>
#import <Flutter/Flutter.h>

@interface AppDelegate : FlutterAppDelegate
@end

AppDelegate.m:

#import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h> // Only if you have Flutter Plugins

#include "AppDelegate.h"

@implementation AppDelegate

// This override can be omitted if you do not have any Flutter Plugins.
- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GeneratedPluginRegistrant registerWithRegistry:self];
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@end

ViewController.m:

#import <Flutter/Flutter.h>
#import "ViewController.h"

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    [button addTarget:self
               action:@selector(handleButtonAction)
     forControlEvents:UIControlEventTouchUpInside];
    [button setTitle:@"Press me" forState:UIControlStateNormal];
    [button setBackgroundColor:[UIColor blueColor]];
    button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);
    [self.view addSubview:button];
}

- (void)handleButtonAction {
    FlutterViewController* flutterViewController = [[FlutterViewController alloc] init];
    [flutterViewController setInitialRoute:@"route1"];
    [self presentViewController:flutterViewController animated:false completion:nil];
}
@end

OK,oc代碼編寫完成,運行app,呈現效果。

Hot restart/reload and debugging Dart code

我們可以運用dart語言的特性實現 hot reload,首先在flutter的根目錄執行:

flutter attach

如下:

ed8318ef8f556e89d1137035b04d6a2615e.jpg

當運行完app,點擊按鈕進入flutter的view時,終端狀態如下:

9be9cea969331953cd06e701fcd5364c505.jpg

當我們再次修改dart代碼,保存之後,在命令中輸入r即可hot reload。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章