FlutterBoost

repository·main·Indexed 27 days ago

https://github.com/alibaba/flutter_boost

A hybrid solution for integrating Flutter into existing native Android, iOS, and HarmonyOS applications. It provides a unified routing mechanism to simplify the management of native and Flutter page stacks, along with a custom event passing API (BoostChannel) for communication between Flutter and native platforms.

Tokens
13.3K
Snippets
27
Records
44
Agent score
92%

What's inside flutter_boost

  1. Overview of FlutterBoost

    main
    FlutterBoost is a next-generation Flutter-Native hybrid solution. It is a Flutter plugin designed to provide easy hybrid integration for existing native applications. The core philosophy is to treat Flutter pages like WebViews, where FlutterBoost manages the mapping and navigation between Native and Flutter pages. Developers can focus on page names and parameters (similar to URLs) rather than the complexities of managing mixed page stacks.
  2. Register a global lifecycle observer

    main

    To monitor lifecycle events across the entire application, implement the GlobalPageVisibilityObserver mixin and register it using PageVisibilityBinding.instance.addGlobalObserver. This is typically done in the main() function.

    Available lifecycle methods to override:

    • onBackground(Route route)
    • onForeground(Route route)
    • onPagePush(Route route)
    • onPagePop(Route route)
    • onPageHide(Route route)
    • onPageShow(Route route)
    void main() {
      /// Add global lifecycle observer
      PageVisibilityBinding.instance.addGlobalObserver(AppLifecycleObserver());
      runApp(MyApp());
    }
    
    /// Example implementation of a global observer
    class AppLifecycleObserver with GlobalPageVisibilityObserver {
      @override
      void onBackground(Route route) {
        super.onBackground(route);
        print("AppLifecycleObserver - onBackground");
      }
    
      @override
      void onForeground(Route route) {
        super.onForeground(route);
        print("AppLifecycleObserver - onForground");
      }
    
      @override
      void onPagePush(Route route) {
        super.onPagePush(route);
        print("AppLifecycleObserver - onPagePush");
      }
    
      @override
      void onPagePop(Route route) {
        super.onPagePop(route);
        print("AppLifecycleObserver - onPagePop");
      }
    
      @override
      void onPageHide(Route route) {
        super.onPageHide(route);
        print("AppLifecycleObserver - onPageHide");
      }
    
      @override
      void onPageShow(Route route) {
        super.onPageShow(route);
        print("AppLifecycleObserver - onPageShow");
      }
    }
  3. Implement Custom Flutter Binding and FlutterBoostApp

    main

    To integrate FlutterBoost into your Dart code, you need to:

    1. Create a CustomFlutterBinding that mixes in BoostFlutterBinding.
    2. Use FlutterBoostApp as the root widget instead of a standard MaterialApp or CupertinoApp.
    3. Provide a routeFactory to handle route creation based on RouteSettings.
    4. Provide an appBuilder (optional) to wrap the home widget, ensuring the builder parameter is set to avoid issues with showDialog.
    import 'package:flutter/cupertino.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter_boost/flutter_boost.dart';
    
    void main() {
      // Must call this to control Boost status (resume/pause)
      CustomFlutterBinding();
      runApp(MyApp());
    }
    
    // Custom binding implementation
    class CustomFlutterBinding extends WidgetsFlutterBinding with BoostFlutterBinding {}
    
    class MyApp extends StatefulWidget {
      @override
      _MyAppState createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      // Map route names to factory functions
      Map<String, FlutterBoostRouteFactory> routerMap = {
        'mainPage': (RouteSettings settings, String uniqueId) {
          return CupertinoPageRoute(
              settings: settings,
              builder: (_) {
                Map<String, Object> map = settings.arguments as Map<String, Object> ;
                String data = map['data'] as String;
                return MainPage(data: data);
              });
        },
        // ... other routes
      };
    
      Route<dynamic> routeFactory(RouteSettings settings, String uniqueId) {
        FlutterBoostRouteFactory func = routerMap[settings.name] as FlutterBoostRouteFactory;
        return func(settings, uniqueId);
      }
    
      Widget appBuilder(Widget home) {
        return MaterialApp(
          home: home,
          builder: (_, __) {
            return home;
          },
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return FlutterBoostApp(
          routeFactory,
          appBuilder: appBuilder,
        );
      }
    }
  4. Install FlutterBoost on Android

    main

    Follow these steps to integrate FlutterBoost into an Android project:

    1. Configure settings.gradle: Reference the Flutter module by adding the following code to include the .android directory and the module project:

    2. Update app/build.gradle: Add the dependencies for the flutter module and the flutter_boost project:

    3. Update AndroidManifest.xml: Add FlutterBoostActivity and the flutterEmbedding meta-data inside the <application> tag.

    4. Initialize in Application class: Call FlutterBoost.instance().setup() in onCreate and implement the FlutterBoostDelegate to handle pushNativeRoute and pushFlutterRoute.

    // settings.gradle
    setBinding(new Binding([gradle: this]))
    evaluate(new File(
            settingsDir.parentFile,
            'flutter_module/.android/include_flutter.groovy'
    ))
    include ':flutter_module'
    project(':flutter_module').projectDir = new File('../flutter_module')
    // app/build.gradle
    implementation project(':flutter')
    implementation project(':flutter_boost')
    <!-- AndroidManifest.xml -->
    <activity
            android:name="com.idlefish.flutterboost.containers.FlutterBoostActivity"
            android:theme="@style/Theme.AppCompat"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize" >
    </activity>
    <meta-data android:name="flutterEmbedding"
               android:value="2">
    </meta-data>
    // Application.java
    public class App extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
            FlutterBoost.instance().setup(this, new FlutterBoostDelegate() {
                @Override
                public void pushNativeRoute(FlutterBoostRouteOptions options) {
                    Intent intent = new Intent(FlutterBoost.instance().currentActivity(), YourTargetAcitvity.class);
                    FlutterBoost.instance().currentActivity().startActivityForResult(intent, options.requestCode());
                }
    
                @Override
                public void pushFlutterRoute(FlutterBoostRouteOptions options) {
                    Intent intent = new Intent(FlutterBoost.instance().currentActivity(), FlutterBoostActivity.class);
                    // Use CachedEngineIntentBuilder for more complex configurations
                    Intent detailedIntent = new FlutterBoostActivity.CachedEngineIntentBuilder(FlutterBoostActivity.class)
                            .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
                            .destroyEngineWithActivity(false)
                            .uniqueId(options.uniqueId())
                            .url(options.pageName())
                            .urlParams(options.arguments())
                            .build(FlutterBoost.instance().currentActivity());
                    FlutterBoost.instance().currentActivity().startActivity(detailedIntent);
                }
            }, engine -> {
            });
        }
    }
  5. Create a Custom FlutterPage in OHOS

    main

    To host a Flutter view, create a component (e.g., MyFlutterPage) that manages a FlutterBoostEntry.

    Key steps:

    1. In aboutToAppear, initialize FlutterBoostEntry using getContext(this) and router.getParams(). Call flutterEntry.aboutToAppear() and retrieve the view via flutterEntry.getFlutterView().
    2. Implement lifecycle forwarding: call flutterEntry.onPageShow(), onPageHide(), and aboutToDisappear() within the corresponding component lifecycle methods.
    3. In the build method, use the FlutterPage component passing the viewId from the flutterView.
    4. To handle back button interception, call FlutterBoost.getInstance().getPlugin()?.onBackPressed() in onBackPress().
    @Entry
    @Component
    struct MyFlutterPage {
      private flutterEntry: FlutterBoostEntry | null = null;
      private flutterView?: FlutterView
    
      aboutToAppear() {
        this.flutterEntry = new FlutterBoostEntry(getContext(this), router.getParams());
        this.flutterEntry.aboutToAppear();
        this.flutterView = this.flutterEntry.getFlutterView();
        hilog.info(0x0000, "Flutter", "Index aboutToAppear===");
      }
      
      aboutToDisappear() {
        hilog.info(0x0000, "Flutter", "Index aboutToDisappear===");
        this.flutterEntry?.aboutToDisappear()
      }
    
      onPageShow() {
        hilog.info(0x0000, "Flutter", "Index onPageShow===");
        this.flutterEntry?.onPageShow()
      }
    
      onPageHide() {
        hilog.info(0x0000, "Flutter", "Index onPageHide===");
        this.flutterEntry?.onPageHide()
      }
    
      build() {
        Stack() {
          FlutterPage({ viewId: this.flutterView?.getId() })
        }
      }
    
      // Intercept back key
      onBackPress(): boolean | void {
        FlutterBoost.getInstance().getPlugin()?.onBackPressed();
        return true;
      }
    }
  6. Initialize FlutterBoost on OHOS

    main

    To initialize FlutterBoost, your UIAbility should implement FlutterBoostDelegate. You must call FlutterBoost.getInstance().setup (ideally within onWindowStageCreate).

    It is highly recommended to include GeneratedPluginRegistrant.getPlugins() in the optionsBuilder to ensure Flutter plugins are correctly registered. For a version that returns a Promise, use FlutterBoost.getInstance().setupSync.

    export default class EntryAbility extends UIAbility implements FlutterBoostDelegate {
      // FlutterBoostDelegate override
      pushNativeRoute(options: FlutterBoostRouteOptions) {
    
      }
      // FlutterBoostDelegate override
      pushFlutterRoute(options: FlutterBoostRouteOptions) {
    
        router.pushUrl({
          url: 'pages/MyFlutterPage', params: {
            uri: options.getPageName(),
            params: options.getArguments(),
          },
        }).then(() => {
          console.info('Succeeded in jumping to the second page.')
        })
      }
      onWindowStageCreate(windowStage: window.WindowStage): void {
    
        // Initialize startup options (recommended to include GeneratedPluginRegistrant.getPlugins())
        const optionsBuilder: FlutterBoostSetupOptionsBuilder = new FlutterBoostSetupOptionsBuilder()
          .setPlugins(GeneratedPluginRegistrant.getPlugins());
    
        FlutterBoost.getInstance().setup(this, this.context, () => {
          // Engine initialized successfully
        }, optionsBuilder.build())
      }
    }
  7. Support Multiple Flutter Instances in Tabs (OHOS)

    main

    To allow multiple Flutter instances to coexist across different tabs in an OHOS application:

    1. Maintain an array of FlutterBoostEntry objects in your parent component.
    2. In aboutToAppear, initialize each FlutterBoostEntry with its respective uri and store them in the array.
    3. In the Tabs component's onChange listener, manually manage the lifecycle of the entries. When switching tabs, call aboutToAppear() and onPageShow() for the entering entry, and onPageHide() for the exiting entry.
    4. In the tab content rendering, use FlutterPage({ viewId: entry.getFlutterView()?.getId() }) to display the specific instance.
    // Inside a Tabs component onChange
    .onChange((index: number) => {
      this.currentIndex = index;
    
      if (this.currentIndex == 1) {
        this.flutterEntries[0]?.aboutToAppear();
        this.flutterEntries[0]?.onPageShow();
        this.flutterEntries[1]?.onPageHide();
      } else if (this.currentIndex == 2) {
        this.flutterEntries[1]?.aboutToAppear();
        this.flutterEntries[1]?.onPageShow();
        this.flutterEntries[0]?.onPageHide();
      }
    })
  8. Add FlutterBoost dependency to your Flutter project

    main

    To add FlutterBoost to your project, open your pubspec.yaml file and add the following dependency using the specific git reference for version 4.6.5:

    flutter_boost:
        git:
            url: 'https://github.com/alibaba/flutter_boost.git'
            ref: '4.6.5'
  9. Install FlutterBoost via pubspec.yaml

    main

    To add FlutterBoost to your Flutter project, add the following dependency to your pubspec.yaml file. It is recommended to use the specific git reference for the version you require.

    flutter_boost:
        git:
            url: 'https://github.com/alibaba/flutter_boost.git'
            ref: '4.6.5'
  10. Configure screen orientation for Flutter ViewControllers on iOS

    main

    Setting orientation for a FlutterViewController requires coordination between the Native NavigationController and the Dart layer:

    1. Native (Objective-C): Override shouldAutorotate and supportedInterfaceOrientations in your NavigationController to delegate orientation decisions to the top view controller if it is a FlutterViewController.
    -(BOOL)shouldAutorotate
    {
        return YES;
    }
    
    -(UIInterfaceOrientationMask)supportedInterfaceOrientations
    {
        id currentViewController = self.topViewController;
        if ([currentViewController isKindOfClass:[FlutterViewController class]]){ 
             return [currentViewController supportedInterfaceOrientations];
        }
        return UIInterfaceOrientationMaskAll;
    }
    1. Dart: Since SystemChrome.setPreferredOrientations is global and can be overwritten when new FlutterViewControllers are created in a hybrid stack, you must call SystemChrome.setPreferredOrientations within the build method of every Dart page to ensure the desired orientation is maintained.
    #!objc
    -(BOOL)shouldAutorotate
    {
    //    id currentViewController = self.topViewController;
    //
    //     if ([currentViewController isKindOfClass:[FlutterViewController class]])
    //        return [currentViewController shouldAutorotate];
    
        return YES;
    }
    
    -(UIInterfaceOrientationMask)supportedInterfaceOrientations
    {
        id currentViewController = self.topViewController;
        if ([currentViewController isKindOfClass:[FlutterViewController class]]){ 
             NSLog("[XDEBUG]----fvc supported:%ld\n",[currentViewController supportedInterfaceOrientations]);
             return [currentViewController supportedInterfaceOrientations];
        }
        return UIInterfaceOrientationMaskAll;
    }
  11. Initialize FlutterBoost in Android Application

    main

    In your Application class, call FlutterBoost.instance().setup(...). You must implement a FlutterBoostDelegate to handle two types of routing:

    1. pushNativeRoute: Triggered when a Flutter page wants to open a Native activity.
    2. pushFlutterRoute: Triggered when a Native page wants to open a Flutter page. Use FlutterBoostActivity.CachedEngineIntentBuilder to build the intent for Flutter routes.
    public class MyApplication extends FlutterApplication {
        @Override
        public void onCreate() {
            super.onCreate();
            FlutterBoost.instance().setup(this, new FlutterBoostDelegate() {
                @Override
                public void pushNativeRoute(String pageName, HashMap<String, String> arguments) {
                    Intent intent = new Intent(FlutterBoost.instance().currentActivity(), NativePageActivity.class);
                    FlutterBoost.instance().currentActivity().startActivity(intent);
                }
    
                @Override
                public void pushFlutterRoute(String pageName, HashMap<String, String> arguments) {
                    Intent intent = new FlutterBoostActivity.CachedEngineIntentBuilder(FlutterBoostActivity.class, FlutterBoost.ENGINE_ID)
                            .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.opaque)
                            .destroyEngineWithActivity(false)
                            .url(pageName)
                            .urlParams(arguments)
                            .build(FlutterBoost.instance().currentActivity());
                    FlutterBoost.instance().currentActivity().startActivity(intent);
                }
            }, engine -> {
                engine.getPlugins();
            });
        }
    }
  12. Navigate between Flutter and Native (Android)

    main

    In Android, use FlutterBoost.instance() to control page transitions.

    • Open a Flutter page: FlutterBoost.instance().open("pageName", params);
    • Close a Flutter page: FlutterBoost.instance().close("uniqueId");

    Note: Ensure your AndroidManifest.xml is configured with flutterEmbedding=2 and includes the FlutterBoostActivity.