Skip to content

Commit 37bcc43

Browse files
committed
Offstage controls
1 parent 8fa6f16 commit 37bcc43

File tree

14 files changed

+372
-83
lines changed

14 files changed

+372
-83
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_redux/flutter_redux.dart';
3+
4+
import '../actions.dart';
5+
import '../models/app_state.dart';
6+
import '../models/control.dart';
7+
import '../protocol/update_control_props_payload.dart';
8+
import '../utils/alignment.dart';
9+
import '../utils/edge_insets.dart';
10+
import '../web_socket_client.dart';
11+
import 'create_control.dart';
12+
import 'error.dart';
13+
14+
class AlertDialogControl extends StatefulWidget {
15+
final Control? parent;
16+
final Control control;
17+
final List<Control> children;
18+
final bool parentDisabled;
19+
20+
const AlertDialogControl(
21+
{Key? key,
22+
this.parent,
23+
required this.control,
24+
required this.children,
25+
required this.parentDisabled})
26+
: super(key: key);
27+
28+
@override
29+
State<AlertDialogControl> createState() => _AlertDialogControlState();
30+
}
31+
32+
class _AlertDialogControlState extends State<AlertDialogControl> {
33+
bool _open = false;
34+
35+
@override
36+
void initState() {
37+
super.initState();
38+
}
39+
40+
@override
41+
void dispose() {
42+
super.dispose();
43+
}
44+
45+
Widget _createAlertDialog() {
46+
bool disabled = widget.control.isDisabled || widget.parentDisabled;
47+
var titleCtrls = widget.children.where((c) => c.name == "title");
48+
var contentCtrls = widget.children.where((c) => c.name == "content");
49+
var actionCtrls = widget.children.where((c) => c.name == "action");
50+
final actionsAlignment = parseMainAxisAlignment(
51+
widget.control, "actionsAlignment", MainAxisAlignment.start);
52+
if (titleCtrls.isEmpty && contentCtrls.isEmpty && actionCtrls.isEmpty) {
53+
return const ErrorControl("AlertDialog does not have any content.");
54+
}
55+
56+
return AlertDialog(
57+
title: titleCtrls.isNotEmpty
58+
? createControl(widget.control, titleCtrls.first.id, disabled)
59+
: null,
60+
titlePadding: parseEdgeInsets(widget.control, "titlePadding"),
61+
content: contentCtrls.isNotEmpty
62+
? createControl(widget.control, contentCtrls.first.id, disabled)
63+
: null,
64+
contentPadding: parseEdgeInsets(widget.control, "contentPadding") ??
65+
const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0),
66+
actions: actionCtrls
67+
.map((c) => createControl(widget.control, c.id, disabled))
68+
.toList(),
69+
actionsPadding:
70+
parseEdgeInsets(widget.control, "actionsPadding") ?? EdgeInsets.zero,
71+
actionsAlignment: actionsAlignment,
72+
);
73+
}
74+
75+
@override
76+
Widget build(BuildContext context) {
77+
debugPrint("AlertDialog build: ${widget.control.id}");
78+
79+
return StoreConnector<AppState, Function>(
80+
distinct: true,
81+
converter: (store) => store.dispatch,
82+
builder: (context, dispatch) {
83+
debugPrint("AlertDialog StoreConnector build: ${widget.control.id}");
84+
85+
var open = widget.control.attrBool("open", false)!;
86+
var modal = widget.control.attrBool("modal", false)!;
87+
// var removeCurrentSnackbar =
88+
// widget.control.attrBool("removeCurrentSnackBar", false)!;
89+
90+
debugPrint("Current open state: $_open");
91+
debugPrint("New open state: $open");
92+
93+
if (open && (open != _open)) {
94+
var dialog = _createAlertDialog();
95+
if (dialog is ErrorControl) {
96+
return dialog;
97+
}
98+
99+
WidgetsBinding.instance!.addPostFrameCallback((_) {
100+
// if (removeCurrentSnackbar) {
101+
// ScaffoldMessenger.of(context).removeCurrentSnackBar();
102+
// }
103+
104+
showDialog(
105+
barrierDismissible: !modal,
106+
context: context,
107+
builder: (context) => _createAlertDialog()).then((value) {
108+
debugPrint("Dialog dismissed");
109+
_open = false;
110+
List<Map<String, String>> props = [
111+
{"i": widget.control.id, "open": "false"}
112+
];
113+
dispatch(UpdateControlPropsAction(
114+
UpdateControlPropsPayload(props: props)));
115+
ws.updateControlProps(props: props);
116+
});
117+
});
118+
} else if (open != _open && _open) {
119+
Navigator.pop(context);
120+
}
121+
122+
_open = open;
123+
124+
return const SizedBox.shrink();
125+
});
126+
}
127+
}

client/lib/controls/create_control.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import '../models/app_state.dart';
55
import '../models/control.dart';
66
import '../models/control_type.dart';
77
import '../models/control_view_model.dart';
8+
import 'alert_dialog.dart';
89
import 'checkbox.dart';
910
import 'column.dart';
1011
import 'container.dart';
@@ -159,6 +160,12 @@ Widget createControl(Control? parent, String id, bool parentDisabled) {
159160
control: controlView.control,
160161
children: controlView.children,
161162
parentDisabled: parentDisabled);
163+
case ControlType.alertDialog:
164+
return AlertDialogControl(
165+
parent: parent,
166+
control: controlView.control,
167+
children: controlView.children,
168+
parentDisabled: parentDisabled);
162169
default:
163170
throw Exception("Unknown control type: ${controlView.control.type}");
164171
}

client/lib/controls/page.dart

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
import 'package:flet_view/models/control_type.dart';
12
import 'package:flet_view/utils/desktop.dart';
23
import 'package:flutter/material.dart';
4+
import 'package:flutter_redux/flutter_redux.dart';
35

6+
import '../models/app_state.dart';
47
import '../models/control.dart';
8+
import '../models/control_children_view_model.dart';
59
import '../utils/alignment.dart';
610
import '../utils/colors.dart';
711
import '../utils/edge_insets.dart';
@@ -22,8 +26,6 @@ class PageControl extends StatelessWidget {
2226
Widget build(BuildContext context) {
2327
debugPrint("Page build: ${control.id}");
2428

25-
debugPrint(Theme.of(context).colorScheme.primary.toString());
26-
2729
bool disabled = control.isDisabled;
2830

2931
final spacing = control.attrDouble("spacing", 10)!;
@@ -32,14 +34,14 @@ class PageControl extends StatelessWidget {
3234
final crossAlignment = parseCrossAxisAlignment(
3335
control, "horizontalAlignment", CrossAxisAlignment.start);
3436

35-
List<Widget> offstage = [];
37+
Control? offstage;
3638
List<Widget> controls = [];
3739
bool firstControl = true;
3840

3941
for (var ctrl in children.where((c) => c.isVisible)) {
4042
// offstage control
41-
if (ctrl.name == "offstage") {
42-
offstage.add(createControl(control, ctrl.id, control.isDisabled));
43+
if (ctrl.type == ControlType.offstage) {
44+
offstage = ctrl;
4345
continue;
4446
}
4547

@@ -79,29 +81,47 @@ class PageControl extends StatelessWidget {
7981
String title = control.attrString("title", "")!;
8082
setWindowTitle(title);
8183

82-
return MaterialApp(
83-
title: title,
84-
theme: theme,
85-
darkTheme: darkTheme,
86-
themeMode: themeMode,
87-
home: Scaffold(
88-
body: Stack(children: [
89-
SizedBox.expand(
90-
child: Container(
91-
padding:
92-
parseEdgeInsets(control, "padding") ?? const EdgeInsets.all(10),
93-
decoration: BoxDecoration(
94-
color: HexColor.fromString(
95-
context, control.attrString("bgcolor", "")!)),
96-
child: Column(
97-
mainAxisAlignment: mainAlignment,
98-
crossAxisAlignment: crossAlignment,
99-
children: controls),
100-
)),
101-
...offstage,
102-
const ScreenSize()
103-
]),
104-
),
105-
);
84+
return StoreConnector<AppState, ControlChildrenViewModel?>(
85+
distinct: true,
86+
converter: (store) => offstage != null
87+
? ControlChildrenViewModel.fromStore(store, offstage.id,
88+
dispatch: store.dispatch)
89+
: null,
90+
builder: (context, offstageView) {
91+
debugPrint("Offstage StoreConnector build");
92+
93+
// offstage
94+
List<Widget> offstageWidgets = offstageView != null
95+
? offstageView.children
96+
.where((c) => c.isVisible)
97+
.map((c) => createControl(offstage, c.id, disabled))
98+
.toList()
99+
: [];
100+
101+
return MaterialApp(
102+
title: title,
103+
theme: theme,
104+
darkTheme: darkTheme,
105+
themeMode: themeMode,
106+
home: Scaffold(
107+
body: Stack(children: [
108+
SizedBox.expand(
109+
child: Container(
110+
padding: parseEdgeInsets(control, "padding") ??
111+
const EdgeInsets.all(10),
112+
decoration: BoxDecoration(
113+
color: HexColor.fromString(
114+
context, control.attrString("bgcolor", "")!)),
115+
child: Column(
116+
mainAxisAlignment: mainAlignment,
117+
crossAxisAlignment: crossAlignment,
118+
children: controls),
119+
)),
120+
...offstageWidgets,
121+
const ScreenSize()
122+
]),
123+
),
124+
);
125+
});
106126
}
107127
}

client/lib/main.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import 'session_store/session_store.dart'
1818
import 'web_socket_client.dart';
1919

2020
void main([List<String>? args]) async {
21-
setupDesktop();
21+
await setupDesktop();
2222

2323
var pageUri = Uri.base;
2424

client/lib/models/control_type.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ enum ControlType {
1313
image,
1414
listView,
1515
outlinedButton,
16+
offstage,
1617
page,
1718
progressBar,
1819
progressRing,

client/lib/utils/desktop.dart

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,21 @@ import 'dart:io';
22

33
import 'package:flutter/foundation.dart';
44
import 'package:flutter/widgets.dart';
5-
import 'package:window_size/window_size.dart' as desktop;
5+
import 'package:window_manager/window_manager.dart';
66

77
const double windowWidth = 480;
88
const double windowHeight = 854;
99

1010
void setWindowTitle(String title) {
1111
if (!kIsWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
12-
desktop.setWindowTitle(title);
12+
windowManager.setTitle(title);
1313
}
1414
}
1515

16-
void setupDesktop() {
16+
Future setupDesktop() async {
1717
if (!kIsWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
1818
WidgetsFlutterBinding.ensureInitialized();
19-
// setWindowMinSize(const Size(windowWidth, windowHeight));
20-
// setWindowMaxSize(const Size(windowWidth, windowHeight));
21-
// getCurrentScreen().then((screen) {
22-
// setWindowFrame(Rect.fromCenter(
23-
// center: screen!.frame.center,
24-
// width: windowWidth,
25-
// height: windowHeight,
26-
// ));
27-
// });
19+
// Must add this line.
20+
await windowManager.ensureInitialized();
2821
}
2922
}

client/pubspec.lock

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -205,14 +205,13 @@ packages:
205205
url: "https://pub.dartlang.org"
206206
source: hosted
207207
version: "2.1.0"
208-
window_size:
208+
window_manager:
209209
dependency: "direct main"
210210
description:
211-
path: "plugins/window_size"
212-
ref: HEAD
213-
resolved-ref: "5c51870ced62a00e809ba4b81a846a052d241c9f"
214-
url: "https://github.com/google/flutter-desktop-embedding.git"
215-
source: git
216-
version: "0.1.0"
211+
name: window_manager
212+
url: "https://pub.dartlang.org"
213+
source: hosted
214+
version: "0.2.1"
217215
sdks:
218216
dart: ">=2.16.1 <3.0.0"
217+
flutter: ">=1.20.0"

client/pubspec.yaml

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ description: A new Flutter project.
33

44
# The following line prevents the package from being accidentally published to
55
# pub.dev using `flutter pub publish`. This is preferred for private packages.
6-
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
6+
publish_to: "none" # Remove this line if you wish to publish to pub.dev
77

88
# The following defines the version and build number for your application.
99
# A version number is three numbers separated by dots, like 1.2.43
@@ -30,18 +30,14 @@ dependencies:
3030
flutter:
3131
sdk: flutter
3232

33-
3433
# The following adds the Cupertino Icons font to your application.
3534
# Use with the CupertinoIcons class for iOS style icons.
3635
cupertino_icons: ^1.0.2
3736
redux: ^5.0.0
3837
flutter_redux: ^0.8.2
3938
equatable: ^2.0.3
4039
web_socket_channel: ^2.1.0
41-
window_size:
42-
git:
43-
url: https://github.com/google/flutter-desktop-embedding.git
44-
path: plugins/window_size
40+
window_manager: ^0.2.1
4541

4642
dev_dependencies:
4743
flutter_test:
@@ -59,7 +55,6 @@ dev_dependencies:
5955

6056
# The following section is specific to Flutter.
6157
flutter:
62-
6358
# The following line ensures that the Material Icons font is
6459
# included with your application, so that you can use the icons in
6560
# the material Icons class.

client/windows/flutter/generated_plugin_registrant.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66

77
#include "generated_plugin_registrant.h"
88

9-
#include <window_size/window_size_plugin.h>
9+
#include <window_manager/window_manager_plugin.h>
1010

1111
void RegisterPlugins(flutter::PluginRegistry* registry) {
12-
WindowSizePluginRegisterWithRegistrar(
13-
registry->GetRegistrarForPlugin("WindowSizePlugin"));
12+
WindowManagerPluginRegisterWithRegistrar(
13+
registry->GetRegistrarForPlugin("WindowManagerPlugin"));
1414
}

client/windows/flutter/generated_plugins.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#
44

55
list(APPEND FLUTTER_PLUGIN_LIST
6-
window_size
6+
window_manager
77
)
88

99
set(PLUGIN_BUNDLED_LIBRARIES)

0 commit comments

Comments
 (0)