Skip to content

Commit ea40ea8

Browse files
settings: Replace RadioListTile with CustomRadioTile
1 parent 47c4ffc commit ea40ea8

File tree

2 files changed

+99
-46
lines changed

2 files changed

+99
-46
lines changed

lib/widgets/settings.dart

Lines changed: 92 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@ class _SettingsHeader extends StatelessWidget {
6868
child: Padding(
6969
padding: const EdgeInsets.fromLTRB(16, 16, 16,8),
7070
child: Text(title,
71-
style: TextStyle(
72-
fontSize: 17).merge(weightVariableTextStyle(context, wght: 600)),
71+
style: TextStyle(fontSize: 17).merge(weightVariableTextStyle(context, wght: 600)),
7372
)));
7473
}}
7574

@@ -117,18 +116,19 @@ class _ThemeSetting extends StatelessWidget {
117116
Widget build(BuildContext context) {
118117
final zulipLocalizations = ZulipLocalizations.of(context);
119118
final globalSettings = GlobalStoreWidget.settingsOf(context);
120-
return RadioGroup<ThemeSetting?>(
121-
groupValue: globalSettings.themeSetting,
122-
onChanged: (newValue) => _handleChange(context, newValue),
119+
final themeSetting = globalSettings.themeSetting;
120+
return Material(
121+
color: Colors.transparent,
123122
child: Column(
124123
children: [
125-
//ListTile(title: Text(zulipLocalizations.themeSettingTitle)),
126-
for (final themeSettingOption in [null, ...ThemeSetting.values])
127-
RadioListTile<ThemeSetting?>.adaptive(
128-
title: Text(ThemeSetting.displayName(
124+
for (final themeSettingOption in [ThemeSetting.dark, ThemeSetting.light, null])
125+
CustomRadioTile<ThemeSetting?>(
126+
value: themeSettingOption,
127+
groupValue: themeSetting,
128+
label: ThemeSetting.displayName(
129129
themeSetting: themeSettingOption,
130-
zulipLocalizations: zulipLocalizations)),
131-
value: themeSettingOption),
130+
zulipLocalizations: zulipLocalizations),
131+
onChanged: (v) => _handleChange(context, v)),
132132
]));
133133
}
134134
}
@@ -188,17 +188,18 @@ class VisitFirstUnreadSettingPage extends StatelessWidget {
188188
final globalSettings = GlobalStoreWidget.settingsOf(context);
189189
return Scaffold(
190190
appBar: AppBar(title: Text(zulipLocalizations.initialAnchorSettingTitle)),
191-
body: RadioGroup<VisitFirstUnreadSetting>(
192-
groupValue: globalSettings.visitFirstUnread,
193-
onChanged: (newValue) => _handleChange(context, newValue),
194-
child: Column(children: [
195-
ListTile(title: Text(zulipLocalizations.initialAnchorSettingDescription)),
196-
for (final value in VisitFirstUnreadSetting.values)
197-
RadioListTile<VisitFirstUnreadSetting>.adaptive(
198-
title: Text(_valueDisplayName(value,
199-
zulipLocalizations: zulipLocalizations)),
200-
value: value),
201-
])));
191+
body: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
192+
Padding(
193+
padding: const EdgeInsets.all(16.0),
194+
child: Text(zulipLocalizations.initialAnchorSettingDescription,
195+
style: TextStyle(fontSize: 17).merge(weightVariableTextStyle(context, wght: 400)))),
196+
for (final value in VisitFirstUnreadSetting.values)
197+
CustomRadioTile(
198+
value: value,
199+
groupValue: globalSettings.visitFirstUnread,
200+
label: _valueDisplayName(value, zulipLocalizations: zulipLocalizations),
201+
onChanged:(newValue) => _handleChange(context, newValue))
202+
]));
202203
}
203204
}
204205

@@ -245,22 +246,18 @@ class MarkReadOnScrollSettingPage extends StatelessWidget {
245246
final globalSettings = GlobalStoreWidget.settingsOf(context);
246247
return Scaffold(
247248
appBar: AppBar(title: Text(zulipLocalizations.markReadOnScrollSettingTitle)),
248-
body: RadioGroup<MarkReadOnScrollSetting>(
249-
groupValue: globalSettings.markReadOnScroll,
250-
onChanged: (newValue) => _handleChange(context, newValue),
251-
child: Column(children: [
252-
ListTile(title: Text(zulipLocalizations.markReadOnScrollSettingDescription)),
253-
for (final value in MarkReadOnScrollSetting.values)
254-
RadioListTile<MarkReadOnScrollSetting>.adaptive(
255-
title: Text(_valueDisplayName(value,
256-
zulipLocalizations: zulipLocalizations)),
257-
subtitle: () {
258-
final result = _valueDescription(value,
259-
zulipLocalizations: zulipLocalizations);
260-
return result == null ? null : Text(result);
261-
}(),
262-
value: value),
263-
])));
249+
body: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
250+
Padding(padding: const EdgeInsets.all(16.0),
251+
child: Text(zulipLocalizations.markReadOnScrollSettingDescription,
252+
style: TextStyle(fontSize: 17).merge(weightVariableTextStyle(context, wght: 400)))),
253+
for (final value in MarkReadOnScrollSetting.values)
254+
CustomRadioTile(
255+
value: value,
256+
groupValue: globalSettings.markReadOnScroll,
257+
label: _valueDisplayName(value, zulipLocalizations: zulipLocalizations),
258+
onChanged: (newValue) => _handleChange(context, newValue),
259+
description: _valueDescription(value, zulipLocalizations: zulipLocalizations))
260+
]));
264261
}
265262
}
266263

@@ -291,3 +288,60 @@ class ExperimentalFeaturesPage extends StatelessWidget {
291288
]));
292289
}
293290
}
291+
292+
class CustomRadioTile<T> extends StatelessWidget {
293+
final T value;
294+
final T groupValue;
295+
final String label;
296+
final ValueChanged<T?> onChanged;
297+
final String? description;
298+
299+
const CustomRadioTile({
300+
super.key,
301+
required this.value,
302+
required this.groupValue,
303+
required this.label,
304+
required this.onChanged,
305+
this.description,
306+
});
307+
308+
@override
309+
Widget build(BuildContext context) {
310+
final selected = value == groupValue;
311+
final size = 20.0;
312+
final colr = const Color(0xff4370f0);
313+
314+
return InkWell(
315+
onTap: () => onChanged(value),
316+
borderRadius: BorderRadius.circular(6),
317+
child: Padding(
318+
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
319+
child: Row(
320+
crossAxisAlignment: CrossAxisAlignment.start,
321+
children: [
322+
AnimatedContainer(
323+
duration: const Duration(milliseconds: 200),
324+
margin: const EdgeInsets.only(top: 4),
325+
width: size,
326+
height: size,
327+
decoration: BoxDecoration(
328+
color: selected ? colr : Colors.transparent,
329+
border: Border.all(color: selected ? colr : Colors.grey.shade400, width: 2),
330+
borderRadius: BorderRadius.circular(size / 2)),
331+
child: selected? const Icon(ZulipIcons.check, size: 16, color: Colors.white): null),
332+
const SizedBox(width: 12),
333+
Expanded(
334+
child: Column(
335+
crossAxisAlignment: CrossAxisAlignment.start,
336+
children: [
337+
Text(label,style: TextStyle(fontSize: 18).merge(weightVariableTextStyle(context, wght: 500))),
338+
if (description != null)
339+
Padding(
340+
padding: const EdgeInsets.only(top: 2),
341+
child: Text(description!,
342+
style: TextStyle(fontSize: 17).merge(weightVariableTextStyle(context, wght: 400)),),
343+
)])),
344+
])));
345+
}
346+
}
347+

test/widgets/settings_test.dart

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,15 @@ void main() {
5151

5252
Finder findRadioListTileWithTitle<T>(String title) => find.ancestor(
5353
of: find.text(title),
54-
matching: find.byType(RadioListTile<T>));
54+
matching: find.byType(CustomRadioTile<T>));
5555

5656
void checkRadioButtonAppearsChecked<T>(WidgetTester tester,
5757
String title, bool expectedIsChecked, {String? subtitle}) {
58-
check(tester.semantics.find(findRadioListTileWithTitle<T>(title)))
59-
.containsSemantics(
60-
label: subtitle == null
61-
? title
62-
: '$title\n$subtitle',
63-
isInMutuallyExclusiveGroup: true,
64-
hasCheckedState: true, isChecked: expectedIsChecked);
58+
final tile = tester.widget<CustomRadioTile<T>>(findRadioListTileWithTitle<T>(title));
59+
if (expectedIsChecked) {
60+
check(tile.value).equals(tile.groupValue);
61+
} else {
62+
check(tile.value).not((it) => it.equals(tile.groupValue));}
6563
}
6664

6765
group('ThemeSetting', () {
@@ -302,3 +300,4 @@ void main() {
302300
// [GlobalSettingsStore.experimentalFeatureFlags] so that tests can
303301
// control making it empty, or non-empty, at will.)
304302
}
303+

0 commit comments

Comments
 (0)