Skip to content

v1: Web Multi-View - embedding Flet web app into existing web page #5274

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

Merged
merged 31 commits into from
May 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2369b8b
Add flet_audio, flet_video extensions to Flet Client
FeodorFitsner Apr 30, 2025
a818f38
Multi-view draft
FeodorFitsner May 1, 2025
1d00daa
MultiView - more code!
FeodorFitsner May 2, 2025
569a4cb
Merge branch 'v1' into feodor/v1-multi-view
FeodorFitsner May 2, 2025
f5c98d1
Before Page split
FeodorFitsner May 2, 2025
1db8d4e
Page split into Page and PageView
FeodorFitsner May 3, 2025
b00a35a
Do not re-add multi-views
FeodorFitsner May 3, 2025
e912f06
FletApp.multiView
FeodorFitsner May 3, 2025
03c4560
Multi-view - almost done
FeodorFitsner May 3, 2025
f4c7f90
Flet web settings are all in JavaScript
FeodorFitsner May 3, 2025
9e1f344
use_color_emoji has gone
FeodorFitsner May 3, 2025
10cae16
Fix tests
FeodorFitsner May 3, 2025
3cc1072
Fix test again
FeodorFitsner May 3, 2025
15fa9bc
page.on_close is back
FeodorFitsner May 4, 2025
ccf8fb6
Cleanup, typing
FeodorFitsner May 4, 2025
6bc887c
FilePicker and uploads
FeodorFitsner May 4, 2025
e4803f0
Image.src_bytes, DecorationBox.src_bytes
FeodorFitsner May 4, 2025
e018883
Fix Map<String, dynamic> everywhere
FeodorFitsner May 5, 2025
77a1817
Fix object patcher: ignore "data" fields
FeodorFitsner May 5, 2025
642b26e
Added key to all stateful control widgets
FeodorFitsner May 5, 2025
cdfad8f
Fix desktop lagginess of CupertinoPicker
FeodorFitsner May 5, 2025
e7ba20f
Fixed DragTarget
FeodorFitsner May 5, 2025
3b93f54
DragTarget improved/simplified
FeodorFitsner May 5, 2025
d8bb7f3
Merge
FeodorFitsner May 5, 2025
ff756fb
Fix ResponsiveRow
FeodorFitsner May 5, 2025
2d8abb4
Autocomplete.selected_index
FeodorFitsner May 6, 2025
2a578bc
revert __init__.py - before
FeodorFitsner May 6, 2025
de6f21b
Fix multiple __init__.py
FeodorFitsner May 6, 2025
6003393
Add missing to __all__
FeodorFitsner May 6, 2025
82c7ffc
FilePicker method typings
FeodorFitsner May 6, 2025
5cc9332
Fix typings and defaults in serve_fastapi_web_app.py
FeodorFitsner May 6, 2025
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
12 changes: 10 additions & 2 deletions client/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,21 @@ void main([List<String>? args]) async {
};
}

runApp(FletApp(
var app = FletApp(
title: 'Flet',
pageUrl: pageUrl,
assetsDir: assetsDir,
errorsHandler: errorsHandler,
showAppStartupScreen: true,
appStartupScreenMessage: "Working...",
extensions: extensions,
));
multiView: isMultiView(),
);

if (app.multiView) {
debugPrint("Flet Web Multi-View mode");
runWidget(app);
} else {
runApp(app);
}
}
23 changes: 15 additions & 8 deletions client/web/flutter_bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,31 @@
{{flutter_build_config}}

var loading = document.querySelector('#loading');
var config = {};
if (webRenderer != "auto") {
config.renderer = webRenderer;

var flutterConfig = {
multiViewEnabled: flet.multiView,
assetBase: flet.assetBase
};
if (flet.webRenderer != "auto") {
flutterConfig.renderer = flet.webRenderer;
}
if (globalThis.canvasKitBaseUrl) {
config.canvasKitBaseUrl = globalThis.canvasKitBaseUrl;
if (flet.noCdn) {
flutterConfig.canvasKitBaseUrl = flet.canvasKitBaseUrl;
flutterConfig.fontFallbackBaseUrl = flet.fontFallbackBaseUrl;
}

_flutter.loader.load({
config: config,
config: flutterConfig,
serviceWorkerSettings: {
serviceWorkerVersion: {{flutter_service_worker_version}},
},
onEntrypointLoaded: async function (engineInitializer) {
loading.classList.add('main_done');
const appRunner = await engineInitializer.initializeEngine({useColorEmoji: useColorEmoji});
const engine = await engineInitializer.initializeEngine(flutterConfig);

loading.classList.add('init_done');
await appRunner.runApp();
flet.flutterApp = await engine.runApp();
flet.flutterAppResolve(flet.flutterApp);

window.setTimeout(function () {
loading.remove();
Expand Down
74 changes: 54 additions & 20 deletions client/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,62 @@
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png" />

<!-- Flet specific -->
<meta name="flet-route-url-strategy" content="%FLET_ROUTE_URL_STRATEGY%">
<meta name="flet-web-pyodide" content="%FLET_WEB_PYODIDE%">
<meta name="flet-websocket-endpoint-path" content="/ws">

<title>Flet</title>
<link rel="manifest" href="manifest.json">

<script>
var webRenderer = "auto";
var useColorEmoji = false;
var appPackageUrl = "app.tar.gz";
var noCdn = false;
</script>

<!-- webRenderer -->
<!-- useColorEmoji -->
<!-- pyodideCode -->
<!-- noCdn -->

<script>
if (noCdn) {
var canvasKitBaseUrl = "/canvaskit/";
var pyodideUrl = "/pyodide/pyodide.js";
var flet = {
pyodide: false,
multiView: false,
noCdn: false,
webSocketEndpoint: "/ws",
assetBase: "/",
routeUrlStrategy: "path",
canvasKitBaseUrl: "/canvaskit/",
pyodideUrl: "/pyodide/pyodide.js",
webRenderer: "auto",
fontFallbackBaseUrl: "assets/fonts/", // for Noto Emoji, use Google CDN
appPackageUrl: "app.tar.gz"
}

flet.flutterAppLoaded = new Promise((resolve) => {
flet.flutterAppResolve = resolve;
});

flet.flutterAppLoaded.then((flutterApp) => {
console.log("Flutter app loaded");
if (flet.multiView) {
console.log(flutterApp.addView({
hostElement: document.querySelector('#view-1'),
initialData: {
greeting: 'Hello, world!',
randomValue: Math.floor(Math.random() * 100),
},
viewConstraints: {
//maxWidth: Infinity,
maxHeight: Infinity,
minHeight: 300,
minWidth: 620
}
}));
console.log(flutterApp.addView({
hostElement: document.querySelector('#view-2'),
initialData: {
bar: "baz"
},
viewConstraints: {
minWidth: 620,
//maxWidth: 320,
Copy link
Preview

Copilot AI May 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] If the commented-out view constraint is no longer required, consider removing it to improve code clarity; otherwise, add a clarifying comment explaining its purpose.

Suggested change
//maxWidth: 320,

Copilot uses AI. Check for mistakes.

//minHeight: 400,
maxHeight: 400,
}
}));
}
});
</script>

<!-- fletAppConfig -->

<script src="python.js"></script>
</head>

Expand Down Expand Up @@ -106,5 +135,10 @@
<img src="icons/loading-animation.png" alt="Loading..." />
</div>
<script src="flutter_bootstrap.js" async></script>

<h1>View 1</h1>
<div id="view-1" style="display: flex; justify-content: center;"></div>
<h1>View 2</h1>
<div id="view-2"></div>
</body>
</html>
8 changes: 4 additions & 4 deletions client/web/python.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ globalThis.jsConnect = async function(appId, args, dartOnMessage) {

// initialize worker
app.worker.postMessage({
pyodideUrl: globalThis.pyodideUrl ?? defaultPyodideUrl,
pyodideUrl: flet.noCdn ? flet.pyodideUrl : defaultPyodideUrl,
args: args,
documentUrl: _documentUrl,
appPackageUrl: globalThis.appPackageUrl,
micropipIncludePre: globalThis.micropipIncludePre,
pythonModuleName: globalThis.pythonModuleName
appPackageUrl: flet.appPackageUrl,
micropipIncludePre: flet.micropipIncludePre,
pythonModuleName: flet.pythonModuleName
});

await pythonInitialized;
Expand Down
1 change: 0 additions & 1 deletion packages/flet/lib/flet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export 'src/flet_extension.dart';
export 'src/flet_service.dart';
export 'src/models/asset_source.dart';
export 'src/models/control.dart';
export 'src/models/page_args_model.dart';
export 'src/models/page_size_view_model.dart';
export 'src/utils.dart';
export 'src/utils/alignment.dart';
Expand Down
3 changes: 2 additions & 1 deletion packages/flet/lib/src/controls/alert_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import 'control_widget.dart';
class AlertDialogControl extends StatefulWidget {
final Control control;

const AlertDialogControl({super.key, required this.control});
AlertDialogControl({Key? key, required this.control})
: super(key: ValueKey("control_${control.id}"));

@override
State<AlertDialogControl> createState() => _AlertDialogControlState();
Expand Down
7 changes: 3 additions & 4 deletions packages/flet/lib/src/controls/auto_complete.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@ class AutoCompleteControl extends StatelessWidget {
var autoComplete = Autocomplete(
optionsMaxHeight: control.getDouble("suggestions_max_height", 200)!,
onSelected: (AutoCompleteSuggestion selection) {
control.triggerEvent("select", {
"selection": selection.toMap(),
"selection_index": suggestions.indexOf(selection)
});
control.updateProperties(
{"_selected_index": suggestions.indexOf(selection)});
control.triggerEvent("select", {"selection": selection.toMap()});
},
// optionsViewBuilder: optionsViewBuilder,
optionsBuilder: (TextEditingValue textEditingValue) {
Expand Down
2 changes: 1 addition & 1 deletion packages/flet/lib/src/controls/banner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class BannerControl extends StatefulWidget {
final Control control;

BannerControl({Key? key, required this.control})
: super(key: ValueKey(control.id));
: super(key: ValueKey("control_${control.id}"));

@override
State<BannerControl> createState() => _BannerControlState();
Expand Down
3 changes: 2 additions & 1 deletion packages/flet/lib/src/controls/bar_chart.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ class BarChartEventData extends Equatable {
class BarChartControl extends StatefulWidget {
final Control control;

const BarChartControl({super.key, required this.control});
BarChartControl({Key? key, required this.control})
: super(key: ValueKey("control_${control.id}"));

@override
State<BarChartControl> createState() => _BarChartControlState();
Expand Down
3 changes: 2 additions & 1 deletion packages/flet/lib/src/controls/bottom_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import '../widgets/error.dart';
class BottomSheetControl extends StatefulWidget {
final Control control;

const BottomSheetControl({super.key, required this.control});
BottomSheetControl({Key? key, required this.control})
: super(key: ValueKey("control_${control.id}"));

@override
State<BottomSheetControl> createState() => _BottomSheetControlState();
Expand Down
6 changes: 2 additions & 4 deletions packages/flet/lib/src/controls/button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import 'package:flutter/material.dart';
class ButtonControl extends StatefulWidget {
final Control control;

const ButtonControl({
super.key,
required this.control,
});
ButtonControl({Key? key, required this.control})
: super(key: ValueKey("control_${control.id}"));

@override
State<ButtonControl> createState() => _ButtonControlState();
Expand Down
3 changes: 2 additions & 1 deletion packages/flet/lib/src/controls/canvas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ typedef CanvasControlOnPaintCallback = void Function(Size size);
class CanvasControl extends StatefulWidget {
final Control control;

const CanvasControl({super.key, required this.control});
CanvasControl({Key? key, required this.control})
: super(key: ValueKey("control_${control.id}"));

@override
State<CanvasControl> createState() => _CanvasControlState();
Expand Down
3 changes: 2 additions & 1 deletion packages/flet/lib/src/controls/checkbox.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import 'list_tile.dart';
class CheckboxControl extends StatefulWidget {
final Control control;

const CheckboxControl({super.key, required this.control});
CheckboxControl({Key? key, required this.control})
: super(key: ValueKey("control_${control.id}"));

@override
State<CheckboxControl> createState() => _CheckboxControlState();
Expand Down
3 changes: 2 additions & 1 deletion packages/flet/lib/src/controls/chip.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import 'base_controls.dart';
class ChipControl extends StatefulWidget {
final Control control;

const ChipControl({super.key, required this.control});
ChipControl({Key? key, required this.control})
: super(key: ValueKey("control_${control.id}"));

@override
State<ChipControl> createState() => _ChipControlState();
Expand Down
Loading