Skip to content

Commit 098e126

Browse files
committed
RadioGroup and Radio
1 parent fef1b70 commit 098e126

File tree

11 files changed

+353
-6
lines changed

11 files changed

+353
-6
lines changed

client/lib/controls/checkbox.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ class _CheckboxControlState extends State<CheckboxControl> {
6565
}
6666

6767
onChange(bool? value) {
68-
debugPrint(value?.toString());
68+
var svalue = value != null ? value.toString() : "";
69+
debugPrint(svalue);
6970
setState(() {
7071
_value = value;
7172
});
@@ -78,6 +79,10 @@ class _CheckboxControlState extends State<CheckboxControl> {
7879
dispatch(UpdateControlPropsAction(
7980
UpdateControlPropsPayload(props: props)));
8081
ws.updateControlProps(props: props);
82+
ws.pageEventFromWeb(
83+
eventTarget: widget.control.id,
84+
eventName: "change",
85+
eventData: svalue);
8186
}
8287

8388
var checkbox = Checkbox(

client/lib/controls/create_control.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import 'outlined_button.dart';
1919
import 'page.dart';
2020
import 'progress_bar.dart';
2121
import 'progress_ring.dart';
22+
import 'radio.dart';
23+
import 'radio_group.dart';
2224
import 'row.dart';
2325
import 'snack_bar.dart';
2426
import 'stack.dart';
@@ -122,6 +124,17 @@ Widget createControl(Control? parent, String id, bool parentDisabled) {
122124
parent: parent,
123125
control: controlView.control,
124126
parentDisabled: parentDisabled);
127+
case ControlType.radioGroup:
128+
return RadioGroupControl(
129+
parent: parent,
130+
control: controlView.control,
131+
children: controlView.children,
132+
parentDisabled: parentDisabled);
133+
case ControlType.radio:
134+
return RadioControl(
135+
parent: parent,
136+
control: controlView.control,
137+
parentDisabled: parentDisabled);
125138
case ControlType.dropdown:
126139
return DropdownControl(
127140
parent: parent,

client/lib/controls/radio.dart

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import 'package:flet_view/controls/error.dart';
2+
import 'package:flet_view/models/control_ancestor_view_model.dart';
3+
import 'package:flet_view/models/control_type.dart';
4+
import 'package:flutter/material.dart';
5+
import 'package:flutter_redux/flutter_redux.dart';
6+
7+
import '../actions.dart';
8+
import '../models/app_state.dart';
9+
import '../models/control.dart';
10+
import '../protocol/update_control_props_payload.dart';
11+
import '../web_socket_client.dart';
12+
import 'create_control.dart';
13+
14+
enum LabelPosition { right, left }
15+
16+
class RadioControl extends StatefulWidget {
17+
final Control? parent;
18+
final Control control;
19+
final bool parentDisabled;
20+
21+
const RadioControl(
22+
{Key? key,
23+
this.parent,
24+
required this.control,
25+
required this.parentDisabled})
26+
: super(key: key);
27+
28+
@override
29+
State<RadioControl> createState() => _RadioControlState();
30+
}
31+
32+
class _RadioControlState extends State<RadioControl> {
33+
@override
34+
void initState() {
35+
super.initState();
36+
}
37+
38+
@override
39+
void dispose() {
40+
super.dispose();
41+
}
42+
43+
@override
44+
Widget build(BuildContext context) {
45+
debugPrint("Radio build: ${widget.control.id}");
46+
47+
String label = widget.control.attrString("label", "")!;
48+
String value = widget.control.attrString("value", "")!;
49+
LabelPosition labelPosition = LabelPosition.values.firstWhere(
50+
(p) =>
51+
p.name.toLowerCase() ==
52+
widget.control.attrString("labelPosition", "")!.toLowerCase(),
53+
orElse: () => LabelPosition.right);
54+
bool disabled = widget.control.isDisabled || widget.parentDisabled;
55+
56+
return StoreConnector<AppState, ControlAncestorViewModel>(
57+
distinct: true,
58+
converter: (store) => ControlAncestorViewModel.fromStore(
59+
store, widget.control.id, ControlType.radioGroup),
60+
builder: (context, viewModel) {
61+
debugPrint("Radio StoreConnector build: ${widget.control.id}");
62+
63+
if (viewModel.ancestor == null) {
64+
return const ErrorControl(
65+
"Radio control must be enclosed with RadioGroup.");
66+
}
67+
68+
String groupValue = viewModel.ancestor!.attrString("value", "")!;
69+
70+
onChange(String? value) {
71+
var svalue = value != null ? value.toString() : "";
72+
debugPrint(svalue);
73+
List<Map<String, String>> props = [
74+
{"i": viewModel.ancestor!.id, "value": svalue}
75+
];
76+
viewModel.dispatch(UpdateControlPropsAction(
77+
UpdateControlPropsPayload(props: props)));
78+
ws.updateControlProps(props: props);
79+
ws.pageEventFromWeb(
80+
eventTarget: viewModel.ancestor!.id,
81+
eventName: "change",
82+
eventData: svalue);
83+
}
84+
85+
var radio = Radio<String>(
86+
groupValue: groupValue,
87+
value: value,
88+
onChanged: !disabled
89+
? (String? value) {
90+
onChange(value);
91+
}
92+
: null);
93+
94+
Widget result = radio;
95+
if (label != "") {
96+
var labelWidget = disabled
97+
? Text(label,
98+
style: TextStyle(color: Theme.of(context).disabledColor))
99+
: MouseRegion(
100+
cursor: SystemMouseCursors.click, child: Text(label));
101+
result = GestureDetector(
102+
onTap: !disabled
103+
? () {
104+
onChange(value);
105+
}
106+
: null,
107+
child: labelPosition == LabelPosition.right
108+
? Row(children: [radio, labelWidget])
109+
: Row(children: [labelWidget, radio]));
110+
}
111+
112+
return constrainedControl(result, widget.parent, widget.control);
113+
});
114+
}
115+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import 'package:flet_view/controls/error.dart';
2+
import 'package:flutter/material.dart';
3+
4+
import '../models/control.dart';
5+
import 'create_control.dart';
6+
7+
class RadioGroupControl extends StatelessWidget {
8+
final Control? parent;
9+
final Control control;
10+
final List<Control> children;
11+
final bool parentDisabled;
12+
13+
const RadioGroupControl(
14+
{Key? key,
15+
this.parent,
16+
required this.control,
17+
required this.children,
18+
required this.parentDisabled})
19+
: super(key: key);
20+
21+
@override
22+
Widget build(BuildContext context) {
23+
debugPrint("RadioGroupControl build: ${control.id}");
24+
25+
var contentCtrls = children.where((c) => c.name == "content");
26+
bool disabled = control.isDisabled || parentDisabled;
27+
28+
if (contentCtrls.isEmpty) {
29+
return const ErrorControl(
30+
"RadioGroup control does not have any content.");
31+
}
32+
33+
return createControl(control, contentCtrls.first.id, disabled);
34+
}
35+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import 'package:equatable/equatable.dart';
2+
import 'package:flet_view/models/control_type.dart';
3+
import 'package:redux/redux.dart';
4+
5+
import 'app_state.dart';
6+
import 'control.dart';
7+
8+
class ControlAncestorViewModel extends Equatable {
9+
final Control? ancestor;
10+
final Function dispatch;
11+
12+
const ControlAncestorViewModel(
13+
{required this.ancestor, required this.dispatch});
14+
15+
static ControlAncestorViewModel fromStore(
16+
Store<AppState> store, String id, ControlType ancestorType) {
17+
Control? ancestor;
18+
String controlId = id;
19+
while (true) {
20+
String parentId = store.state.controls[controlId]!.pid;
21+
if (parentId == "") {
22+
break;
23+
}
24+
ancestor = store.state.controls[parentId]!;
25+
if (ancestor.type == ancestorType) {
26+
break;
27+
}
28+
controlId = ancestor.id;
29+
}
30+
31+
return ControlAncestorViewModel(
32+
ancestor: ancestor, dispatch: store.dispatch);
33+
}
34+
35+
@override
36+
List<Object?> get props => [ancestor];
37+
}

client/lib/models/control_type.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ enum ControlType {
1616
page,
1717
progressBar,
1818
progressRing,
19+
radioGroup,
1920
radio,
2021
row,
2122
slider,

sdk/python/flet/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from flet.progress_bar import ProgressBar
1818
from flet.progress_ring import ProgressRing
1919
from flet.radio import Radio
20+
from flet.radio_group import RadioGroup
2021
from flet.ref import Ref
2122
from flet.row import Row
2223
from flet.slider import Slider

sdk/python/flet/radio.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from typing import Optional
22

33
from beartype import beartype
4-
from flet.constrained_control import ConstrainedControl
54

5+
from flet.constrained_control import ConstrainedControl
66
from flet.control import Control, OptionalNumber
77
from flet.ref import Ref
88

@@ -31,7 +31,7 @@ def __init__(
3131
#
3232
label: str = None,
3333
label_position: LabelPosition = None,
34-
value: bool = None,
34+
value: str = None,
3535
on_change=None,
3636
):
3737
ConstrainedControl.__init__(
@@ -56,11 +56,10 @@ def _get_control_name(self):
5656
# value
5757
@property
5858
def value(self):
59-
return self._get_attr("value", data_type="bool", def_value=False)
59+
return self._get_attr("value", def_value="")
6060

6161
@value.setter
62-
@beartype
63-
def value(self, value: Optional[bool]):
62+
def value(self, value):
6463
self._set_attr("value", value)
6564

6665
# label

sdk/python/flet/radio_group.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
from typing import Optional
2+
3+
from beartype import beartype
4+
5+
from flet.control import Control, OptionalNumber
6+
from flet.ref import Ref
7+
8+
9+
class RadioGroup(Control):
10+
def __init__(
11+
self,
12+
content: Control = None,
13+
#
14+
# Control
15+
#
16+
ref: Ref = None,
17+
opacity: OptionalNumber = None,
18+
visible: bool = None,
19+
disabled: bool = None,
20+
data: any = None,
21+
#
22+
# Specific
23+
#
24+
value: str = None,
25+
on_change=None,
26+
):
27+
28+
Control.__init__(
29+
self,
30+
ref=ref,
31+
opacity=opacity,
32+
visible=visible,
33+
disabled=disabled,
34+
data=data,
35+
)
36+
37+
self.content = content
38+
self.value = value
39+
self.on_change = on_change
40+
41+
def _get_control_name(self):
42+
return "radiogroup"
43+
44+
def _get_children(self):
45+
if self.__content == None:
46+
return []
47+
self.__content._set_attr_internal("n", "content")
48+
return [self.__content]
49+
50+
# value
51+
@property
52+
def value(self):
53+
return self._get_attr("value")
54+
55+
@value.setter
56+
def value(self, value):
57+
self._set_attr("value", value)
58+
59+
# content
60+
@property
61+
def content(self):
62+
return self.__content
63+
64+
@content.setter
65+
@beartype
66+
def content(self, value: Optional[Control]):
67+
self.__content = value
68+
69+
# on_change
70+
@property
71+
def on_change(self):
72+
return self._get_event_handler("change")
73+
74+
@on_change.setter
75+
def on_change(self, handler):
76+
self._add_event_handler("change", handler)

0 commit comments

Comments
 (0)