iOS Native混编Flutter 交互实践

 

hellobike的thrio

https://github.com/hellobike/flutter_thrio

https://juejin.im/post/5eb281e86fb9a0433c50a182

 

alibaba的 flutter_boost 

https://github.com/alibaba/flutter_boost

完成的demo

https://gitee.com/youhui_xm/flutter_boost_sample/

 

Jony-iMac:~ Jony$ cd /Users/Jony/Documents/DEMO/flutter
Jony-iMac:flutter Jony$ flutter create my_flutter
Creating project my_flutter... androidx: true
my_flutter/ios/Runner.xcworkspace/contents.xcworkspacedata (created)
my_flutter/ios/Runner/Info.plist (created)
my_flutter/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
(created)
my_flutter/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
(created)
my_flutter/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md (created)
my_flutter/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
(created)
my_flutter/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
(created)
my_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
(created)
my_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
(created)
my_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
(created)
my_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
(created)
my_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x
.png (created)
my_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x
.png (created)
my_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
(created)
my_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
(created)
my_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
(created)
my_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
(created)
my_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
(created)
my_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
(created)
my_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
(created)
my_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
(created)
my_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
(created)
my_flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
(created)
my_flutter/ios/Runner/Base.lproj/LaunchScreen.storyboard (created)
my_flutter/ios/Runner/Base.lproj/Main.storyboard (created)
my_flutter/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
(created)
my_flutter/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
(created)
my_flutter/ios/Flutter/Debug.xcconfig (created)
my_flutter/ios/Flutter/Release.xcconfig (created)
my_flutter/ios/Flutter/AppFrameworkInfo.plist (created)
my_flutter/ios/.gitignore (created)
my_flutter/test/widget_test.dart (created)
my_flutter/my_flutter.iml (created)
my_flutter/.gitignore (created)
my_flutter/web/index.html (created)
my_flutter/.metadata (created)
my_flutter/android/app/src/profile/AndroidManifest.xml (created)
my_flutter/android/app/src/main/res/mipmap-mdpi/ic_launcher.png (created)
my_flutter/android/app/src/main/res/mipmap-hdpi/ic_launcher.png (created)
my_flutter/android/app/src/main/res/drawable/launch_background.xml (created)
my_flutter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (created)
my_flutter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (created)
my_flutter/android/app/src/main/res/values/styles.xml (created)
my_flutter/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png (created)
my_flutter/android/app/src/main/AndroidManifest.xml (created)
my_flutter/android/app/src/debug/AndroidManifest.xml (created)
my_flutter/android/gradle/wrapper/gradle-wrapper.properties (created)
my_flutter/android/gradle.properties (created)
my_flutter/android/.gitignore (created)
my_flutter/android/settings.gradle (created)
my_flutter/android/app/build.gradle (created)
my_flutter/android/app/src/main/kotlin/com/example/my_flutter/MainActivity.kt
(created)
my_flutter/android/build.gradle (created)
my_flutter/android/my_flutter_android.iml (created)
my_flutter/pubspec.yaml (created)
my_flutter/README.md (created)
my_flutter/ios/Runner/Runner-Bridging-Header.h (created)
my_flutter/ios/Runner/AppDelegate.swift (created)
my_flutter/ios/Runner.xcodeproj/project.pbxproj (created)
my_flutter/lib/main.dart (created)
my_flutter/.idea/runConfigurations/main_dart.xml (created)
my_flutter/.idea/libraries/Flutter_for_Android.xml (created)
my_flutter/.idea/libraries/Dart_SDK.xml (created)
my_flutter/.idea/libraries/KotlinJavaRuntime.xml (created)
my_flutter/.idea/modules.xml (created)
my_flutter/.idea/workspace.xml (created)
Running "flutter pub get" in my_flutter... 1.0s
Wrote 69 files.

All done!
[✓] Flutter: is fully installed. (Channel master, v1.13.9-pre.36, on Mac OS X
10.15.3 19D76, locale zh-Hans-CN)
[✓] Android toolchain - develop for Android devices: is fully installed.
(Android SDK version 29.0.3)
[✓] Xcode - develop for iOS and macOS: is fully installed. (Xcode 11.3.1)
[✓] Chrome - develop for the web: is fully installed.
[✓] Android Studio: is fully installed. (version 3.6)
[✓] IntelliJ IDEA Ultimate Edition: is fully installed. (version 2019.2.3)
[✓] VS Code: is fully installed. (version 1.43.0)
[✓] Connected device: is fully installed. (3 available)

In order to run your application, type:

$ cd my_flutter
$ flutter run

Your application code is in my_flutter/lib/main.dart.

Jony-iMac:flutter Jony$ cd my_flutter/
android ios my_flutter.iml pubspec.yaml web
Jony-iMac:my_flutter Jony$ cd ios
Jony-iMac:ios Jony$ pod install
Analyzing dependencies
Downloading dependencies
Installing Flutter (1.0.0)
Installing flutter_boost (0.0.2)
Generating Pods project
Integrating client project
Pod installation complete! There are 2 dependencies from the Podfile and 2 total pods installed.

[!] Automatically assigning platform `iOS` with version `8.0` on target `Runner` because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`.

[!] CocoaPods did not set the base configuration of your project because your project already has a custom config set. In order for CocoaPods integration to work at all, please either set the base configurations of the target `Runner` to `Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig` or include the `Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig` in your build configuration (`Flutter/Release.xcconfig`).

Jony-iMac:ios Jony$ cd ../
Jony-iMac:my_flutter Jony$ flutter build ios --debug --no-codesign
Warning: Building for device with codesigning disabled. You will have to
manually codesign before deploying to device.
Building com.example.myFlutter for device (ios)...
Running pod install... 1.4s
Running Xcode build...

├─Assembling Flutter resources... 9.7s
└─Compiling, linking and signing... 18.8s
Xcode build done. 34.0s
Built
/Users/Jony/Documents/DEMO/flutter/my_flutter/build/ios/iphoneos/Runner.app.
Jony-iMac:my_flutter Jony$ flutter run
Launching lib/main.dart on iPhone 11 Pro Max in debug mode...

Running pod install... 1.5s
Running Xcode build...

├─Assembling Flutter resources... 10.8s
└─Compiling, linking and signing... 18.9s
Xcode build done. 33.6s
flutter: FlutterBoost#onShownContainerChanged old:null now:default
flutter: FlutterBoost#onMetohdCall didInitPageContainer
flutter: FlutterBoost#BoostContainerLifeCycleObserver container:second lifeCycle:ContainerLifeCycle.Init
flutter: FlutterBoost#onMetohdCall willShowPageContainer
flutter: FlutterBoost#ContainerObserver#2 didPush
flutter: flutterboost#didPush
flutter: FlutterBoost#build widget:SecondRouteWidget for page:second(0)
flutter: FlutterBoost#onShownContainerChanged old:default now:0
Syncing files to device iPhone 11 Pro Max...
flutter: FlutterBoost#onMetohdCall didShowPageContainer
flutter: FlutterBoost#onShownContainerChanged old:null now:0 flutter: FlutterBoost#BoostContainerLifeCycleObserver container:second lifeCycle:ContainerLifeCycle.Appear
flutter: FlutterBoost#native containner did show, manager dump: onstage#: {uniqueId=0,name=second} offstage#: {uniqueId=default,name=default} flutter: FlutterBoost#onMetohdCall willDisappearPageContainer flutter: FlutterBoost#BoostContainerLifeCycleObserver container:second lifeCycle:ContainerLifeCycle.WillDisappear
flutter: FlutterBoost#onMetohdCall didDisappearPageContainer flutter: FlutterBoost#BoostContainerLifeCycleObserver container:second lifeCycle:ContainerLifeCycle.Disappear
Syncing files to device iPhone 11 Pro Max... 5,653ms (!)
Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h Repeat this help message.
d Detach (terminate "flutter run" but leave application running).
q Quit (terminate the application on the device).
An Observatory debugger and profiler on iPhone 11 Pro Max is available at:
http://127.0.0.1:63692/zgHSnkcPeN8=/
Application finished.
Jony-iMac:my_flutter Jony$

 

 

1、pubspec.yaml  加入

  flutter_boost:
    git:
        url: 'https://github.com/alibaba/flutter_boost.git'
        ref: '1.12.13'

 

2、lib目录下 加入如下代码

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
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
import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'simple_page_widgets.dart';
 
void main() {
  runApp(MyApp());
}
 
class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}
 
class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
 
    FlutterBoost.singleton.registerPageBuilders({
      'embeded': (pageName, params, _)=>EmbededFirstRouteWidget(),
      'first': (pageName, params, _) => FirstRouteWidget(),
      'second': (pageName, params, _) => SecondRouteWidget(),
      'tab': (pageName, params, _) => TabRouteWidget(),
      'platformView': (pageName, params, _) => PlatformRouteWidget(),
      'flutterFragment': (pageName, params, _) => FragmentRouteWidget(params),
      ///可以在native层通过 getContainerParams 来传递参数
      'flutterPage': (pageName, params, _) {
        print("flutterPage params:$params");
 
        return FlutterRouteWidget(params:params);
      },
    });
    FlutterBoost.singleton.addBoostNavigatorObserver(TestBoostNavigatorObserver());
  }
 
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Boost example',
        builder: FlutterBoost.init(postPush: _onRoutePushed),
        home: Container(
            color:Colors.white
        ));
  }
 
  void _onRoutePushed(
      String pageName, String uniqueId, Map params, Route route, Future _) {
  }
}
class TestBoostNavigatorObserver extends NavigatorObserver{
  void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
 
    print("flutterboost#didPush");
  }
 
  void didPop(Route<dynamic> route, Route<dynamic> previousRoute) {
    print("flutterboost#didPop");
  }
 
  void didRemove(Route<dynamic> route, Route<dynamic> previousRoute) {
    print("flutterboost#didRemove");
  }
 
  void didReplace({Route<dynamic> newRoute, Route<dynamic> oldRoute}) {
    print("flutterboost#didReplace");
  }
}

platform_view.dart

复制代码
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

typedef void TextViewCreatedCallback(TextViewController controller);

class TextView extends StatefulWidget {
  const TextView({
    Key key,
    this.onTextViewCreated,
  }) : super(key: key);

  final TextViewCreatedCallback onTextViewCreated;

  @override
  State<StatefulWidget> createState() => _TextViewState();
}

class _TextViewState extends State<TextView> {
  @override
  Widget build(BuildContext context) {
    if (defaultTargetPlatform == TargetPlatform.android) {
      return AndroidView(
        viewType: 'plugins.test/view',
        onPlatformViewCreated: _onPlatformViewCreated,
      );
    }
    return Text(
        '$defaultTargetPlatform is not yet supported by the text_view plugin');
  }

  void _onPlatformViewCreated(int id) {
    if (widget.onTextViewCreated == null) {
      return;
    }
    widget.onTextViewCreated(new TextViewController._(id));
  }
}

class TextViewController {
  TextViewController._(int id)
      : _channel = new MethodChannel('plugins.felix.angelov/textview_$id');

  final MethodChannel _channel;

  Future<void> setText(String text) async {
    assert(text != null);
    return _channel.invokeMethod('setText', text);
  }
}
复制代码

simple_page_widgets.dart

复制代码
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:my_flutter/platform_view.dart';

class FirstRouteWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Route'),
      ),
      body: Center(
        child:
        Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: 
          <Widget>[
            RaisedButton(
                child: Text('Open native page'),
                onPressed: () {
                  print("open natve page!");
                  FlutterBoost.singleton.open("native").then((Map value) {
                    print(
                        "call me when page is finished. did recieve second route result $value");
                  });
                },
              ),

            RaisedButton(
                child: Text('Open second route'),
                onPressed: () {
                  print("open second page!");
                  FlutterBoost.singleton.open("second").then((Map value) {
                    print(
                        "call me when page is finished. did recieve second route result $value");
                  });
                },
              ),

              RaisedButton(
                  child: Text('Present second route'),
                  onPressed: () {
                    print("Present second page!");
                    FlutterBoost.singleton.open("second",urlParams:<dynamic,dynamic>{"present":true}).then((Map value) {
                      print(
                          "call me when page is finished. did recieve second route result $value");
                    });
                  },
                ),
            ],
        ),
      ),
    );
  }
}

class EmbededFirstRouteWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RaisedButton(
          child: Text('Open second route'),
          onPressed: () {
            print("open second page!");
            FlutterBoost.singleton.open("second").then((Map value) {
              print(
                  "call me when page is finished. did recieve second route result $value");
            });
          },
        ),
      ),
    );
  }
}

class SecondRouteWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Route"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            // Navigate back to first route when tapped.

            BoostContainerSettings settings =
                BoostContainer.of(context).settings;
            FlutterBoost.singleton.close(settings.uniqueId,
                result: <dynamic,dynamic>{"result": "data from second"});
          },
          child: Text('Go back with result!'),
        ),
      ),
    );
  }
}

class TabRouteWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Tab Route"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            FlutterBoost.singleton.open("second");
          },
          child: Text('Open second route'),
        ),
      ),
    );
  }
}

class PlatformRouteWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title:Text("Platform Route"),
      ),
      body: Center(
        child: RaisedButton(
          child: TextView(),
          onPressed: () {
            print("open second page!");
            FlutterBoost.singleton.open("second").then((Map value) {
              print(
                  "call me when page is finished. did recieve second route result $value");
            });
          },
        ),
      ),
    );
  }
}
class FlutterRouteWidget extends StatefulWidget {
  FlutterRouteWidget({this.params,this.message});
  final Map params;
  final String message;

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

class _FlutterRouteWidgetState extends State<FlutterRouteWidget> {
  final TextEditingController _usernameController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    final String message=widget.message;
    return Scaffold(
      appBar: AppBar(
        brightness:Brightness.light,
        backgroundColor: Colors.white,
        textTheme:new TextTheme(title: TextStyle(color: Colors.black)) ,

        title: Text('flutter_boost_example'),
      ),
      body: SingleChildScrollView(
        child:Container(
            margin: const EdgeInsets.all(24.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Container(
                  margin: const EdgeInsets.only(top: 10.0,bottom: 20.0),
                  child: Text(
                    message ?? "This is a flutter activity \n params:${widget.params}",
                    style: TextStyle(fontSize: 28.0, color: Colors.blue),
                  ),
                  alignment: AlignmentDirectional.center,
                ),
//                Expanded(child: Container()),
                const CupertinoTextField(
                  prefix: Icon(
                    CupertinoIcons.person_solid,
                    color: CupertinoColors.lightBackgroundGray,
                    size: 28.0,
                  ),
                  padding: EdgeInsets.symmetric(horizontal: 6.0, vertical: 12.0),
                  clearButtonMode: OverlayVisibilityMode.editing,
                  textCapitalization: TextCapitalization.words,
                  autocorrect: false,
                  decoration: BoxDecoration(
                    border: Border(bottom: BorderSide(width: 0.0, color: CupertinoColors.inactiveGray)),
                  ),
                  placeholder: 'Name',
                ),
                InkWell(
                  child: Container(
                      padding: const EdgeInsets.all(8.0),
                      margin: const EdgeInsets.all(8.0),
                      color: Colors.yellow,
                      child: Text(
                        'open native page',
                        style: TextStyle(fontSize: 22.0, color: Colors.black),
                      )),

                  ///后面的参数会在native的IPlatform.startActivity方法回调中拼接到url的query部分。
                  ///例如:sample://nativePage?aaa=bbb
                  onTap: () => FlutterBoost.singleton
                      .open("sample://nativePage", urlParams: <dynamic,dynamic>{
                    "query": {"aaa": "bbb"}
                  }),
                ),
                InkWell(
                  child: Container(
                      padding: const EdgeInsets.all(8.0),
                      margin: const EdgeInsets.all(8.0),
                      color: Colors.yellow,
                      child: Text(
                        'open first',
                        style: TextStyle(fontSize: 22.0, color: Colors.black),
                      )),

                  ///后面的参数会在native的IPlatform.startActivity方法回调中拼接到url的query部分。
                  ///例如:sample://nativePage?aaa=bbb
                  onTap: () => FlutterBoost.singleton
                      .open("first", urlParams: <dynamic,dynamic>{
                    "query": {"aaa": "bbb"}
                  }),
                ),
                InkWell(
                  child: Container(
                      padding: const EdgeInsets.all(8.0),
                      margin: const EdgeInsets.all(8.0),
                      color: Colors.yellow,
                      child: Text(
                        'open second',
                        style: TextStyle(fontSize: 22.0, color: Colors.black),
                      )),

                  ///后面的参数会在native的IPlatform.startActivity方法回调中拼接到url的query部分。
                  ///例如:sample://nativePage?aaa=bbb
                  onTap: () => FlutterBoost.singleton
                      .open("second", urlParams:<dynamic,dynamic> {
                    "query": {"aaa": "bbb"}
                  }),
                ),
                InkWell(
                  child: Container(
                      padding: const EdgeInsets.all(8.0),
                      margin: const EdgeInsets.all(8.0),
                      color: Colors.yellow,
                      child: Text(
                        'open tab',
                        style: TextStyle(fontSize: 22.0, color: Colors.black),
                      )),

                  ///后面的参数会在native的IPlatform.startActivity方法回调中拼接到url的query部分。
                  ///例如:sample://nativePage?aaa=bbb
                  onTap: () => FlutterBoost.singleton
                      .open("tab", urlParams:<dynamic,dynamic> {
                    "query": {"aaa": "bbb"}
                  }),
                ),
                InkWell(
                  child: Container(
                      padding: const EdgeInsets.all(8.0),
                      margin: const EdgeInsets.all(8.0),
                      color: Colors.yellow,
                      child: Text(
                        'open flutter page',
                        style: TextStyle(fontSize: 22.0, color: Colors.black),
                      )),

                  ///后面的参数会在native的IPlatform.startActivity方法回调中拼接到url的query部分。
                  ///例如:sample://nativePage?aaa=bbb
                  onTap: () => FlutterBoost.singleton
                      .open("sample://flutterPage", urlParams:<dynamic,dynamic> {
                    "query": {"aaa": "bbb"}
                  }),
                ),
                InkWell(
                  child: Container(
                      padding: const EdgeInsets.all(8.0),
                      margin: const EdgeInsets.all(8.0),
                      color: Colors.yellow,
                      child: Text(
                        'push flutter widget',
                        style: TextStyle(fontSize: 22.0, color: Colors.black),
                      )),
                  onTap: () {
                  Navigator.push<dynamic>(context,
                        MaterialPageRoute<dynamic>(builder: (_) => PushWidget()));
                  },
                ),

              InkWell(
                child: Container(
                    padding: const EdgeInsets.all(8.0),
                    margin: const EdgeInsets.all(8.0),
                    color: Colors.yellow,
                    child: Text(
                      'push Platform demo',
                      style: TextStyle(fontSize: 22.0, color: Colors.black),
                    )),
                onTap: () {
                  Navigator.push<dynamic>(context,
                      MaterialPageRoute<dynamic>(builder: (_) => PlatformRouteWidget()));
                },
              ),
                InkWell(
                  child: Container(
                      padding: const EdgeInsets.all(8.0),
                      margin: const EdgeInsets.all(8.0),
                      color: Colors.yellow,
                      child: Text(
                        'open flutter fragment page',
                        style: TextStyle(fontSize: 22.0, color: Colors.black),
                      )),
                  onTap: () => FlutterBoost.singleton
                      .open("sample://flutterFragmentPage"),
                ),
              ],
            ),

        ),
      ),
    );
  }
}

class FragmentRouteWidget extends StatelessWidget {
  final Map params;

  FragmentRouteWidget(this.params);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('flutter_boost_example'),
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Container(
            margin: const EdgeInsets.only(top: 80.0),
            child: Text(
              "This is a flutter fragment",
              style: TextStyle(fontSize: 28.0, color: Colors.blue),
            ),
            alignment: AlignmentDirectional.center,
          ),
          Container(
            margin: const EdgeInsets.only(top: 32.0),
            child: Text(
              params['tag'] ?? '',
              style: TextStyle(fontSize: 28.0, color: Colors.red),
            ),
            alignment: AlignmentDirectional.center,
          ),
          Expanded(child: Container()),
          InkWell(
            child: Container(
                padding: const EdgeInsets.all(8.0),
                margin: const EdgeInsets.all(8.0),
                color: Colors.yellow,
                child: Text(
                  'open native page',
                  style: TextStyle(fontSize: 22.0, color: Colors.black),
                )),
            onTap: () => FlutterBoost.singleton.open("sample://nativePage"),
          ),
          InkWell(
            child: Container(
                padding: const EdgeInsets.all(8.0),
                margin: const EdgeInsets.all(8.0),
                color: Colors.yellow,
                child: Text(
                  'open flutter page',
                  style: TextStyle(fontSize: 22.0, color: Colors.black),
                )),
            onTap: () => FlutterBoost.singleton.open("sample://flutterPage"),
          ),
          InkWell(
            child: Container(
                padding: const EdgeInsets.all(8.0),
                margin: const EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 80.0),
                color: Colors.yellow,
                child: Text(
                  'open flutter fragment page',
                  style: TextStyle(fontSize: 22.0, color: Colors.black),
                )),
            onTap: () =>
                FlutterBoost.singleton.open("sample://flutterFragmentPage"),
          )
        ],
      ),
    );
  }
}

class PushWidget extends StatefulWidget {
  @override
  _PushWidgetState createState() => _PushWidgetState();
}

class _PushWidgetState extends State<PushWidget> {
  VoidCallback _backPressedListenerUnsub;

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

  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    super.didChangeDependencies();

//    if (_backPressedListenerUnsub == null) {
//      _backPressedListenerUnsub =
//          BoostContainer.of(context).addBackPressedListener(() {
//        if (BoostContainer.of(context).onstage &&
//            ModalRoute.of(context).isCurrent) {
//          Navigator.pop(context);
//        }
//      });
//    }
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    _backPressedListenerUnsub?.call();
  }

  @override
  Widget build(BuildContext context) {
    return FlutterRouteWidget(message: "Pushed Widget");
  }
}
复制代码

test_input.dart

复制代码
import 'package:flutter/material.dart';

class TestPage extends StatefulWidget {
  TestPage({Key key, this.title = "Input Test"}) : super(key: key);

  final String title;

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

class _TestPageState extends State<TestPage> {
  int _counter = 0;

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: SafeArea(
          bottom: false,
          child: ListView(
        children: <Widget>[
          Container(
            child: Text(
              'You have pushed the button this many times:',
            ),
            margin: const EdgeInsets.all(8.0),
            alignment: Alignment.center,
          ),
          Container(
            child: Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
            margin: const EdgeInsets.all(8.0),
            alignment: Alignment.center,
          ),
          Container(
            child: TextField(
              minLines: 2,
              maxLines: 10,
            ),
            padding: const EdgeInsets.all(8.0),
          ),
          TestTextField(),
          Container(
            child: Container(
              color: Colors.red,
              width: double.infinity,
              height: 128.0,
            ),
            padding: const EdgeInsets.all(8.0),
          ),
          Container(
            child: Container(
              color: Colors.orange,
              width: double.infinity,
              height: 128.0,
            ),
            padding: const EdgeInsets.all(8.0),
          ),
          Container(
            child: Container(
              color: Colors.green,
              width: double.infinity,
              height: 128.0,
            ),
            padding: const EdgeInsets.all(8.0),
          ),
          Container(
            child: Container(
              color: Colors.blue,
              width: double.infinity,
              height: 128.0,
            ),
            padding: const EdgeInsets.all(8.0),
          ),
          Container(
            child: Container(
              color: Colors.yellow,
              width: double.infinity,
              height: 128.0,
            ),
            padding: const EdgeInsets.all(8.0),
          ),
          Container(
            child: TextField(
              minLines: 2,
              maxLines: 10,
            ),
            padding: const EdgeInsets.all(8.0),
          ),
          TestTextField(),
        ],
      )),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

class TestTextField extends StatefulWidget {
  @override
  _TestTextFieldState createState() => _TestTextFieldState();
}

class _TestTextFieldState extends State<TestTextField> {
  FocusNode _node;
  PersistentBottomSheetController _controller;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _node = FocusNode();
    _node.addListener(() {
      if (_node.hasFocus) {
        print('showBottomSheet');
        _controller = Scaffold.of(context)
            .showBottomSheet<dynamic>((BuildContext ctx) => Container(
                  width: double.infinity,
                  height: 36.0,
                  color: Colors.deepPurple,
                ));
      } else {
        if (_controller != null) {
          //Navigator.of(context).pop();
          print('closeBottomSheet');
          _controller.close();
        }
        _controller = null;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: TextField(
        minLines: 2,
        maxLines: 10,
        focusNode: _node,
      ),
      padding: const EdgeInsets.all(8.0),
    );
  }
}
复制代码

 

iOS podfile 加入如下代码

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
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
 
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
 
project 'Runner', {
  'Debug' => :debug,
  'Profile' => :release,
  'Release' => :release,
}
 
def parse_KV_file(file, separator='=')
  file_abs_path = File.expand_path(file)
  if !File.exists? file_abs_path
    return [];
  end
  generated_key_values = {}
  skip_line_start_symbols = ["#", "/"]
  File.foreach(file_abs_path) do |line|
    next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
    plugin = line.split(pattern=separator)
    if plugin.length == 2
      podname = plugin[0].strip()
      path = plugin[1].strip()
      podpath = File.expand_path("#{path}", file_abs_path)
      generated_key_values[podname] = podpath
    else
      puts "Invalid plugin specification: #{line}"
    end
  end
  generated_key_values
end
 
target 'Runner' do
  use_frameworks!
  use_modular_headers!
 
  # Flutter Pod
 
  copied_flutter_dir = File.join(__dir__, 'Flutter')
  copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
  copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
  unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
    # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
    # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
    # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
 
    generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
    unless File.exist?(generated_xcode_build_settings_path)
      raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
    end
    generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
    cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR'];
 
    unless File.exist?(copied_framework_path)
      FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
    end
    unless File.exist?(copied_podspec_path)
      FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
    end
  end
 
  # Keep pod path relative so it can be checked into Podfile.lock.
  pod 'Flutter', :path => 'Flutter'
 
  # Plugin Pods
 
  # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
  # referring to absolute paths on developers' machines.
  system('rm -rf .symlinks')
  system('mkdir -p .symlinks/plugins')
  plugin_pods = parse_KV_file('../.flutter-plugins')
  plugin_pods.each do |name, path|
    symlink = File.join('.symlinks', 'plugins', name)
    File.symlink(path, symlink)
    pod name, :path => File.join(symlink, 'ios')
  end
end
 
# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
install! 'cocoapods', :disable_input_output_paths => true
 
post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['ENABLE_BITCODE'] = 'NO'
    end
  end
end

  

iOS  

PlatformRouterImp.swift

 

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
//
//  ViewController.swift
 
import UIKit
 
class ViewController: UIViewController {
 
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
 
    @IBAction func onClickPushFlutterPage(_ sender: UIButton, forEvent event: UIEvent){
        FlutterBoostPlugin.open("first", urlParams:[kPageCallBackId:"MycallbackId#1"], exts: ["animated":true], onPageFinished: { (_ result:Any?) in
            print(String(format:"call me when page finished, and your result is:%@", result as! CVarArg));
        }) { (f:Bool) in
            print(String(format:"page is opened"));
        }
    }
    @IBAction func onClickPresentFlutterPage(_ sender: UIButton, forEvent event: UIEvent){
        FlutterBoostPlugin.present("second", urlParams:[kPageCallBackId:"MycallbackId#2"], exts: ["animated":true], onPageFinished: { (_ result:Any?) in
            print(String(format:"call me when page finished, and your result is:%@", result as! CVarArg));
        }) { (f:Bool) in
            print(String(format:"page is presented"));
        }
    }
}

  

  

  

posted on   youhui  阅读(732)  评论(0编辑  收藏  举报

编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示