Skip to content

Commit a246461

Browse files
committed
accounts-ui: Make accounts list scrollable
Before this fix, the list of accounts did not scroll when they were more than a screenful and would show an overflow error on the screen. After this fix, the list of accounts scrolls properly with no overflow error and without shifting other content offscreen. Fixes: #100
1 parent 711bc69 commit a246461

File tree

2 files changed

+122
-7
lines changed

2 files changed

+122
-7
lines changed

lib/widgets/app.dart

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,15 @@ class ChooseAccountPage extends StatelessWidget {
194194
child: Center(
195195
child: ConstrainedBox(
196196
constraints: const BoxConstraints(maxWidth: 400),
197-
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
198-
for (final (:accountId, :account) in globalStore.accountEntries)
199-
_buildAccountItem(context,
200-
accountId: accountId,
201-
title: Text(account.realmUrl.toString()),
202-
subtitle: Text(account.email)),
197+
child: Column(mainAxisSize: MainAxisSize.min, children: [
198+
Flexible(child: SingleChildScrollView(
199+
child: Column(mainAxisSize: MainAxisSize.min, children: [
200+
for (final (:accountId, :account) in globalStore.accountEntries)
201+
_buildAccountItem(context,
202+
accountId: accountId,
203+
title: Text(account.realmUrl.toString()),
204+
subtitle: Text(account.email)),
205+
]))),
203206
const SizedBox(height: 12),
204207
ElevatedButton(
205208
onPressed: () => Navigator.push(context,

test/widgets/app_test.dart

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import 'package:checks/checks.dart';
2-
import 'package:flutter/widgets.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:flutter_gen/gen_l10n/zulip_localizations.dart';
34
import 'package:flutter_test/flutter_test.dart';
5+
import 'package:zulip/model/database.dart';
46
import 'package:zulip/widgets/app.dart';
57
import 'package:zulip/widgets/inbox.dart';
68
import 'package:zulip/widgets/page.dart';
9+
import 'package:zulip/widgets/store.dart';
710

811
import '../example_data.dart' as eg;
912
import '../model/binding.dart';
@@ -51,4 +54,113 @@ void main() {
5154
]);
5255
});
5356
});
57+
58+
group('ChooseAccountPage', () {
59+
Future<void> setupChooseAccountPage(WidgetTester tester, {
60+
required List<Account>? accounts,
61+
}) async {
62+
addTearDown(testBinding.reset);
63+
64+
for (final account in accounts ?? []) {
65+
await testBinding.globalStore
66+
.insertAccount(account.toCompanion(false));
67+
}
68+
69+
await tester.pumpWidget(
70+
const MaterialApp(
71+
localizationsDelegates: ZulipLocalizations.localizationsDelegates,
72+
supportedLocales: ZulipLocalizations.supportedLocales,
73+
home: GlobalStoreWidget(
74+
child: ChooseAccountPage())));
75+
76+
// global store gets loaded
77+
await tester.pumpAndSettle();
78+
}
79+
80+
List<Account> generateAccounts(int count) {
81+
return List.generate(count, (i) => eg.account(
82+
id: i,
83+
user: eg.user(fullName: 'User $i', email: 'user$i@example'),
84+
apiKey: 'user${i}apikey',
85+
));
86+
}
87+
88+
Finder findAccount(Account account) => find.text(account.email).hitTestable();
89+
90+
Finder findButton<T extends ButtonStyleButton>({required String withText}) {
91+
return find
92+
.descendant(of: find.bySubtype<T>(), matching: find.text(withText))
93+
.hitTestable();
94+
}
95+
96+
void checkAccountShown(Account account, {required bool expected}) {
97+
check(findAccount(account).evaluate()).length.equals(expected ? 1 : 0);
98+
}
99+
100+
void checkButtonShown<T extends ButtonStyleButton>({
101+
required String withText,
102+
required bool expected,
103+
}) {
104+
check(findButton<T>(withText: withText).evaluate())
105+
.length.equals(expected ? 1 : 0);
106+
}
107+
108+
testWidgets('accounts list is scrollable when more than a screenful',
109+
(tester) async {
110+
final accounts = generateAccounts(15);
111+
await setupChooseAccountPage(tester, accounts: accounts);
112+
113+
// Accounts list is more than a screenful
114+
// * First account is shown
115+
// * Last account is out of view
116+
checkAccountShown(accounts.first, expected: true);
117+
checkAccountShown(accounts.last, expected: false);
118+
119+
// Button to add an account is visible
120+
// and not moved offscreen by the long list of accounts
121+
checkButtonShown(withText: 'Add an account', expected: true);
122+
123+
// Accounts list is scrollable to the bottom
124+
await tester.scrollUntilVisible(findAccount(accounts.last), 50);
125+
checkAccountShown(accounts.last, expected: true);
126+
});
127+
128+
testWidgets('with just one account, the layout is centered',
129+
(tester) async {
130+
final account = eg.selfAccount;
131+
await setupChooseAccountPage(tester, accounts: [account]);
132+
133+
const buttonText = 'Add an account';
134+
checkAccountShown(account, expected: true);
135+
checkButtonShown(withText: buttonText, expected: true);
136+
137+
final screenHeight =
138+
(tester.view.physicalSize / tester.view.devicePixelRatio).height;
139+
final thirdOfScreenHeight = screenHeight / 3;
140+
141+
final accountRect = tester.getRect(findAccount(account));
142+
check(accountRect.top).isGreaterThan(thirdOfScreenHeight);
143+
check(accountRect.bottom).isLessThan(screenHeight - thirdOfScreenHeight);
144+
145+
final buttonRect = tester.getRect(findButton(withText: buttonText));
146+
check(buttonRect.top).isGreaterThan(thirdOfScreenHeight);
147+
check(buttonRect.bottom).isLessThan(screenHeight - thirdOfScreenHeight);
148+
});
149+
150+
testWidgets('with no accounts, the Add an Account button is centered',
151+
(tester) async {
152+
await setupChooseAccountPage(tester, accounts: null);
153+
154+
const buttonText = 'Add an account';
155+
checkButtonShown(withText: buttonText, expected: true);
156+
157+
final screenHeight =
158+
(tester.view.physicalSize / tester.view.devicePixelRatio).height;
159+
final thirdOfScreenHeight = screenHeight / 3;
160+
161+
final buttonRect = tester.getRect(findButton(withText: buttonText));
162+
check(buttonRect.top).isGreaterThan(thirdOfScreenHeight);
163+
check(buttonRect.bottom).isLessThan(screenHeight - thirdOfScreenHeight);
164+
});
165+
});
54166
}

0 commit comments

Comments
 (0)