Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[web] Update web_embedding sample with multi-view API. #2436

Open
arnleal09 opened this issue Sep 12, 2024 · 2 comments
Open

[web] Update web_embedding sample with multi-view API. #2436

arnleal09 opened this issue Sep 12, 2024 · 2 comments
Assignees
Labels
enhancement New feature or request p2 tech-debt

Comments

@arnleal09
Copy link

arnleal09 commented Sep 12, 2024

From the current documentation, it's not clear how to use the multi-view API in a higher-level framework, like Angular in our web_embedding demo.

We must update the web_embedding sample for Angular:

  • Update angular pin and deps
  • Update flutter and deps
  • Update the sample for multi-view mode (encapsulate app.addView and app.removeView in the Angular element lifecycle)

Original issue follows

Multiple embedded views on the same page

I am using the example of rendering embebed Flutter widgets in a React web page with NextJs. When I add two on the same page, only one of the views renders. Using print, I can see that the flutter ViewCollection does indeed contain both views, and the target has two different elements with distinct IDs. However, the second view is always the one that gets displayed. There is any limitation to these use? I am using flutter version 3.24.2

<FlutterView
                    key={´1´}
                    className={´1´}
                    flutterApp={flutterApp}
                    onClicksChange={setClicks}
                    onScreenChange={setScreen}
                    onTextChange={setText}
                    removeView={() => removeView(1)}
                    text={text}
                    clicks={clicks}
                    screen={screen}
                  /> 

other elements

<FlutterView
                    key={´2´}
                    className={´2´}
                    flutterApp={flutterApp}
                    onClicksChange={setClicks}
                    onScreenChange={setScreen2}
                    onTextChange={setText}
                    removeView={() => removeView(2)}
                    text={text}
                    clicks={clicks}
                    screen={screen2}
                  />
<!DOCTYPE html>
<html>

<head>
  <!--
    If you are serving your web app in a path other than the root, change the
    href value below to reflect the base path you are serving from.

    The path provided below has to start and end with a slash "/" in order for
    it to work correctly.

    For more details:
    * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base

    This is a placeholder for base href that will be replaced by the value of
    the `--base-href` argument provided to `flutter build`.
  -->
  <script id="googleMapScript"
    src="https://maps.googleapis.com/maps/api/js?key=xxx"></script>
</head>
<base href="$FLUTTER_BASE_HREF">

<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">

<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="app">
<link rel="apple-touch-icon" href="icons/Icon-192.png">

<!-- Favicon -->
<link rel="icon" type="icons" href="favicon.ico" />

<title>plataforma_revendedora_mobile</title>
<link rel="manifest" href="manifest.json">
<script>
  // The value below is injected by flutter build, do not touch.
  var serviceWorkerVersion = '{{flutter_service_worker_version}}';
</script>
<script src="flutter.js" defer></script>
<style>
  html,
  body {
    height: 100%;
    padding: 0;
    margin: 0;
  }
</style>
</head>

<body>

  <script>
    // Listen until Flutter tells us it's ready to rumble
    window.addEventListener('flutter-initialized', function (event) {
      const state = event.detail;
      window['_debugCounter'] = state;
      state.onClicksChanged(() => {
        console.log('New clicks value: ', state.getClicks());
      });
    });
    window.addEventListener('load', function (ev) {
      // Download main.dart.js
      {{flutter_js}}
      {{flutter_build_config}}

      _flutter.loader.load({
        serviceWorker: {
          serviceWorkerVersion: serviceWorkerVersion,
        },
        onEntrypointLoaded: async function (engineInitializer) {
          engineInitializer.initializeEngine({
            multiViewEnabled: true,
          }).then(function (appRunner) {
            appRunner.runApp().then(function (flutterApp) {
              flutterApp.addView({ hostElement: document.querySelector("body") });
            });
          });
        }
      });
    });
  </script>
</body>

</html>

I am making the flutter init in this way and passing the context to the root of all components. When i do navigation for another page the widgets render ok, the only problem is when are two in the same page.

import React, { createContext, useContext, useState, useEffect } from 'react';
declare var _flutter: any;

interface FlutterAppContextProps { flutterApp: any; }

const FlutterAppContext = createContext<FlutterAppContextProps | undefined>(undefined);

export const useFlutterApp = () => {
    const context = useContext(FlutterAppContext);
    if (!context) { throw new Error('useFlutterApp must be used within a FlutterAppProvider'); } return context;
};
export const FlutterAppProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
    const [flutterApp, setFlutterApp] = useState(null);
    useEffect(() => {
        const initializeFlutter = async () => {
            try {
                console.log('setup Flutter engine initializer...');
                const engineInitializer = await new Promise<any>((resolve, reject) => {
                    if (_flutter && _flutter.loader) {
                        console.log('flutter loaded value');
                        console.log(_flutter);
                        _flutter.loader.loadEntrypoint({ entrypointUrl: 'https://url/main.dart.js', onEntrypointLoaded: resolve, });
                    }
                    else { reject(new Error('_flutter.loader not available')); }
                });
                const appRunner = await engineInitializer?.initializeEngine({ assetBase: 'https://url/', multiViewEnabled: true, });
                console.log('Flutter initialized:', appRunner);
                const flutterAppInstance = await appRunner.runApp();
                setFlutterApp(flutterAppInstance); console.log('Flutter initialized:', flutterAppInstance);
            } catch (error) { console.error('Error initializing Flutter:', error); }
        };
        initializeFlutter();
    }, []);
    return (<FlutterAppContext.Provider value={{ flutterApp }}> {children} </FlutterAppContext.Provider>);
};
@parlough
Copy link
Member

parlough commented Sep 13, 2024

\cc @ditman Any sage advice you can offer here? Thanks :)

@ditman
Copy link
Member

ditman commented Sep 13, 2024

From a cursory look at the code (there isn't enough to figure out exactly what's going on), @arnleal09, it seems that the flutter initialization is a bit funky:

  • On your index, there's a call to _flutter.loader.load where only one view is added: the body element
    • Also, do not use flutter.js directly, use flutter_bootstrap.js. Docs.
  • On your react code, there's a call to _flutter.loader.loadEntrypoint that won't set Flutter in multi-view mode.

IMO you should start by a smaller app (maybe the ReactJS one here), and build up to add multi-view support.

PS: I'm hijacking this issue to remember ourselves to update the web_embedding samples to work in multi-view mode, thanks for reporting @arnleal09!

@ditman ditman changed the title Multiple embedded views on the same page [web] Update web_embedding sample with multi-view API. Sep 13, 2024
@ditman ditman self-assigned this Sep 13, 2024
@ditman ditman added enhancement New feature or request p2 tech-debt labels Sep 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request p2 tech-debt
Projects
None yet
Development

No branches or pull requests

3 participants