Skip to content

Commit d3b3c5d

Browse files
committed
Add web support to Flutter example
Add the Flutter web scaffold for the `nativeapi_flutter` example and update the sample app to handle web builds safely. Native-only `nativeapi` features are now guarded behind `kIsWeb`, desktop actions are disabled on the web, and the example shows a clear message that the native backend is unavailable there.
1 parent 90180e4 commit d3b3c5d

9 files changed

Lines changed: 149 additions & 55 deletions

File tree

‎bindings/dart/nativeapi_flutter/example/.metadata‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# This file should be version controlled and should not be manually edited.
55

66
version:
7-
revision: "9f455d2486bcb28cad87b062475f42edc959f636"
7+
revision: "5fc346839b5d0eef006ed8404392afb4dfae428d"
88
channel: "stable"
99

1010
project_type: app
@@ -13,11 +13,11 @@ project_type: app
1313
migration:
1414
platforms:
1515
- platform: root
16-
create_revision: 9f455d2486bcb28cad87b062475f42edc959f636
17-
base_revision: 9f455d2486bcb28cad87b062475f42edc959f636
18-
- platform: ios
19-
create_revision: 9f455d2486bcb28cad87b062475f42edc959f636
20-
base_revision: 9f455d2486bcb28cad87b062475f42edc959f636
16+
create_revision: 5fc346839b5d0eef006ed8404392afb4dfae428d
17+
base_revision: 5fc346839b5d0eef006ed8404392afb4dfae428d
18+
- platform: web
19+
create_revision: 5fc346839b5d0eef006ed8404392afb4dfae428d
20+
base_revision: 5fc346839b5d0eef006ed8404392afb4dfae428d
2121

2222
# User provided section
2323

‎bindings/dart/nativeapi_flutter/example/lib/main.dart‎

Lines changed: 62 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// ignore_for_file: avoid_print
22

3+
import 'package:flutter/foundation.dart';
34
import 'package:flutter/material.dart';
45
import 'package:nativeapi_flutter/nativeapi_flutter.dart';
56

@@ -62,15 +63,18 @@ class _MyHomePageState extends State<MyHomePage> {
6263
int _counter = 0;
6364

6465
late final TrayIcon _trayIcon;
65-
late final Menu _menu;
66+
67+
/// Null on the web, where nativeapi has no backend and every call throws.
68+
Menu? _menu;
6669

6770
@override
6871
void initState() {
6972
super.initState();
73+
if (kIsWeb) return;
7074

71-
_menu = Menu.create()!;
75+
final menu = _menu = Menu.create()!;
7276
// One listener per emitter now; the event carries which kind it is.
73-
_menu.addListener((event) {
77+
menu.addListener((event) {
7478
switch (event) {
7579
case MenuOpenedEvent(:final menuId):
7680
print('主菜单打开了!菜单ID: $menuId');
@@ -88,7 +92,7 @@ class _MyHomePageState extends State<MyHomePage> {
8892
print('$label clicked, item ${event.itemId}');
8993
}
9094
});
91-
_menu.addItem(item);
95+
menu.addItem(item);
9296
}
9397
}
9498

@@ -133,12 +137,40 @@ class _MyHomePageState extends State<MyHomePage> {
133137
_trayIcon.setVisible(true);
134138
}
135139

140+
void _showContextMenu() {
141+
final menu = Menu.create()!;
142+
final item1 = MenuItem.createWithLabelAndType(
143+
'Item1',
144+
MenuItemType.normal,
145+
)!;
146+
menu.addItem(item1);
147+
print(item1.label);
148+
final item2 = MenuItem.createWithLabelAndType(
149+
'Item2',
150+
MenuItemType.normal,
151+
)!;
152+
menu.addItem(item2);
153+
print('Context menu shown, item count: ${menu.itemCount}');
154+
menu.open(
155+
PositioningStrategy.absolute(const Offset(100.0, 100.0).toNative())!,
156+
Placement.bottomStart,
157+
);
158+
}
159+
160+
void _openDesktopFeatures() {
161+
Navigator.of(context).push(
162+
MaterialPageRoute<void>(builder: (_) => const DesktopFeaturesPage()),
163+
);
164+
}
165+
136166
void _incrementCounter() {
137-
for (final display in DisplayManager.instance.getAll()) {
138-
print('Display ID: ${display.id}');
139-
print('Display Name: ${display.name}');
140-
print('Display Width: ${display.size.width}');
141-
print('Display Height: ${display.size.height}');
167+
if (!kIsWeb) {
168+
for (final display in DisplayManager.instance.getAll()) {
169+
print('Display ID: ${display.id}');
170+
print('Display Name: ${display.name}');
171+
print('Display Width: ${display.size.width}');
172+
print('Display Height: ${display.size.height}');
173+
}
142174
}
143175

144176
setState(() {
@@ -193,55 +225,36 @@ class _MyHomePageState extends State<MyHomePage> {
193225
'$_counter',
194226
style: Theme.of(context).textTheme.headlineMedium,
195227
),
228+
if (kIsWeb)
229+
const Padding(
230+
padding: EdgeInsets.all(16),
231+
child: Text(
232+
'nativeapi is not available on the web, '
233+
'so the native features below are disabled.',
234+
),
235+
),
196236
FilledButton(
237+
onPressed: kIsWeb ? null : addTrayIcon,
197238
child: Text('Add TrayIcon'),
198-
onPressed: () {
199-
addTrayIcon();
200-
},
201239
),
202-
ContextMenuRegion(
203-
menu: _menu,
204-
child: Container(
205-
color: Colors.blue,
206-
padding: EdgeInsets.all(16),
207-
width: 100,
208-
height: 100,
209-
child: Center(child: Text('Context Menu Region')),
240+
if (_menu case final menu?)
241+
ContextMenuRegion(
242+
menu: menu,
243+
child: Container(
244+
color: Colors.blue,
245+
padding: EdgeInsets.all(16),
246+
width: 100,
247+
height: 100,
248+
child: Center(child: Text('Context Menu Region')),
249+
),
210250
),
211-
),
212251
FilledButton(
252+
onPressed: kIsWeb ? null : _showContextMenu,
213253
child: Text('Show Context Menu'),
214-
onPressed: () {
215-
final menu = Menu.create()!;
216-
final item1 = MenuItem.createWithLabelAndType(
217-
'Item1',
218-
MenuItemType.normal,
219-
)!;
220-
menu.addItem(item1);
221-
print(item1.label);
222-
final item2 = MenuItem.createWithLabelAndType(
223-
'Item2',
224-
MenuItemType.normal,
225-
)!;
226-
menu.addItem(item2);
227-
print('Context menu shown, item count: ${menu.itemCount}');
228-
menu.open(
229-
PositioningStrategy.absolute(
230-
const Offset(100.0, 100.0).toNative(),
231-
)!,
232-
Placement.bottomStart,
233-
);
234-
},
235254
),
236255
FilledButton(
256+
onPressed: kIsWeb ? null : _openDesktopFeatures,
237257
child: Text('Desktop Features: Dialogs, Pickers, Notifications'),
238-
onPressed: () {
239-
Navigator.of(context).push(
240-
MaterialPageRoute<void>(
241-
builder: (_) => const DesktopFeaturesPage(),
242-
),
243-
);
244-
},
245258
),
246259
],
247260
),
917 Bytes
Loading
5.17 KB
Loading
8.06 KB
Loading
5.46 KB
Loading
20.5 KB
Loading
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<!--
5+
If you are serving your web app in a path other than the root, change the
6+
href value below to reflect the base path you are serving from.
7+
8+
The path provided below has to start and end with a slash "/" in order for
9+
it to work correctly.
10+
11+
For more details:
12+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
13+
14+
This is a placeholder for base href that will be replaced by the value of
15+
the `--base-href` argument provided to `flutter build`.
16+
-->
17+
<base href="$FLUTTER_BASE_HREF">
18+
19+
<meta charset="UTF-8">
20+
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
21+
<meta name="description" content="A new Flutter project.">
22+
23+
<!-- iOS meta tags & icons -->
24+
<meta name="mobile-web-app-capable" content="yes">
25+
<meta name="apple-mobile-web-app-status-bar-style" content="black">
26+
<meta name="apple-mobile-web-app-title" content="nativeapi_example">
27+
<link rel="apple-touch-icon" href="icons/Icon-192.png">
28+
29+
<!-- Favicon -->
30+
<link rel="icon" type="image/png" href="favicon.png"/>
31+
32+
<title>nativeapi_example</title>
33+
<link rel="manifest" href="manifest.json">
34+
</head>
35+
<body>
36+
<!--
37+
You can customize the "flutter_bootstrap.js" script.
38+
This is useful to provide a custom configuration to the Flutter loader
39+
or to give the user feedback during the initialization process.
40+
41+
For more details:
42+
* https://docs.flutter.dev/platform-integration/web/initialization
43+
-->
44+
<script src="flutter_bootstrap.js" async></script>
45+
</body>
46+
</html>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"name": "nativeapi_example",
3+
"short_name": "nativeapi_example",
4+
"start_url": ".",
5+
"display": "standalone",
6+
"background_color": "#0175C2",
7+
"theme_color": "#0175C2",
8+
"description": "A new Flutter project.",
9+
"orientation": "portrait-primary",
10+
"prefer_related_applications": false,
11+
"icons": [
12+
{
13+
"src": "icons/Icon-192.png",
14+
"sizes": "192x192",
15+
"type": "image/png"
16+
},
17+
{
18+
"src": "icons/Icon-512.png",
19+
"sizes": "512x512",
20+
"type": "image/png"
21+
},
22+
{
23+
"src": "icons/Icon-maskable-192.png",
24+
"sizes": "192x192",
25+
"type": "image/png",
26+
"purpose": "maskable"
27+
},
28+
{
29+
"src": "icons/Icon-maskable-512.png",
30+
"sizes": "512x512",
31+
"type": "image/png",
32+
"purpose": "maskable"
33+
}
34+
]
35+
}

0 commit comments

Comments
 (0)