This paper mainly explains how existing iOS projects want to access flutter, how to access flutter, how to manage the project, how to call between Native and flutter, and how to debug flutter.
Create a Flutter Module
Execute the following command to create a Flutter Moudle
cd some/path/
flutter create --template module my_flutter
Copy the code
Some /path/ is the folder where you want to store the project, and then create a Flutter Module. This step should be careful not to create a flutter project.
Creating project my_flutter... androidx: true
my_flutter/test/widget_test.dart (created)
my_flutter/my_flutter.iml (created)
my_flutter/.gitignore (created)
my_flutter/.metadata (created)
my_flutter/pubspec.yaml (created)
my_flutter/README.md (created)
my_flutter/lib/main.dart (created)
my_flutter/my_flutter_android.iml (created)
my_flutter/.idea/libraries/Flutter_for_Android.xml (created)
my_flutter/.idea/libraries/Dart_SDK.xml (created)
my_flutter/.idea/modules.xml (created)
my_flutter/.idea/workspace.xml (created)
Running "flutter pub get" inmy_flutter... 1.8s Wrote 12 filesdone!
Your module code is in my_flutter/lib/main.dart.
Copy the code
The structure of the my_flutter file is as follows:
├── ├─ ├─ ├─ ├.├.├ ── ├.├.├ ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ─test/ └ ─ ─ pubspec yamlCopy the code
You can then add code logic in lib and dependency packages and plugins in pubspec.yaml.
Ii. Integration mode
1. Integrate with The Flutter SDK using CocoaPods
This solution is a hybrid solution for SDK versions higher than Flutter 1.8.4- pre-.21. If you use the previous SDK, Check out Upgrading Flutter added to Existing iOS Xcode project and Add Flutter to Existing apps
1.1 Add the following configuration to Podfile
flutter_application_path = '.. /my_flutter'
load File.join(flutter_application_path, '.ios'.'Flutter'.'podhelper.rb')
Copy the code
. /my_flutter is the directory where your flutter Moudle is stored. Here my_flutter is stored in the upper directory of your profile, so write this.
1.2 Add install_all_FLutter_PODS (Flutter_APPLICation_path) to Podfile target
target 'MyApp' do
install_all_flutter_pods(flutter_application_path)
end
Copy the code
In this case, MyApp is the name of the corresponding iOS project, and you can store it in the target of your project.
1.3 pod install
In the directory where your Podfile is located, execute Pod Install. If this works, it will add the following dependencies to your project:
Installing FlutterPluginRegistrant (0.0.1) Installing MY_FLUTTER (0.0.1)Copy the code
If the above 👆 dependency is not added after pod Install, there may be a problem with the project.
Error: My_flutter is a Flutter project, not a Flutter Moudle.
[!] Invalid `Podfile` file: cannot load such file -- ./my_flutter/.ios/Flutter/podhelper.rb.# from /Users/Example/Podfile:10
# -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
#
> load File.join(flutter_application_path, '.ios'.'Flutter'.'podhelper.rb')
#
# -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Copy the code
Check that flutter_application_path is correct. If it is, check that my_flutter is a Flutter project. Check that my_flutter contains.ios. First, set it to show and hide your files in the computer, and then check and confirm. If it contains the file, it is Moudle, and if it does not, it is Project.
Problem 2. Incorrect project signature
Run the following command in the my_flutter directory:
open -a Simulator
flutter build ios
Copy the code
If the following error message is displayed, the certificate is faulty:
It appears that your application still contains the default signing identifier.
Try replacing 'com.example' with your signing id in Xcode:
open ios/Runner.xcworkspace
Encountered error while building for device.
Copy the code
How to solve this problem?
Method one:
- 1. Find my_flutter/.ios and open Runner. Xcworkspace
- 2. Find out how to use the Signing & Capabilities certificate and configure the Generic iOS Deveice.
Method 2: Run the following command to ignore the signature:
flutter build ios --release --no-codesign
Copy the code
After the configuration is successful, run the flutter build ios again and the following information is printed:
Automatically signing iOS for device deployment using specified development team inXcode project: 56XB5ELH9A Running Xcode build... ├ ─ Building Dart code... 78.1 s ├ ─ Generating dSYM file... 0.1 s ├ ─ Stripping the debug symbols... 0.0 s ├ ─ Assembling Flutter resources... ├ ─ unscied, linking and signing... 2.9s Xcode build done. 84.0s BuiltCopy the code
Then pod Install should be successful again.
1.4 Advantages and Disadvantages of the Solution
Advantages:
- 1. Simple function configuration and convenient management.
- 2. Use CocoaPods for easy integration.
Disadvantages:
- 1. Team members must configure the FLUTTER environment, otherwise compilation will fail
- 2.Native code stored with Flutter code becomes complex.
2. Access the system in Framework mode
2.1 generation FrameWork
First switch to the directory where my_flutter is located and run the following command to generate the framework
flutter build ios-framework --output=.. /Flutter/Copy the code
After the command is executed successfully, a Flutter file is generated in the same directory as my_flutter. The file structure of the Flutter file is as follows:
Flutter / ├ ─ ─ the Debug / │ ├ ─ ─ Flutter. The framework │ ├ ─ ─ App. The framework │ ├ ─ ─ FlutterPluginRegistrant. The framework (onlyif├─ ├─ ├─ ├─ ├─ Profile / │ ├ ─ ─ Flutter. The framework │ ├ ─ ─ App. The framework │ ├ ─ ─ FlutterPluginRegistrant. The framework │ └ ─ ─ Example_plugin. Framework └ ─ ─ the Release / ├ ─ ─ Flutter. The framework ├ ─ ─ App. The framework ├ ─ ─ FlutterPluginRegistrant. The framework └ ─ ─ example_plugin.frameworkCopy the code
2.2 Configuring frameWork Paths
Build Settings > Build Phases > Link Binary With Libraries Add $(PROJECT_DIR)/Flutter/Release/ to Framework Search Paths
2.3 the embedded frameWork
Libraries and Embedded Content app.Framework and Flutter. Framework are added to the project and should be used.
Failed to find assets path for “flutter_assets”
Failed to find assets path for "flutter_assets"
[VERBOSE-2:engine.cc(114)] Engine run configuration was invalid.
Copy the code
If this error is reported, run the following command in my_flutter:
flutter clean
flutter build ios
Copy the code
Q.2 dyld: Library not the loaded: @ rpath/Flutter. The framework/Flutter
Embed frameWork Link Binary With Libraries Embed frameWork Link Binary With Libraries
2.4 Advantages and Disadvantages
Advantages:
- 1. Team members are not dependent on the FLUTTER environment
Disadvantages:
- 1. Packing configurations is cumbersome and requires manual operations.
3. Integrate with Flutter Framework and CocoaPods (native)
3.1 generation frameWork
The –cocoapods parameter is supported in Flutter v1.13.6 and can be used with the following command.
flutter build ios-framework --cocoapods --output=.. /Flutter/Copy the code
Generate the following file structure:
Flutter / ├ ─ ─ the Debug / │ ├ ─ ─ Flutter. Podspec │ ├ ─ ─ App. The framework │ ├ ─ ─ FlutterPluginRegistrant. The framework │ └ ─ ─ Example_plugin. Framework (each plugin with iOS Platform code is a separate framework) ├─ Profile/ │ ├─ fris.podSpec │ ├ ─ ─ App. Framework │ ├ ─ ─ FlutterPluginRegistrant. The framework │ └ ─ ─ example_plugin. The framework └ ─ ─ the Release / ├ ─ ─ Flutter. Podspec ├ ─ ─ App. Framework ├ ─ ─ FlutterPluginRegistrant. The framework └ ─ ─ example_plugin) frameworkCopy the code
3.2 Configuring a Profile
pod 'Flutter', :podspec => '.. /Flutter/{build_mode}/Flutter.podspec'
Copy the code
3.3 Advantages and Disadvantages of the Scheme
Advantages:
- 1. Team members are not dependent on the FLUTTER environment
- 2. You can use Cocoapods for integrated management.
Disadvantages:
- 1. The Flutter version is limited
- 2. Play frmaework by yourself every time
4. Integrate with Flutter Framework and CocoaPods (remotely)
4.1 Create a CocoaPods private library
Create the CocoaPods private library in the directory corresponding to my_FLUTTER
$ pod lib create MyFlutterFramework
Copy the code
Terminal execution code:
xingkunkun:FlutterForFW admin$ pod lib create MyFlutterFramework Cloning `https://github.com/CocoaPods/pod-template.git` into `MyFlutterFramework `. Configuring MyFlutter template. ------------------------------ To get you started we need to ask a few questions, this should only take a minute. What platformdo you want to use?? [ iOS / macOS ]
> ios
What language do you want to use?? [ Swift / ObjC ]
> objc
Would you like to include a demo application with your library? [ Yes / No ]
> no
Which testing frameworks will you use? [ Specta / Kiwi / None ]
> none
Would you like to do view based testing? [ Yes / No ]
> no
What is your class prefix?
>
Running pod install on your new library.
Copy the code
4.2 Creating a Flutter Module
- Create a Flutter Module step
flutter create --template module my_flutter
Copy the code
- 2. Build a framework
$flutter build ios --debug or flutter build ios --release -- no-coDesignCopy the code
- 3. Check the. Ios directory
- Is there a Flutter – > App framework
- Is there a Flutter – > engine – > Flutter. The framework
Ios directory Flutter-->App. Framework Flutter-->engine-->Flutter. FrameworkCopy the code
4.3 Integrate CocoaPods private library into Native project
Create iOS_Frameworks in MyFlutterFramework and copy app. framework and Flutter. Framework into it.
In the MyFlutterFramework podspec file, add the following configuration:
s.static_framework = true
arr = Array.new
arr.push('ios_frameworks/*.framework')
s.ios.vendored_frameworks = arr
Copy the code
It then executes in the podfile sibling of MyFlutterFramework
$ pod install
Copy the code
Add to podfile under MyApp project
platform :ios, '8.0'
target 'MyApp' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for MyApp
pod 'MyFlutterFramework', :path => '.. /MyFlutterFramework'
end
Copy the code
It is then executed in the podfile sibling of MyApp
$ pod install
Copy the code
In MyApp, app. framework and Flutter. Framework can be found
4.4 Push MyFlutterFramework and my_FLUTTER to the remote repository
- 1.MyFlutterFramework and my_FLUTTER are pushed to remote repositories
- 2. Modify the podfile under MyApp project
pod 'MyFlutterFramework'
Dependency changed to MyFlutterFramework remote connection.
platform :ios, '8.0'
target 'MyApp' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for MyApp
pod 'MyFlutterFramework', :git=>'https://gitlab.com/MyFlutterFramework.git'
end
Copy the code
-
- If iOS_Frameworks in MyFlutterFramework is not pushed to the remote repository, you can add them to the gitignore file
# ignore the file in iOS_Frameworks
ios_frameworks
Copy the code
4.5 Advantages and Disadvantages of the Solution
Advantages:
- 1. Team members are not dependent on the FLUTTER environment
- 2. You can use Cocoapods for integrated management.
- 3. Project code can be shared and managed using remote repositories
Disadvantages:
- 1. Each time you rebuild the framework, you need to move the framework, which is cumbersome. You can use scripts to solve the problem.
3. Interaction between Flutter and Native
The Flutter official provides a Platform Channel solution for Dart and Platform communication.
Core Principles:
- The Flutter application encodes data into messages via Platform Channel and sends them across threads to the host of the application (Android or iOS).
- When a host receives a message from the Platform Channel, it invokes the Platform’s API, the native programming language, to execute the corresponding method.
- After execution, the resulting data is returned to the Flutter section of the application in the same way.
Flutter provides three different channels:
- BasicMessageChannel (mainly passing strings and some semi-structured data)
- MethodChannel (for passing method calls)
- EventChannel (Communication of data streams)
Here is an example of using Platform Channel to communicate: Sample code
1.Native APP actively interacts with Flutter
The interaction is mainly divided into three steps:
- 1. Register MethodChannel flutter
- 2. The flutter MethodChannel listens for native messages
- 3. Native sends messages through the MethodChannel
The Dart code
class HomePage extends StatefulWidget {
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String title = 'Flutter to Native'; Color backGroundColor = Colors.red; Static const MethodChannel MethodChannel = const MethodChannel('com.pages.your/native_get');
_HomePageStateCall the Dart method () {/ / Native methodChannel. SetMethodCallHandler ({(MethodCall call)if(call.method == "NativeToFlutter") {setState(() {
title = call.arguments;
backGroundColor = Colors.yellow;
});
}
return Future<dynamic>.value();
});
}
@override
Widget build(BuildContext context) {
returnScaffold( backgroundColor: backGroundColor, body: Center( child: GestureDetector( behavior: HitTestBehavior.opaque, child: new Text(title), onTap: (){ _iOSPushToVC(); },),),); }}Copy the code
Native code
#import "ViewController.h"
#import <Flutter/Flutter.h>
#define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width)
@interface ViewController ()
@property (nonatomic, strong) FlutterMethodChannel *messageChannel;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:@selector(pressOn) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@"Loading Flutter" forState:UIControlStateNormal];
[button setBackgroundColor:[UIColor blueColor]]; button.frame = CGRectMake((SCREEN_WIDTH-100)/2, 100, 100, 60); [self.view addSubview:button]; } - (void)pressOn { FlutterViewController *flutterViewController =[FlutterViewController new]; // Set the route parameter [flutterViewController]setInitialRoute:@"route"];
NSString *channelName = @"com.pages.your/native_get"; Agree. / / to the main dart _messageChannel = [FlutterMethodChannel methodChannelWithName: channelName binaryMessenger:flutterViewController]; [self nativeToFlutter]; [self presentViewController:flutterViewController animated:false completion:nil];
}
- (void)nativeToFlutter
{
sleep(5);
[_messageChannel invokeMethod:@"NativeToFlutter" arguments:@"NativeToFlutter"];
}
@end
Copy the code
2.Flutter actively interacts with Native APP
Interaction is mainly divided into the following steps:
- 1.Native creates a MethodChannel.
- 2.Native adds HandleBlcok.
- 3.Flutter sends messages.
The Dart code
class HomePage extends StatefulWidget {
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String title = 'Flutter to Native'; Color backGroundColor = Colors.red; Static const MethodChannel MethodChannel = const MethodChannel('com.pages.your/native_get'); //Dart calls Native methods and receives the return value. _iOSPushToVC() async { title = await methodChannel.invokeMethod('FlutterToNative');
setState(() {
backGroundColor = Colors.green;
});
}
@override
Widget build(BuildContext context) {
returnScaffold( backgroundColor: backGroundColor, body: Center( child: GestureDetector( behavior: HitTestBehavior.opaque, child: new Text(title), onTap: (){ _iOSPushToVC(); },),),); }}Copy the code
Native code
#import "ViewController.h"
#import <Flutter/Flutter.h>
#define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width)
@interface ViewController ()
@property (nonatomic, strong) FlutterMethodChannel *messageChannel;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:@selector(pressOn) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@"Loading Flutter" forState:UIControlStateNormal];
[button setBackgroundColor:[UIColor blueColor]]; button.frame = CGRectMake((SCREEN_WIDTH-100)/2, 100, 100, 60); [self.view addSubview:button]; } - (void)pressOn { FlutterViewController *flutterViewController =[FlutterViewController new]; // Set the route parameter [flutterViewController]setInitialRoute:@"route"];
NSString *channelName = @"com.pages.your/native_get"; Agree. / / to the main dart _messageChannel = [FlutterMethodChannel methodChannelWithName: channelName binaryMessenger:flutterViewController]; __weak typeof(self) weakSelf = self; [_messageChannelsetMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result)
{
__strong typeof(self) strongSelf = weakSelf;
if ([call.method isEqualToString:@"FlutterToNative"]) {
if (result) {
result(@"NativeBack"); }}}]; [self presentViewController:flutterViewController animated:false completion:nil];
}
@end
Copy the code
Iv. App debugging
How do I debug the Dart code during XCode debugging when I’m in mixed development? Or can you use hot loading?
1. Debug Dart code
In mixed development, in iOS projects, how do we debug dart code?
- 1. Close our app
- 2. Click the Flutter Attach button on the Android Studio toolbar
- Waiting for a connection from Flutter on iPhone 11 Pro…
- 3. Start our app
- After the app is started, the message Syncing Files to Device iPhone 11 Pro…
You can then debug the Dart code in mixed development mode just as you would debug the normal Flutter project.
2. The thermal load
- 1. Close our app
- 2. Run the flutter attach command in terminal.
$ flutter attach
Waiting for a connection from Flutter on iPhone 11 Pro Max...
Copy the code
Note that there are multiple devices, as follows:
More than one device connected; please specify a device with the '-d <deviceId>' flag, or use '-d all'IPhone • 00008030-000445611146802E • ios • ios 13.3 iPhone 11 Pro Max • 67 fcc5b2 - ef0 DA5D - 4-8 de1-53 e8f8c4cba9, ios, com. Apple. CoreSimulator. SimRuntime. Ios - 13-2 (simulator)Copy the code
You can use the following command:
flutter attach -d67FCC5B2-DA5D-4EF0-8DE1-53E8F8c4cba9 // 67FCC5B2-DA5D-4EF0-8DE1-53E8F8c4cba9 is the id of the deviceCopy the code
- 3. Start the app. After startup, the following prompt will be displayed, indicating success.
Syncing files to device iPhone 11 Pro Max...
4,196ms (!)
🔥 To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".
An Observatory debugger and profiler on iPhone 11 Pro Max is available at: http://127.0.0.1:62889/T9DjblAu03w=/
For a more detailed help message, press "h". To detach, press "d"; to quit, press "q".
Copy the code
Now you can debug in Terminal:
R: hot loading; R: hot restart; H: Get help; D: Disconnect; Q: Exit;Copy the code
References: Integrate a Flutter module into your iOS project Upgrading Flutter added to existing iOS Xcode project Add Flutter to Introduction to the elegant mix of Flutter components, flutter and native iOS interaction, flutter hybrid development IOS Project Integration with Flutter Module detailed guide to understand Flutter Platform Channel Flutter hybrid development ii – How to use Flutter hybrid development In-depth understanding of the Platform Channel mechanism of Flutter