Integrate Flutter into existing applications

Github official Demo: github.com/flutter/sam…


1, create,Flutter Module,iOS,Androidproject

① Create folder multiple_demo

② Create a flutter Module under folder multiple_demo: flutter create -t Module multiple_flutters_module

Add the pod install file to the iOS project MultipleFluttersIos.

platform :ios.'9.0'

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

target 'MultipleFluttersIos' do

use_frameworks!
install_all_flutter_pods(flutter_application_path)

end
Copy the code

④ Create an Android project

2, a singleFlutterThe instance

① Command + B compiles the iOS project to generate Flutter and FlutterPluginRegistrant

② Create a single Flutter instance

//
// AppDelegate.swift
// MultipleFluttersIos
//
// Created by yuanzhiying on 2021/9/6.
//

import UIKit
import Flutter / / 1
import FlutterPluginRegistrant / / 2

@main
class AppDelegate: FlutterAppDelegate  { // 3 - Replace with FlutterAppDelegate
    lazy var flutterEngine = FlutterEngine(name: "my flutter engine") / / 4

    override func application(_ application: UIApplication.didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        flutterEngine.run() / / 5
        GeneratedPluginRegistrant.register(with: self.flutterEngine) / / 6
        
        return super.application(application, didFinishLaunchingWithOptions: launchOptions) / / 7
    }

    // MARK: UISceneSession Lifecycle

    override func application(_ application: UIApplication.configurationForConnecting connectingSceneSession: UISceneSession.options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    override func application(_ application: UIApplication.didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.}}Copy the code
//
// ViewController.swift
// MultipleFluttersIos
//
// Created by yuanzhiying on 2021/9/6.
//

import UIKit
import Flutter / / 1

class ViewController: UIViewController {
    override func viewDidLoad(a) {
        super.viewDidLoad()

        / / 2
        let button = UIButton(type: UIButton.ButtonType.custom)
        button.addTarget(self, action: #selector(showFlutter), for: .touchUpInside)
        button.setTitle("Show Flutter!", for: UIControl.State.normal)
        button.frame = CGRect(x: 80.0, y: 210.0, width: 160.0, height: 40.0)
        button.backgroundColor = UIColor.blue
        view.addSubview(button)
    }

    @objc func showFlutter(a) {
        / / 3
        let flutterEngine = (UIApplication.shared.delegate as! AppDelegate).flutterEngine
        let flutterViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
        present(flutterViewController, animated: true, completion: nil)}}Copy the code

3. Multiple Flutter instances

① Use FlutterEngineGroup to initialize FlutterEngine in AppDelegate

import Flutter / / 1
import UIKit

@main
class AppDelegate: UIResponder.UIApplicationDelegate {
    let engines = FlutterEngineGroup(name: "nultiple-flutters", project: nil) / / 2

    func application(_ application: UIApplication.didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        return true
    }

    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication.configurationForConnecting connectingSceneSession: UISceneSession.options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication.didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.}}Copy the code

② Define the Flutter page entry button

import UIKit

class HostViewController: UIViewController {
    override func viewDidLoad(a) {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

    @IBAction func gotoSingleFlutterPage(_ sender: Any) {
        let vc = SingleFlutterViewController(withEntrypoint: nil)
        navigationController?.pushViewController(vc, animated: true)}@IBAction func gotoMultipleFlutterPage(_ sender: Any) {
        let vc = DoubleFlutterViewController()
        navigationController?.pushViewController(vc, animated: true)}}Copy the code

③ Single Flutter instance page

import UIKit
import Flutter

class SingleFlutterViewController: FlutterViewController {
    init(withEntrypoint entryPoint: String?). {
        let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
        let newEngine = appDelegate.engines.makeEngine(withEntrypoint: entryPoint, libraryURI: nil)
        super.init(engine: newEngine, nibName: nil, bundle: nil)}required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")}deinit{}override func viewDidLoad(a) {
        super.viewDidLoad()
        
        self.view.backgroundColor = .white
    }
    

    /* // MARK: - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation override func prepare(for segue: UIStoryboardSegue, sender: Any?) { // Get the new view controller using segue.destination. // Pass the selected object to the new view controller. } */

}
Copy the code

④ Multiple Flutter instance pages

import UIKit

class DoubleFlutterViewController: UIViewController {
    // topMain bottomMain corresponds to the VM in a Flutter :entry-point
    private let topFlutter: SingleFlutterViewController = SingleFlutterViewController(withEntrypoint: "topMain")
    private let bottomFlutter: SingleFlutterViewController = SingleFlutterViewController(withEntrypoint: "bottomMain")

    override func viewDidLoad(a) {
        super.viewDidLoad()
        
        self.view.backgroundColor = .white

        addChild(topFlutter)
        addChild(bottomFlutter)

        let safeFrame = view.safeAreaLayoutGuide.layoutFrame
        let halfHeight = safeFrame.height / 2.0

        topFlutter.view.frame = CGRect(x: safeFrame.minX, y: safeFrame.minY, width: safeFrame.width, height: halfHeight)
        bottomFlutter.view.frame = CGRect(x: safeFrame.minX, y: topFlutter.view.frame.maxY, width: safeFrame.width, height: halfHeight)

        view.addSubview(topFlutter.view)
        view.addSubview(bottomFlutter.view)

        topFlutter.didMove(toParent: self)
        bottomFlutter.didMove(toParent: self)}}Copy the code

⑤ EntryPoint needed to define FlutterViewController in Flutter

import 'package:flutter/material.dart';

void main() => runApp(MyApp(color: Colors.blue));

@pragma('vm:entry-point')
void topMain() => runApp(MyApp(color: Colors.orange));

@pragma('vm:entry-point')
void bottomMain() => runApp(MyApp(color: Colors.green));

class MyApp extends StatelessWidget {
  // This widget is the root of your application.

  final MaterialColor color;

  const MyApp({Key? key, required this.color}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: color,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page')); }}class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment', child: Icon(Icons.add), ), ); }}Copy the code
4, original andFlutterExamples of data transfer between instances

① Create method channels and callbacks to native methods on the Flutter side

/ / create MethodChannel
_channel = const MethodChannel('multiple-flutters');
// Add the receive operation for the native calling method
_channel.setMethodCallHandler((call) async {
  if (call.method == "setCount") {
    // A notification that the host platform's data model has been updated.
    setState(() {
      _counter = call.arguments as int?;
    });
  } else {
    throw Exception('not implemented ${call.method}'); }});Copy the code

② Flutter calls native methods

_channel.invokeMethod<void> ("incrementCount", _counter);
Copy the code

③ Create a MethodChannel using FlutterEngine

private var channel: FlutterMethodChannel?
Copy the code
FlutterMethodChannel = FlutterMethodChannel(name: "multiple-flutters", binaryMessenger: self.engine! .binaryMessenger) // Create a callback channel for Flutter to invoke the native method? .setMethodCallHandler({ (call: FlutterMethodCall, result: FlutterResult) in if call.method == "incrementCount" { DataModel.shared.count = DataModel.shared.count + 1 result(nil) } })Copy the code
// Call the Flutter method channel? .invokeMethod("setCount", arguments: DataModel.shared.count)Copy the code