-
Notifications
You must be signed in to change notification settings - Fork 309
accounts ui: Offer Flutter's [LicensePage], to acknowledge software used by the app #101
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
89c4f44
to
99ef5ab
Compare
(Fixed a formatting nit I just noticed.) |
For a proposed UI to show the user the list of licenses tracked by [LicenseRegistry], see PR zulip#101.
Design-wise, I think a standard solution here would be an "overflow menu" at the end of the app bar. See mentions of "overflow" in: That would also have the advantage of imposing less of a constraint on how we lay out the list of accounts. Putting them at the top feels likely to be annoying for the common case of one or two accounts, for people using a phone and having to reach up to the top with their thumb. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than "help", I think I'd file this under an "about" area. So in full:
- in app bar, overflow menu has an item "About Zulip"
- That item goes to a screen that's just a settings-style page, with a couple of items on it
- One is "Zulip" and a version number or something — just to help it feel less empty
- The other is "Open-source licenses"
- That one uses
showLicensePage
to go to the Flutter-provided license page.
lib/widgets/app.dart
Outdated
Builder(builder: (context) => | ||
MediaQuery.removePadding( | ||
context: context, | ||
removeBottom: true, | ||
child: Expanded( | ||
child: SingleChildScrollView( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This gets pretty long before it becomes clear what the payload is (with for (… in globalStore.accountEntries)
and _buildAccountItem
). It'd be good to pull out as a local variable.
Sure, I'll update this soon.
I'm wary of putting too much design work into a new "About" page at this early stage, when |
Yeah, definitely would want to keep it to something quick. I think that plan sounds like a good one. If you find that the tools at hand don't seem to give you a quick way to get something reasonable by that approach, then |
For a proposed UI to show the user the list of licenses tracked by [LicenseRegistry], see PR zulip#101.
99ef5ab
to
cfc3354
Compare
Thanks for the review! Revision pushed. |
For a proposed UI to show the user the list of licenses tracked by [LicenseRegistry], see PR zulip#101.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! This UI looks good. Two small comments in the code.
lib/widgets/app.dart
Outdated
|
||
@override | ||
Widget build(BuildContext context) { | ||
final navigator = Navigator.of(context); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move this inside the onSelected
callback. That way it doesn't add any work to do at build time, and we only do the work when the user has chosen to interact with the menu, which is much less common.
… Hm, in fact: I was about to write that this method is presumably cheap and so it doesn't much matter in this case and it's mostly a matter of maintaining good patterns — but it turns out that [Navigator.of] is actually expensive! From its docs:
This method can be expensive (it walks the element tree).
So yeah, definitely to be avoided in the build phase, and invoked only when handling an actual user interaction that needs it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(And I guess that also makes an illustration of why deferring things from the build method that are really only needed in event handlers, even when one presumes they're probably cheap, is a good pattern — if you haven't carefully checked that they're cheap, you might be surprised.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting! In the common case of an event handler that just has to walk the tree once (it doesn't need the NavigatorState
for two or more tasks), it might make sense to favor Navigator.push(context, …)
over Navigator.of(context).push(…)
, which would prevent the tree-walking work from straying outside the event handler, as it did in this revision.
lib/widgets/about_zulip.dart
Outdated
if (_packageInfo != null) ...[ | ||
ListTile(title: const Text('App version'), subtitle: Text(_packageInfo!.version)), | ||
], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, awkward that this info is async.
If we do find ourselves showing a frame or two where we don't have the version number yet, I think it'd be better to still show this list item and just have some kind of placeholder for the actual value, rather than leave out the whole list item. That should reduce the amount of visible jank.
So e.g.:
if (_packageInfo != null) ...[ | |
ListTile(title: const Text('App version'), subtitle: Text(_packageInfo!.version)), | |
], | |
ListTile( | |
title: const Text('App version'), | |
subtitle: Text(_packageInfo?.version ?? '(…)')), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To simulate that situation, you can insert an await Future.delayed(…)
:
final result = await PackageInfo.fromPlatform();
+ await Future.delayed(Duration(milliseconds: 20));
setState(() {
_packageInfo = result;
Like we do with the realm-url and username/password screens, which also have this pattern: body: SafeArea( minimum: const EdgeInsets.all(8), child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 400),
These changes happened automatically for me when building for macOS.
Thanks for the review! Revision pushed. |
cfc3354
to
4d6f561
Compare
Thanks! Looks good; merging. |
(EDITED following #101 (review) . Notably, this revision doesn't address #100.)
From this new page, the user can tap a button to show [LicensePage],
so resolving #99.
Fixes: #99