Skip to content

Commit 191478f

Browse files
authored
CupertinoRadio control and Radio.adaptive (#2225)
* CupertinoRadio control * Radio.adaptive * Radio.active_color * create cupertino_radio.py
1 parent 1c9ba0a commit 191478f

File tree

7 files changed

+416
-1
lines changed

7 files changed

+416
-1
lines changed

client/macos/Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,4 @@ SPEC CHECKSUMS:
5757

5858
PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7
5959

60-
COCOAPODS: 1.12.1
60+
COCOAPODS: 1.14.2

package/lib/src/controls/create_control.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import 'popup_menu_button.dart';
6363
import 'progress_bar.dart';
6464
import 'progress_ring.dart';
6565
import 'radio.dart';
66+
import 'cupertino_radio.dart';
6667
import 'radio_group.dart';
6768
import 'range_slider.dart';
6869
import 'responsive_row.dart';
@@ -539,6 +540,13 @@ Widget createWidget(Key? key, ControlViewModel controlView, Control? parent,
539540
control: controlView.control,
540541
parentDisabled: parentDisabled,
541542
dispatch: controlView.dispatch);
543+
case "cupertinoradio":
544+
return CupertinoRadioControl(
545+
key: key,
546+
parent: parent,
547+
control: controlView.control,
548+
parentDisabled: parentDisabled,
549+
dispatch: controlView.dispatch);
542550
case "dropdown":
543551
return DropdownControl(
544552
key: key,
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import 'package:flutter/cupertino.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:flutter_redux/flutter_redux.dart';
4+
5+
import '../actions.dart';
6+
import '../flet_app_services.dart';
7+
import '../models/app_state.dart';
8+
import '../models/control.dart';
9+
import '../models/control_ancestor_view_model.dart';
10+
import '../protocol/update_control_props_payload.dart';
11+
import '../utils/colors.dart';
12+
import 'create_control.dart';
13+
import 'error.dart';
14+
import 'list_tile.dart';
15+
16+
enum LabelPosition { right, left }
17+
18+
class CupertinoRadioControl extends StatefulWidget {
19+
final Control? parent;
20+
final Control control;
21+
final bool parentDisabled;
22+
final dynamic dispatch;
23+
24+
const CupertinoRadioControl(
25+
{Key? key,
26+
this.parent,
27+
required this.control,
28+
required this.parentDisabled,
29+
required this.dispatch})
30+
: super(key: key);
31+
32+
@override
33+
State<CupertinoRadioControl> createState() => _CupertinoRadioControlState();
34+
}
35+
36+
class _CupertinoRadioControlState extends State<CupertinoRadioControl> {
37+
late final FocusNode _focusNode;
38+
39+
@override
40+
void initState() {
41+
super.initState();
42+
_focusNode = FocusNode();
43+
_focusNode.addListener(_onFocusChange);
44+
}
45+
46+
void _onFocusChange() {
47+
FletAppServices.of(context).server.sendPageEvent(
48+
eventTarget: widget.control.id,
49+
eventName: _focusNode.hasFocus ? "focus" : "blur",
50+
eventData: "");
51+
}
52+
53+
@override
54+
void dispose() {
55+
_focusNode.removeListener(_onFocusChange);
56+
_focusNode.dispose();
57+
super.dispose();
58+
}
59+
60+
void _onChange(String ancestorId, String? value) {
61+
var svalue = value ?? "";
62+
debugPrint(svalue);
63+
List<Map<String, String>> props = [
64+
{"i": ancestorId, "value": svalue}
65+
];
66+
widget.dispatch(
67+
UpdateControlPropsAction(UpdateControlPropsPayload(props: props)));
68+
69+
final server = FletAppServices.of(context).server;
70+
server.updateControlProps(props: props);
71+
server.sendPageEvent(
72+
eventTarget: ancestorId, eventName: "change", eventData: svalue);
73+
}
74+
75+
@override
76+
Widget build(BuildContext context) {
77+
debugPrint("CupertinoRadio build: ${widget.control.id}");
78+
79+
String label = widget.control.attrString("label", "")!;
80+
String value = widget.control.attrString("value", "")!;
81+
LabelPosition labelPosition = LabelPosition.values.firstWhere(
82+
(p) =>
83+
p.name.toLowerCase() ==
84+
widget.control.attrString("labelPosition", "")!.toLowerCase(),
85+
orElse: () => LabelPosition.right);
86+
bool autofocus = widget.control.attrBool("autofocus", false)!;
87+
bool disabled = widget.control.isDisabled || widget.parentDisabled;
88+
89+
return StoreConnector<AppState, ControlAncestorViewModel>(
90+
distinct: true,
91+
ignoreChange: (state) {
92+
return state.controls[widget.control.id] == null;
93+
},
94+
converter: (store) => ControlAncestorViewModel.fromStore(
95+
store, widget.control.id, "radiogroup"),
96+
builder: (context, viewModel) {
97+
debugPrint(
98+
"CupertinoRadio StoreConnector build: ${widget.control.id}");
99+
100+
if (viewModel.ancestor == null) {
101+
return const ErrorControl(
102+
"CupertinoRadio control must be enclosed with RadioGroup.");
103+
}
104+
105+
String groupValue = viewModel.ancestor!.attrString("value", "")!;
106+
String ancestorId = viewModel.ancestor!.id;
107+
108+
var cupertinoRadio = CupertinoRadio<String>(
109+
autofocus: autofocus,
110+
focusNode: _focusNode,
111+
groupValue: groupValue,
112+
value: value,
113+
useCheckmarkStyle:
114+
widget.control.attrBool("useCheckmarkStyle", false)!,
115+
fillColor: HexColor.fromString(Theme.of(context),
116+
widget.control.attrString("fillColor", "")!),
117+
activeColor: HexColor.fromString(Theme.of(context),
118+
widget.control.attrString("activeColor", "")!),
119+
inactiveColor: HexColor.fromString(Theme.of(context),
120+
widget.control.attrString("inactiveColor", "")!),
121+
onChanged: !disabled
122+
? (String? value) {
123+
_onChange(ancestorId, value);
124+
}
125+
: null);
126+
127+
ListTileClicks.of(context)?.notifier.addListener(() {
128+
_onChange(ancestorId, value);
129+
});
130+
131+
Widget result = cupertinoRadio;
132+
if (label != "") {
133+
var labelWidget = disabled
134+
? Text(label,
135+
style: TextStyle(color: Theme.of(context).disabledColor))
136+
: MouseRegion(
137+
cursor: SystemMouseCursors.click, child: Text(label));
138+
result = MergeSemantics(
139+
child: GestureDetector(
140+
onTap: !disabled
141+
? () {
142+
_onChange(ancestorId, value);
143+
}
144+
: null,
145+
child: labelPosition == LabelPosition.right
146+
? Row(children: [cupertinoRadio, labelWidget])
147+
: Row(children: [labelWidget, cupertinoRadio])));
148+
}
149+
150+
return constrainedControl(
151+
context, result, widget.parent, widget.control);
152+
});
153+
}
154+
}

package/lib/src/controls/radio.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:flutter/foundation.dart';
12
import 'package:flutter/material.dart';
23
import 'package:flutter_redux/flutter_redux.dart';
34

@@ -8,7 +9,9 @@ import '../models/control.dart';
89
import '../models/control_ancestor_view_model.dart';
910
import '../protocol/update_control_props_payload.dart';
1011
import '../utils/buttons.dart';
12+
import '../utils/colors.dart';
1113
import 'create_control.dart';
14+
import 'cupertino_radio.dart';
1215
import 'error.dart';
1316
import 'list_tile.dart';
1417

@@ -75,6 +78,16 @@ class _RadioControlState extends State<RadioControl> {
7578
Widget build(BuildContext context) {
7679
debugPrint("Radio build: ${widget.control.id}");
7780

81+
bool adaptive = widget.control.attrBool("adaptive", false)!;
82+
if (adaptive &&
83+
(defaultTargetPlatform == TargetPlatform.iOS ||
84+
defaultTargetPlatform == TargetPlatform.macOS)) {
85+
return CupertinoRadioControl(
86+
control: widget.control,
87+
parentDisabled: widget.parentDisabled,
88+
dispatch: widget.dispatch);
89+
}
90+
7891
String label = widget.control.attrString("label", "")!;
7992
String value = widget.control.attrString("value", "")!;
8093
LabelPosition labelPosition = LabelPosition.values.firstWhere(
@@ -108,6 +121,8 @@ class _RadioControlState extends State<RadioControl> {
108121
focusNode: _focusNode,
109122
groupValue: groupValue,
110123
value: value,
124+
activeColor: HexColor.fromString(Theme.of(context),
125+
widget.control.attrString("activeColor", "")!),
111126
fillColor: parseMaterialStateColor(
112127
Theme.of(context), widget.control, "fillColor"),
113128
onChanged: !disabled

sdk/python/packages/flet-core/src/flet_core/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,5 +227,6 @@
227227
from flet_core.badge import Badge
228228
from flet_core.navigation_drawer import NavigationDrawer, NavigationDrawerDestination
229229
from flet_core.selection_area import SelectionArea
230+
from flet_core.cupertino_radio import CupertinoRadio
230231
from flet_core.cupertino_checkbox import CupertinoCheckbox
231232
from flet_core.cupertino_switch import CupertinoSwitch

0 commit comments

Comments
 (0)