第2节:交互时iOS端的代码编写

第2节:交互时iOS端的代码编写

本节学习内容:原生iOS项目在交互时的代码编写。

本节学习用时:30分钟

本节学习方式:动手实践


本节目录:

一、本节内容介绍

二、本节代码解释


一、本节内容介绍

略!

二、本节代码解释

1、本节代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
- (void)showMainFlutterViewControllerWithoutParam {
self.navigationController.navigationBarHidden = YES;

FlutterViewController *flutterViewController = [[FlutterViewController alloc] initWithProject:nil nibName:nil bundle:nil];

NSString *channelName = @"com.dvlproad.ciyouzen/platform_channel";// 要与.dart中一致
FlutterMethodChannel *messageChannel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:flutterViewController];

[messageChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) {
// call.method获取flutter给回到的方法名,要匹配到channelName对应的多个 发送方法名,一般需要判断区分
// call.arguments获取到flutter给到的参数,(比如跳转到另一个页面所需要参数)
// result是给flutter的回调,只能回调一次
NSString *message = [NSString stringWithFormat:@"flutter回调:\n nmethod = %@\n arguments = %@", call.method, call.arguments];
NSLog(@"%@", message);

if ([call.method isEqualToString:@"showToast"]) {
[CJToast shortShowMessage:message];
return;

} else if ([call.method isEqualToString:@"goBack"]) {
[self.navigationController popViewControllerAnimated:YES];
return;

} else if ([call.method isEqualToString:@"goiosPage"]) {
OCCallFlutterViewController *viewController = [[OCCallFlutterViewController alloc] init];
viewController.parames = call.arguments;
[self.navigationController pushViewController:viewController animated:YES];
return;

} else if ([call.method isEqualToString:@"changeLeftBarButtonAction"]) {
NSDictionary *params = @{@"imageName":@"lib/Resources/message_arrow.png"};
result(params);
return;
}

result(FlutterMethodNotImplemented);
}];

[self.navigationController pushViewController:flutterViewController animated:YES];
}

第1节:用于测试与原生项目交互的Flutter项目

第1节:用于测试与原生项目交互的Flutter项目

本节学习内容:通过Flutter的平台通道在Flutter工程中建立提供给原生项目的方法及接收从原生项目返回的值。

本节学习用时:30分钟

本节学习方式:动手实践


本节目录:

一、本节内容介绍

二、本节代码解释


一、本节内容介绍

1、主要介绍的知识点有:

  • 在iOS界面中点击一个按钮跳转到Flutter页面,该Flutter页面有需要传参Main页面

2、要实现的效果如下图所示:

OC跳到Flutter有需要传参的Main页面

3、实现本节效果的代码

本节所有代码文件为:main.dartStatelessMainPage.dartStatefulMainPage.dartNewRouteNoneParam.dartNewRouteWithParam

您可自己通过flutter create materialappproject创建materialappproject项目后,将其lib文件夹下的代码文件替换为如下即可。

3.1、其中完整的main.dart代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import 'package:flutter/material.dart';
import 'StatelessMainPage.dart';
import 'StatefulMainPage.dart';
import 'NewRouteNoneParam.dart';
import 'NewRouteWithParam.dart';



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

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Platform Channel Demo',
home: StatelessMainPage(), //不需传参数的Flutter页面
//home: StatefulMainPage(), //需传参数的Flutter页面
routes: {
'StatelessMainPage':(context) => StatelessMainPage(),
'StatefulMainPage':(context) => StatefulMainPage(),
'NewRouteNoneParam':(context) => NewRouteNoneParam(),
'NewRouteWithParam':(context) => NewRouteWithParam()
}
);
}
}
3.2、其中完整的StatelessMainPage.dart代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import 'package:flutter/material.dart';
import 'StatefulMainPage.dart';
import 'NewRouteNoneParam.dart';
import 'NewRouteWithParam.dart';

import 'package:flutter/services.dart';
import 'dart:async';

// 已写在main.dart中
//void main() => runApp(MyApp());
//
//class MyApp extends StatelessWidget {
// @override
// Widget build(BuildContext context) {
// return MaterialApp(
// title: 'Platform Channel Demo',
// home: StatelessMainPage(),
// routes: {
// 'newRoute':(context) => NewRoute(),
// 'newRouteWithParam':(context) => NewRouteWithParam(),
// 'StatefulMainPage':(context) => StatefulMainPage()
// }
// );
// }
//}

class StatelessMainPage extends StatelessWidget {
// 创建一个给native的channel (类似iOS的通知)
static const methodChannel = const MethodChannel('com.dvlproad.ciyouzen/platform_channel');

// 给客户端发送一些东东, 这里先不用去拿回什么
Future<Null> _showToast() async {
try {
await methodChannel.invokeMethod('showToast','这是我在Flutter中写的文字');
} on PlatformException {

}
}

Future<Null> _goBack() async {
try {
await methodChannel.invokeMethod('goBack');
} on PlatformException {

}
}

Future<Null> _goiosPage() async {
try {
Map<String, dynamic> map = { "content": "flutter传给iOS的内容","data":[1,2,3,4,5]};
await methodChannel.invokeMethod('goiosPage', map);
} on PlatformException {

}
}

@override
Widget build(BuildContext context) {
//Scaffold是Material中主要的布局组件.
return new Scaffold(
appBar: new AppBar(
leading: new IconButton(
icon: new Icon(Icons.menu),
tooltip: 'Navigation menu',
onPressed: null,
),
title: new Text('platform channel Example title'),
actions: <Widget>[
// new IconButton(
// icon: new Icon(Icons.search),
// tooltip: 'Search',
// //onPressed: null,
// onPressed: () {
// goStatefulMainPage(context);
// },
// ),
new FlatButton(
child: Image.asset('lib/Resources/nav_right_gray_normal.png'),
onPressed: (){
goStatefulMainPage(context);
}),
],
),
//body占屏幕的大部分
body: new Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text('Hello, platform channel!'),
new RaisedButton(
child: Text('showToast'),
onPressed: (){
_showToast();
}
),
new RaisedButton(
child: Text('goBack'),
onPressed: (){
_goBack();
}
),
new RaisedButton(
child: Text('goiosPage'),
onPressed: (){
_goiosPage();
}
),

],
),
),
floatingActionButton: new FloatingActionButton(
tooltip: 'Add', // used by assistive technologies
child: new Icon(Icons.add),
onPressed: () {
goNextPageNoneParam(context);
},
)
);
}
}

void goNextPageNoneParam(context) {
Navigator.push(context, new MaterialPageRoute(builder: (context) {
return new NewRouteNoneParam();
}));
}


void goNextPageWithParams(context) {
Navigator.push(context, new MaterialPageRoute(builder: (context) {
return new NewRouteWithParam();
}));
}

void goStatefulMainPage(context) {
Navigator.push(context, new MaterialPageRoute(builder: (context) {
return new StatefulMainPage();
}));
}
3.3、其中完整的StatefulMainPage.dart代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import 'package:flutter/material.dart';
import 'NewRouteNoneParam.dart';

import 'package:flutter/services.dart';
import 'dart:async';


// 已写在main.dart中
//void main() => runApp(MyApp());
//
//class MyApp extends StatelessWidget {
// @override
// Widget build(BuildContext context) {
// return MaterialApp(
// title: 'Platform Channel Demo',
//
// home: StatefulMainPage(),
// );
// }
//}

class StatefulMainPage extends StatefulWidget {

@override
State<StatefulWidget> createState() {
// TODO: implement createState
return StatefulMainPageState();
}
}

class StatefulMainPageState extends State<StatefulMainPage> {
// 创建一个给native的channel (类似iOS的通知)
static const methodChannel = const MethodChannel(
'com.dvlproad.ciyouzen/platform_channel');

// 给客户端发送一些东东, 这里先不用去拿回什么
Future<Null> _showToast() async {
try {
await methodChannel.invokeMethod('showToast','这是我在Flutter中写的文字');
} on PlatformException {

}
}

Future<Null> _goBack() async {
try {
await methodChannel.invokeMethod('goBack');
} on PlatformException {

}
}

Future<Null> _goiosPage() async {
try {
Map<String, dynamic> map = { "content": "flutter传给iOS的内容","data":[1,2,3,4,5]};
await methodChannel.invokeMethod('goiosPage', map);
} on PlatformException {

}
}

String imageName = "lib/Resources/message_arrow.png";
Future<Null> _changeLeftBarButtonAction() async {
try {
Map<String, dynamic> map = { "content": "flutter传给iOS的内容","data":[1,2,3,4,5]};
final Map dict = await methodChannel.invokeMethod('changeLeftBarButtonAction', map);

setState(() {
imageName = dict["imageName"];
print(imageName);
});
} on PlatformException {

}
}

@override
void initState() {
// TODO: implement initState
super.initState();
_changeLeftBarButtonAction();
}

@override
Widget build(BuildContext context) {
//Scaffold是Material中主要的布局组件.
return new Scaffold(
appBar: new AppBar(
leading: new FlatButton(
// child: Image.asset('lib/Resources/nav_back_white_normal.png'),
child: Image.asset(imageName),
onPressed: (){
_goBack();
}),

// new IconButton(
// icon: new Icon(Icons.menu),
//// icon: Image.asset('lib/Resources/message_arrow.png'),
// tooltip: 'Navigation menu',
// onPressed: null,
// ),
title: new Text('StatefulMainPage'),
actions: <Widget>[
// new IconButton(
// icon: new Icon(Icons.search),
// tooltip: 'Search',
// onPressed: null,
// ),
new FlatButton(
child: Image.asset('lib/Resources/nav_right_gray_normal.png'),
onPressed: null
),
],
),
//body占屏幕的大部分
body: new Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset('lib/Resources/message_arrow.png'),
new Text('Hello, StatefulMainPage!'),
new RaisedButton(
child: Text('showToast'),
onPressed: (){
_showToast();
}
),
new RaisedButton(
child: Text('goBack'),
onPressed: (){
_goBack();
}
),
new RaisedButton(
child: Text('goiosPage'),
onPressed: (){
_goiosPage();
}
),

],
),
),
floatingActionButton: new FloatingActionButton(
tooltip: 'Add', // used by assistive technologies
child: new Icon(Icons.add),
onPressed: () {
goNextPageNoneParam(context);
},
)
);
}
}

void goNextPageNoneParam(context) {
Navigator.push(context, new MaterialPageRoute(builder: (context) {
return new NewRouteNoneParam();
}));
}
3.4、其中完整的NewRouteNoneParam.dart代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:async';

class NewRouteNoneParam extends StatelessWidget {
static const methodChannel = const MethodChannel('com.dvlproad.ciyouzen/platform_channel');
Future<Null> _goBack() async {
try {
await methodChannel.invokeMethod('goBack');
} on PlatformException {

}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("New route none Param"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("This is new route"),
RaisedButton(
child: Text('goback'),
onPressed: _goBack
)
],
)

),
);
}
}
3.5、其中完整的NewRouteWithParam.dart代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:async';

class NewRouteWithParam extends StatelessWidget {
static const methodChannel = const MethodChannel('com.dvlproad.ciyouzen/platform_channel');
Future<Null> _goBack() async {
try {
await methodChannel.invokeMethod('goBack');
} on PlatformException {

}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("New route with params"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("This is new route with params"),
RaisedButton(
child: Text('goback'),
onPressed: _goBack
)
],
)

),
);
}
}

第2节:Android项目集成Flutter

第2节:Android项目集成Flutter

本节学习内容:在已有的Android项目中集成Flutter,以使得您的Android项目中的部分界面/功能可以通过Flutter来实现。

本节学习用时:3分钟

本节学习方式:动手实践


本节目录:

一、集成方式介绍

二、集成方法介绍

三、集成后调用Flutter界面的代码编写


一、集成方式介绍

本节将介绍两种在已有的Android项目中集成Flutter的方法。

待补充。。。。。。。

第1节:iOS项目集成Flutter

第1节:iOS项目集成Flutter

本节学习内容:在已有的iOS项目中集成Flutter,以使得您的iOS项目中的部分界面/功能可以通过Flutter来实现。

本节学习用时:30分钟

本节学习方式:动手实践


本节目录:

一、集成方式介绍

二、集成方法一:手动配置iOS与Flutter的混编环境

三、集成方法二:Pod集成Flutter方式

四、集成后调用Flutter界面的代码编写


一、集成方式介绍

本节将介绍两种在已有的iOS项目中集成Flutter的方法,分别为①手动配置iOS与Flutter的混编环境、②Pod集成Flutter方式。

下面表格是两种集成Flutter的方法比较:

手动集成Flutter Pod集成Flutter
集成速度 需要自己建立文件、进行相应配置,并替换 执行Pod及Run Script即可
Flutter项目更新时 每次都需要替换 不用替换
Flutter项目与iOS同时路径变更时 不影响iOS工程 Flutter工程需要重新编译执行;
然后iOS工程需再重新执行Pod

二、集成方法一:手动配置iOS与Flutter的混编环境

1.1 集成后的最后项目结构如图:

myiosproject project structure

1.2 集成后的最后文件结构如图:

myiosproject file structure

1.3 详细的集成实现步骤如下:
  • ①、通过终端命令在指定目录(这里我们选择iOS项目的同级目录)创建myfluttermodule。flutter create -t module myfluttermodule

  • ②、Xcode:创建iOS项目,设为myiosproject;

  • ③、Xcode:创建配置衔接文件

    • 在②中建立的iOS项目中新建Config目录,用于存放/管理接下来Xcode工程需要创建的的配置衔接文件;

    • 选择创建Configuration Settings File文件,分别创建名为 Flutter.xcconfigDebug.xcconfigRelease.xcconfig的三个配置文件;

      New Configuration Settings File

    • 完善Flutter.xcconfigDebug.xcconfigRelease.xcconfig三个配置文件的内容为分别如下:

      Flutter.xcconfig 配置文件内容如下:

      1
      2
      3
      4
      5
      6
      > //Flutter.xconfig,用于指向外目录flutter module的`Generated.xcconfig`文件路径引用文件
      >
      > //这里填写前面建立的 myfluttermodule 的Generated.xcconfig的路径
      > #include "../../myfluttermodule/.ios/Flutter/Generated.xcconfig"
      > ENABLE_BITCODE=NO
      >

      Debug.xcconfig 配置文件内容如下:

      1
      2
      3
      4
      5
      6
      7
      > //Debug.xconfig,Xcode的环境配置文件
      >
      > #include "Flutter.xcconfig"
      > //#include "../Pods/Target Support Files/******.debug.xcconfig"//pod路径
      > #include "../Pods/Target Support Files/Pods-myiosproject/Pods-myiosproject.debug.xcconfig"//pod路径
      > FLUTTER_BUILD_MODE=debug
      >

      Release.xcconfig 配置文件内容如下:

      1
      2
      3
      4
      5
      6
      7
      > //Release.xconfig,Xcode的环境配置文件
      >
      > #include "Flutter.xcconfig"
      > //#include "../Pods/Target Support Files/******.release.xcconfig"//pod路径
      > #include "../Pods/Target Support Files/Pods-myiosproject/Pods-myiosproject.release.xcconfig"//pod路径
      > FLUTTER_BUILD_MODE=release
      >
    • 修改Xcode Project的环境配置选择

      myiosproject Project Info Configurations

  • ④、Xcode -> Target -> Build Phases -> New Run Script Phase,添加要执行的脚本内容引入xcode-backend.sh,此步很重要。

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

    myiosproject New Script xcode-backend.sh

  • ⑤、添加/替换flutter编译产物,如果之前已添加,则这里每次有有新的时候,注意要替换掉,否则界面还是旧的。

    即复制 myfluttermodule/.ios/Flutter下的App.frameworkengineflutter_assets进行替换。

    然后Xcode重新编译即可。

    注意:flutter_assets 并不能使用Create groups 的方式添加,只能使用Creat folder references 的方式添加进Xcode项目内,否则跳转flutter会页面渲染失败(页面空白)。

  • ⑥、至此,恭喜你,到这里您可向往常一样直接执行已集成Flutter的Xcode项目了。

三、集成方法二:Pod集成Flutter方式

2.1 最后的项目结构如图:

myiosproject2 project structure

2.2 实现步骤如下:
  • ①、通过终端命令在指定目录(这里我们选择iOS项目的同级目录)创建myfluttermodule。flutter create -t module myfluttermodule。这里因为我们已经在1中实现,所以不再重复创建。

  • ②、Xcode:创建myiosproject2;添加如Podfile,及在Podfile中如结构图中所示,补充

    1
    2
    flutter_application_path = './../myfluttermodule/'
    eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)

    后,执行pod install

    myiosproject2 pod install

  • ③、Xcode -> Target -> Build Phases -> New Run Script Phase,添加要执行的脚本内容引入xcode-backend.sh,此步很重要。同1中一样的做法。

  • ④、至此,混编环境配置成功!

四、集成后调用Flutter界面的代码编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#import "AppDelegate.h"
#import <Flutter/Flutter.h>

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
// ViewController *viewController = [[ViewController alloc] init];
// UIViewController *rootViewController = [[UINavigationController alloc] initWithRootViewController:viewController];
UIViewController *rootViewController = [[FlutterViewController alloc] initWithProject:nil nibName:nil bundle:nil];
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];

return YES;
}

而后,正常执行Xcode项目即可。

三、更多混编

更多混编知识,请查看官网:使用平台通道编写平台特定的代码

platform channel

附: