Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions docs/docs/languages/flutter.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,31 @@ class _CounterAppState extends State<CounterApp> {
}
```

### Root Element

Flutter draws its own UI, so it renders into an element in the [result page](../features/result.mdx).
Use `"livecodes-app"` as the element `id` to render into a specific element. Otherwise a `div` element is added to `document.body` and rendered into.

The app is sized to that element's box, so the element must have a size. An empty `div` in normal flow has none, and `width`/`height` in percent resolve against an unsized body.

Example:

export const flutterRootDemo = {
activeEditor: 'script',
script: {
language: 'flutter',
content: `import 'package:flutter/material.dart';\n\nvoid main() {\n runApp(\n const Center(\n child: Text('Hello, World!', textDirection: TextDirection.ltr),\n ),\n );\n}`,
},
markup: {
language: 'html',
content: `<h1>Custom Root Element</h1>
<div id="livecodes-app" style="width: 300px; height: 240px; border: 1px solid #888; margin: auto"></div>
`,
},
};

<LiveCodes config={flutterRootDemo} height="80vh"></LiveCodes>

## Language Info

### Name
Expand Down
34 changes: 29 additions & 5 deletions src/livecodes/languages/dart/lang-dart-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,42 @@ export const setupDartRuntime = (engine: 'dart' | 'flutter') => {

const postLoading = (payload: boolean) => parent.postMessage({ type: 'loading', payload }, '*'); // NOSONAR - the parent validates the source.

ns.ready ??= (async () => {
ns.runtime = await window.DartWasm.loadRuntime({ engine, baseUrl: dartWasmBaseUrl });
return ns.runtime;
})();
// Flutter draws its own UI, so the engine needs an element to draw into. LiveCodes' convention is
// a `#livecodes-app` element from the HTML tab when there is one, otherwise a `div` added to the
// page. The Dart engine runs in the page itself and needs no container.
const flutterContainer = () => {
const existing = document.querySelector<HTMLElement>('#livecodes-app');
if (existing) return existing;
const container = document.createElement('div');
// The engine sizes to the host element, so it has to fill the result pane the way the engine's
// own full-page host would have.
Object.assign(container.style, {
position: 'fixed',
inset: '0',
overflow: 'hidden',
});
return document.body.appendChild(container);
};

// Created on the first run, not while this script is parsed: the result page's markup (and so
// `#livecodes-app`) does not exist yet when the head scripts run.
const ready = () =>
(ns.ready ??= (async () => {
ns.runtime = await window.DartWasm.loadRuntime({
engine,
baseUrl: dartWasmBaseUrl,
...(engine === 'flutter' ? { container: flutterContainer() } : {}),
});
return ns.runtime;
})());

ns.run ??= async (program: {
libraryUri: string;
modules: Array<{ name: string; code: string }>;
}) => {
postLoading(true);
try {
const runtime = await ns.ready;
const runtime = await ready();
await runtime.run(program);
} catch (error) {
// eslint-disable-next-line no-console
Expand Down
2 changes: 1 addition & 1 deletion src/livecodes/vendors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export const cytoscapeSvgUrl = /* @__PURE__ */ getUrl('cytoscape-svg@0.4.0/cytos

export const cytoscapeUrl = /* @__PURE__ */ getUrl('cytoscape@3.25.0/dist/cytoscape.min.js');

export const dartWasmBaseUrl = /* @__PURE__ */ getUrl('@live-codes/dart-wasm@0.6.0/dist/');
export const dartWasmBaseUrl = /* @__PURE__ */ getUrl('@live-codes/dart-wasm@0.7.0/dist/');

export const ddietrCmThemesBaseUrl = /* @__PURE__ */ getUrl(
'@ddietr/codemirror-themes@1.4.2/dist/theme/',
Expand Down
Loading