feat(ui): URL-shaped keyboard + lowercase enforcement for server host
The server-host TextField on the connect form accepts a hostname or
hostname:port pair (e.g. kr.teamspeak.app:9987). Two improvements:
1. keyboardType: TextInputType.url surfaces '.', '/', ':' on the
primary on-screen keyboard plane so the user does not have to
switch to the symbols pane mid-address. Matches iOS Safari's
URL bar.
2. textCapitalization.none + autocorrect/enableSuggestions=false
prevents iOS from auto-capitalising the first letter or
'correcting' 'kr.teamspeak.app' to something else.
3. inputFormatters belt-and-braces:
* deny whitespace (handles tab-indented paste)
* lowercase pipeline (handles uppercase paste)
4. Visual affordances: prefix dns icon + 'host[:port]' hint.
Nickname field intentionally unchanged \u2014 may contain unicode,
mixed case, spaces.
flutter build ios --release --no-codesign: 30.7 s, Runner.app
30.0 MB. flutter analyze clean (6 pre-existing deprecation
warnings on Radio.groupValue/onChanged).
This commit is contained in:
@@ -1294,11 +1294,42 @@ class _ConnectFormState extends State<_ConnectForm> {
|
|||||||
controller: widget.hostCtl,
|
controller: widget.hostCtl,
|
||||||
focusNode: _hostFocus,
|
focusNode: _hostFocus,
|
||||||
onTap: () => _hostFocus.requestFocus(),
|
onTap: () => _hostFocus.requestFocus(),
|
||||||
|
// Server addresses are URL-shaped: hostname or
|
||||||
|
// hostname:port, all-lowercase ASCII, never user-
|
||||||
|
// friendly prose. Configure the on-screen keyboard
|
||||||
|
// accordingly:
|
||||||
|
// * keyboardType: .url \u2014 surfaces ".", "/",
|
||||||
|
// ":" on the primary keyboard plane so the user
|
||||||
|
// doesn't have to switch to the symbols pane to
|
||||||
|
// type kr.teamspeak.app:9987.
|
||||||
|
// * textInputAction: .next \u2014 return key advances
|
||||||
|
// to the nickname field.
|
||||||
|
// * autocorrect / enableSuggestions: false \u2014 iOS
|
||||||
|
// should not autocorrect 'kr.teamspeak.app' to
|
||||||
|
// 'kr.teamspeak.lap' or suggest 'KR' in caps.
|
||||||
|
// * textCapitalization: .none \u2014 don't capitalise the
|
||||||
|
// first letter the way iOS does for sentences.
|
||||||
|
// * inputFormatters: deny whitespace and uppercase\u2014
|
||||||
|
// belt-and-braces in case the user pastes from a
|
||||||
|
// formatted source (e.g. tab-indented copy).
|
||||||
|
keyboardType: TextInputType.url,
|
||||||
|
textCapitalization: TextCapitalization.none,
|
||||||
textInputAction: TextInputAction.next,
|
textInputAction: TextInputAction.next,
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
enableSuggestions: false,
|
enableSuggestions: false,
|
||||||
|
inputFormatters: [
|
||||||
|
FilteringTextInputFormatter.deny(RegExp(r'\s')),
|
||||||
|
TextInputFormatter.withFunction(
|
||||||
|
(oldValue, newValue) => newValue.copyWith(
|
||||||
|
text: newValue.text.toLowerCase(),
|
||||||
|
selection: newValue.selection,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: l10n.fieldServerHost,
|
labelText: l10n.fieldServerHost,
|
||||||
|
hintText: 'host[:port]',
|
||||||
|
prefixIcon: const Icon(Icons.dns_outlined),
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user