Skip to content

Commit 5e2b6ee

Browse files
committed
test(settings): Adapt BrowserPreference tests to FigmaToggle
1 parent ea7abd2 commit 5e2b6ee

File tree

2 files changed

+277
-112
lines changed

2 files changed

+277
-112
lines changed

lib/widgets/settings.dart

Lines changed: 150 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class FigmaToggle extends StatelessWidget {
3434
final theme = Theme.of(context);
3535
final colorScheme = theme.colorScheme;
3636

37-
// Figma-specified dimensions
37+
// Exact Figma-specified dimensions
3838
final trackWidth = value ? 48.0 : 46.0;
3939
final trackHeight = value ? 28.0 : 26.0;
4040
final thumbRadius = value ? 10.0 : 7.0;
@@ -48,6 +48,13 @@ class FigmaToggle extends StatelessWidget {
4848
final trackColor = value ? effectiveActiveColor : effectiveInactiveColor;
4949
final thumbColor = value ? effectiveActiveThumbColor : effectiveInactiveThumbColor;
5050

51+
// Calculate thumb positioning with proper padding
52+
final thumbDiameter = thumbRadius * 2;
53+
final horizontalPadding = 4.0;
54+
final thumbLeftPosition = value
55+
? trackWidth - thumbDiameter - horizontalPadding
56+
: horizontalPadding;
57+
5158
return GestureDetector(
5259
onTap: onChanged != null ? () => onChanged!(!value) : null,
5360
child: AnimatedContainer(
@@ -64,21 +71,19 @@ class FigmaToggle extends StatelessWidget {
6471
AnimatedPositioned(
6572
duration: const Duration(milliseconds: 200),
6673
curve: Curves.easeInOut,
67-
left: value
68-
? trackWidth - (thumbRadius * 2) - 4.0 // 4px padding from edge
69-
: 4.0, // 4px padding from edge
70-
top: (trackHeight - (thumbRadius * 2)) / 2,
74+
left: thumbLeftPosition,
75+
top: (trackHeight - thumbDiameter) / 2,
7176
child: AnimatedContainer(
7277
duration: const Duration(milliseconds: 200),
7378
curve: Curves.easeInOut,
74-
width: thumbRadius * 2,
75-
height: thumbRadius * 2,
79+
width: thumbDiameter,
80+
height: thumbDiameter,
7681
decoration: BoxDecoration(
7782
shape: BoxShape.circle,
7883
color: thumbColor,
7984
boxShadow: [
8085
BoxShadow(
81-
color: Colors.black.withOpacity(0.2),
86+
color: Colors.black.withValues(alpha: 0.2),
8287
blurRadius: 4,
8388
offset: const Offset(0, 2),
8489
),
@@ -105,32 +110,46 @@ class SettingsPage extends StatelessWidget {
105110

106111
static AccountRoute<void> buildRoute({required BuildContext context}) {
107112
return MaterialAccountWidgetRoute(
108-
context: context, page: const SettingsPage());
113+
context: context,
114+
page: const SettingsPage(),
115+
);
109116
}
110117

111118
@override
112119
Widget build(BuildContext context) {
113120
final zulipLocalizations = ZulipLocalizations.of(context);
114121
return Scaffold(
115122
appBar: ZulipAppBar(
116-
title: Text(zulipLocalizations.settingsPageTitle)),
117-
body: Column(children: [
118-
const _ThemeSetting(),
119-
const _BrowserPreferenceSetting(),
120-
const _VisitFirstUnreadSetting(),
121-
const _MarkReadOnScrollSetting(),
122-
if (GlobalSettingsStore.experimentalFeatureFlags.isNotEmpty)
123-
ListTile(
124-
title: Text(zulipLocalizations.experimentalFeatureSettingsPageTitle),
125-
onTap: () => Navigator.push(context,
126-
ExperimentalFeaturesPage.buildRoute()))
127-
]));
123+
title: Text(zulipLocalizations.settingsPageTitle),
124+
),
125+
body: Column(
126+
children: [
127+
const _ThemeSetting(),
128+
const _BrowserPreferenceSetting(),
129+
const _VisitFirstUnreadSetting(),
130+
const _MarkReadOnScrollSetting(),
131+
if (GlobalSettingsStore.experimentalFeatureFlags.isNotEmpty)
132+
ListTile(
133+
title: Text(zulipLocalizations.experimentalFeatureSettingsPageTitle),
134+
onTap: () => Navigator.push(
135+
context,
136+
ExperimentalFeaturesPage.buildRoute(),
137+
),
138+
),
139+
],
140+
),
141+
);
128142
}
129143
}
130144

131-
class _ThemeSetting extends StatelessWidget {
145+
class _ThemeSetting extends StatefulWidget {
132146
const _ThemeSetting();
133147

148+
@override
149+
State<_ThemeSetting> createState() => _ThemeSettingState();
150+
}
151+
152+
class _ThemeSettingState extends State<_ThemeSetting> {
134153
void _handleChange(BuildContext context, ThemeSetting? newThemeSetting) {
135154
final globalSettings = GlobalStoreWidget.settingsOf(context);
136155
globalSettings.setThemeSetting(newThemeSetting);
@@ -143,18 +162,24 @@ class _ThemeSetting extends StatelessWidget {
143162
return Column(
144163
children: [
145164
ListTile(title: Text(zulipLocalizations.themeSettingTitle)),
146-
for (final themeSettingOption in [null, ...ThemeSetting.values])
147-
RadioListTile<ThemeSetting?>.adaptive(
148-
title: Text(ThemeSetting.displayName(
149-
themeSetting: themeSettingOption,
150-
zulipLocalizations: zulipLocalizations)),
151-
value: themeSettingOption,
152-
// TODO(#1545) stop using the deprecated members
153-
// ignore: deprecated_member_use
154-
groupValue: globalSettings.themeSetting,
155-
// ignore: deprecated_member_use
156-
onChanged: (newValue) => _handleChange(context, newValue)),
157-
]);
165+
RadioGroup<ThemeSetting?>(
166+
groupValue: globalSettings.themeSetting,
167+
onChanged: (newValue) => _handleChange(context, newValue),
168+
child: Column(
169+
children: [
170+
for (final themeSettingOption in [null, ...ThemeSetting.values])
171+
RadioListTile<ThemeSetting?>(
172+
title: Text(ThemeSetting.displayName(
173+
themeSetting: themeSettingOption,
174+
zulipLocalizations: zulipLocalizations,
175+
)),
176+
value: themeSettingOption,
177+
),
178+
],
179+
),
180+
),
181+
],
182+
);
158183
}
159184
}
160185

@@ -165,7 +190,8 @@ class _BrowserPreferenceSetting extends StatelessWidget {
165190
final globalSettings = GlobalStoreWidget.settingsOf(context);
166191
globalSettings.setBrowserPreference(
167192
newOpenLinksWithInAppBrowser ? BrowserPreference.inApp
168-
: BrowserPreference.external);
193+
: BrowserPreference.external,
194+
);
169195
}
170196

171197
@override
@@ -194,9 +220,14 @@ class _VisitFirstUnreadSetting extends StatelessWidget {
194220
return ListTile(
195221
title: Text(zulipLocalizations.initialAnchorSettingTitle),
196222
subtitle: Text(VisitFirstUnreadSettingPage._valueDisplayName(
197-
globalSettings.visitFirstUnread, zulipLocalizations: zulipLocalizations)),
198-
onTap: () => Navigator.push(context,
199-
VisitFirstUnreadSettingPage.buildRoute()));
223+
globalSettings.visitFirstUnread,
224+
zulipLocalizations: zulipLocalizations,
225+
)),
226+
onTap: () => Navigator.push(
227+
context,
228+
VisitFirstUnreadSettingPage.buildRoute(),
229+
),
230+
);
200231
}
201232
}
202233

@@ -207,7 +238,8 @@ class VisitFirstUnreadSettingPage extends StatelessWidget {
207238
return MaterialWidgetRoute(page: const VisitFirstUnreadSettingPage());
208239
}
209240

210-
static String _valueDisplayName(VisitFirstUnreadSetting value, {
241+
static String _valueDisplayName(
242+
VisitFirstUnreadSetting value, {
211243
required ZulipLocalizations zulipLocalizations,
212244
}) {
213245
return switch (value) {
@@ -221,7 +253,7 @@ class VisitFirstUnreadSettingPage extends StatelessWidget {
221253
}
222254

223255
void _handleChange(BuildContext context, VisitFirstUnreadSetting? value) {
224-
if (value == null) return; // TODO(log); can this actually happen? how?
256+
if (value == null) return;
225257
final globalSettings = GlobalStoreWidget.settingsOf(context);
226258
globalSettings.setVisitFirstUnread(value);
227259
}
@@ -232,19 +264,28 @@ class VisitFirstUnreadSettingPage extends StatelessWidget {
232264
final globalSettings = GlobalStoreWidget.settingsOf(context);
233265
return Scaffold(
234266
appBar: AppBar(title: Text(zulipLocalizations.initialAnchorSettingTitle)),
235-
body: Column(children: [
236-
ListTile(title: Text(zulipLocalizations.initialAnchorSettingDescription)),
237-
for (final value in VisitFirstUnreadSetting.values)
238-
RadioListTile.adaptive(
239-
title: Text(_valueDisplayName(value,
240-
zulipLocalizations: zulipLocalizations)),
241-
value: value,
242-
// TODO(#1545) stop using the deprecated members
243-
// ignore: deprecated_member_use
267+
body: Column(
268+
children: [
269+
ListTile(title: Text(zulipLocalizations.initialAnchorSettingDescription)),
270+
RadioGroup<VisitFirstUnreadSetting>(
244271
groupValue: globalSettings.visitFirstUnread,
245-
// ignore: deprecated_member_use
246-
onChanged: (newValue) => _handleChange(context, newValue)),
247-
]));
272+
onChanged: (newValue) => _handleChange(context, newValue),
273+
child: Column(
274+
children: [
275+
for (final value in VisitFirstUnreadSetting.values)
276+
RadioListTile.adaptive(
277+
title: Text(_valueDisplayName(
278+
value,
279+
zulipLocalizations: zulipLocalizations,
280+
)),
281+
value: value,
282+
),
283+
],
284+
),
285+
),
286+
],
287+
),
288+
);
248289
}
249290
}
250291

@@ -258,9 +299,14 @@ class _MarkReadOnScrollSetting extends StatelessWidget {
258299
return ListTile(
259300
title: Text(zulipLocalizations.markReadOnScrollSettingTitle),
260301
subtitle: Text(MarkReadOnScrollSettingPage._valueDisplayName(
261-
globalSettings.markReadOnScroll, zulipLocalizations: zulipLocalizations)),
262-
onTap: () => Navigator.push(context,
263-
MarkReadOnScrollSettingPage.buildRoute()));
302+
globalSettings.markReadOnScroll,
303+
zulipLocalizations: zulipLocalizations,
304+
)),
305+
onTap: () => Navigator.push(
306+
context,
307+
MarkReadOnScrollSettingPage.buildRoute(),
308+
),
309+
);
264310
}
265311
}
266312

@@ -271,7 +317,8 @@ class MarkReadOnScrollSettingPage extends StatelessWidget {
271317
return MaterialWidgetRoute(page: const MarkReadOnScrollSettingPage());
272318
}
273319

274-
static String _valueDisplayName(MarkReadOnScrollSetting value, {
320+
static String _valueDisplayName(
321+
MarkReadOnScrollSetting value, {
275322
required ZulipLocalizations zulipLocalizations,
276323
}) {
277324
return switch (value) {
@@ -284,7 +331,8 @@ class MarkReadOnScrollSettingPage extends StatelessWidget {
284331
};
285332
}
286333

287-
static String? _valueDescription(MarkReadOnScrollSetting value, {
334+
static String? _valueDescription(
335+
MarkReadOnScrollSetting value, {
288336
required ZulipLocalizations zulipLocalizations,
289337
}) {
290338
return switch (value) {
@@ -296,7 +344,7 @@ class MarkReadOnScrollSettingPage extends StatelessWidget {
296344
}
297345

298346
void _handleChange(BuildContext context, MarkReadOnScrollSetting? value) {
299-
if (value == null) return; // TODO(log); can this actually happen? how?
347+
if (value == null) return;
300348
final globalSettings = GlobalStoreWidget.settingsOf(context);
301349
globalSettings.setMarkReadOnScroll(value);
302350
}
@@ -307,24 +355,35 @@ class MarkReadOnScrollSettingPage extends StatelessWidget {
307355
final globalSettings = GlobalStoreWidget.settingsOf(context);
308356
return Scaffold(
309357
appBar: AppBar(title: Text(zulipLocalizations.markReadOnScrollSettingTitle)),
310-
body: Column(children: [
311-
ListTile(title: Text(zulipLocalizations.markReadOnScrollSettingDescription)),
312-
for (final value in MarkReadOnScrollSetting.values)
313-
RadioListTile.adaptive(
314-
title: Text(_valueDisplayName(value,
315-
zulipLocalizations: zulipLocalizations)),
316-
subtitle: () {
317-
final result = _valueDescription(value,
318-
zulipLocalizations: zulipLocalizations);
319-
return result == null ? null : Text(result);
320-
}(),
321-
value: value,
322-
// TODO(#1545) stop using the deprecated members
323-
// ignore: deprecated_member_use
358+
body: Column(
359+
children: [
360+
ListTile(title: Text(zulipLocalizations.markReadOnScrollSettingDescription)),
361+
RadioGroup<MarkReadOnScrollSetting>(
324362
groupValue: globalSettings.markReadOnScroll,
325-
// ignore: deprecated_member_use
326-
onChanged: (newValue) => _handleChange(context, newValue)),
327-
]));
363+
onChanged: (newValue) => _handleChange(context, newValue),
364+
child: Column(
365+
children: [
366+
for (final value in MarkReadOnScrollSetting.values)
367+
RadioListTile.adaptive(
368+
title: Text(_valueDisplayName(
369+
value,
370+
zulipLocalizations: zulipLocalizations,
371+
)),
372+
subtitle: () {
373+
final result = _valueDescription(
374+
value,
375+
zulipLocalizations: zulipLocalizations,
376+
);
377+
return result == null ? null : Text(result);
378+
}(),
379+
value: value,
380+
),
381+
],
382+
),
383+
),
384+
],
385+
),
386+
);
328387
}
329388
}
330389

@@ -343,18 +402,23 @@ class ExperimentalFeaturesPage extends StatelessWidget {
343402
assert(flags.isNotEmpty);
344403
return Scaffold(
345404
appBar: AppBar(
346-
title: Text(zulipLocalizations.experimentalFeatureSettingsPageTitle)),
347-
body: Column(children: [
348-
ListTile(
349-
title: Text(zulipLocalizations.experimentalFeatureSettingsWarning)),
350-
for (final flag in flags)
405+
title: Text(zulipLocalizations.experimentalFeatureSettingsPageTitle),
406+
),
407+
body: Column(
408+
children: [
351409
ListTile(
352-
title: Text(flag.name), // no i18n; these are developer-facing settings
353-
trailing: FigmaToggle(
354-
value: globalSettings.getBool(flag),
355-
onChanged: (value) => globalSettings.setBool(flag, value),
356-
),
410+
title: Text(zulipLocalizations.experimentalFeatureSettingsWarning),
357411
),
358-
]));
412+
for (final flag in flags)
413+
ListTile(
414+
title: Text(flag.name), // no i18n; these are developer-facing settings
415+
trailing: FigmaToggle(
416+
value: globalSettings.getBool(flag),
417+
onChanged: (value) => globalSettings.setBool(flag, value),
418+
),
419+
),
420+
],
421+
),
422+
);
359423
}
360424
}

0 commit comments

Comments
 (0)