Merge pull request #208 from krawieck/feature/better-keyboard
This commit is contained in:
commit
6df83943fd
15
CHANGELOG.md
15
CHANGELOG.md
|
@ -3,13 +3,18 @@
|
|||
### Changed
|
||||
|
||||
- Disable commenting on locked posts
|
||||
|
||||
### Fixed
|
||||
|
||||
- When writing a comment the parent text is now selectable
|
||||
- Text of a post is now selectable
|
||||
- Enhanced keyboard experience
|
||||
- appropriate keyboard types are opened
|
||||
- correct capitalization
|
||||
- added text input hints for things like password managers
|
||||
- Account actions in settings are more obvious to access: long press an account/instance to see possible actions such as setting as default or removal
|
||||
|
||||
### Added
|
||||
|
||||
- When writing a comment, the parent text is now selectable
|
||||
- Text of a post is now selectable
|
||||
- Tapping outside of a text input hides the keyboard
|
||||
|
||||
## v0.4.1 - 2021-04-06
|
||||
|
||||
### Fixed
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:keyboard_dismisser/keyboard_dismisser.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'hooks/stores.dart';
|
||||
|
@ -41,15 +42,17 @@ class MyApp extends HookWidget {
|
|||
Widget build(BuildContext context) {
|
||||
final configStore = useConfigStore();
|
||||
|
||||
return MaterialApp(
|
||||
title: 'lemmur',
|
||||
supportedLocales: L10n.supportedLocales,
|
||||
localizationsDelegates: L10n.localizationsDelegates,
|
||||
themeMode: configStore.theme,
|
||||
darkTheme: configStore.amoledDarkMode ? amoledTheme : darkTheme,
|
||||
locale: configStore.locale,
|
||||
theme: lightTheme,
|
||||
home: const MyHomePage(),
|
||||
return KeyboardDismisser(
|
||||
child: MaterialApp(
|
||||
title: 'lemmur',
|
||||
supportedLocales: L10n.supportedLocales,
|
||||
localizationsDelegates: L10n.localizationsDelegates,
|
||||
themeMode: configStore.theme,
|
||||
darkTheme: configStore.amoledDarkMode ? amoledTheme : darkTheme,
|
||||
locale: configStore.locale,
|
||||
theme: lightTheme,
|
||||
home: const MyHomePage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ class AddAccountPage extends HookWidget {
|
|||
|
||||
final usernameController = useListenable(useTextEditingController());
|
||||
final passwordController = useListenable(useTextEditingController());
|
||||
final passwordFocusNode = useFocusNode();
|
||||
final accountsStore = useAccountsStore();
|
||||
|
||||
final loading = useDelayedLoading();
|
||||
|
@ -54,96 +55,109 @@ class AddAccountPage extends HookWidget {
|
|||
loading.cancel();
|
||||
}
|
||||
|
||||
final handleSubmit =
|
||||
usernameController.text.isEmpty || passwordController.text.isEmpty
|
||||
? null
|
||||
: loading.pending
|
||||
? () {}
|
||||
: handleOnAdd;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: const CloseButton(),
|
||||
title: const Text('Add account'),
|
||||
),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(15),
|
||||
children: [
|
||||
if (icon.value == null)
|
||||
const SizedBox(height: 150)
|
||||
else
|
||||
SizedBox(
|
||||
height: 150,
|
||||
child: FullscreenableImage(
|
||||
url: icon.value!,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: icon.value!,
|
||||
errorWidget: (_, __, ___) => const SizedBox.shrink(),
|
||||
body: AutofillGroup(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(15),
|
||||
children: [
|
||||
if (icon.value == null)
|
||||
const SizedBox(height: 150)
|
||||
else
|
||||
SizedBox(
|
||||
height: 150,
|
||||
child: FullscreenableImage(
|
||||
url: icon.value!,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: icon.value!,
|
||||
errorWidget: (_, __, ___) => const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
RadioPicker<String>(
|
||||
title: 'select instance',
|
||||
values: accountsStore.instances.toList(),
|
||||
groupValue: selectedInstance.value,
|
||||
onChanged: (value) => selectedInstance.value = value,
|
||||
buttonBuilder: (context, displayValue, onPressed) => TextButton(
|
||||
onPressed: onPressed,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(displayValue),
|
||||
const Icon(Icons.arrow_drop_down),
|
||||
],
|
||||
RadioPicker<String>(
|
||||
title: 'select instance',
|
||||
values: accountsStore.instances.toList(),
|
||||
groupValue: selectedInstance.value,
|
||||
onChanged: (value) => selectedInstance.value = value,
|
||||
buttonBuilder: (context, displayValue, onPressed) => TextButton(
|
||||
onPressed: onPressed,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(displayValue),
|
||||
const Icon(Icons.arrow_drop_down),
|
||||
],
|
||||
),
|
||||
),
|
||||
trailing: ListTile(
|
||||
leading: const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Icon(Icons.add),
|
||||
),
|
||||
title: const Text('Add instance'),
|
||||
onTap: () async {
|
||||
final value = await showCupertinoModalPopup<String>(
|
||||
context: context,
|
||||
builder: (context) => const AddInstancePage(),
|
||||
);
|
||||
Navigator.of(context).pop(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
trailing: ListTile(
|
||||
leading: const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Icon(Icons.add),
|
||||
),
|
||||
title: const Text('Add instance'),
|
||||
onTap: () async {
|
||||
final value = await showCupertinoModalPopup<String>(
|
||||
context: context,
|
||||
builder: (context) => const AddInstancePage(),
|
||||
);
|
||||
Navigator.of(context).pop(value);
|
||||
},
|
||||
TextField(
|
||||
autofocus: true,
|
||||
controller: usernameController,
|
||||
autofillHints: const [
|
||||
AutofillHints.email,
|
||||
AutofillHints.username
|
||||
],
|
||||
onSubmitted: (_) => passwordFocusNode.requestFocus(),
|
||||
decoration: InputDecoration(
|
||||
labelText: L10n.of(context)!.email_or_username),
|
||||
),
|
||||
),
|
||||
// TODO: add support for password managers
|
||||
TextField(
|
||||
autofocus: true,
|
||||
controller: usernameController,
|
||||
decoration:
|
||||
InputDecoration(labelText: L10n.of(context)!.email_or_username),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
TextField(
|
||||
controller: passwordController,
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(labelText: L10n.of(context)!.password),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: usernameController.text.isEmpty ||
|
||||
passwordController.text.isEmpty
|
||||
? null
|
||||
: loading.pending
|
||||
? () {}
|
||||
: handleOnAdd,
|
||||
child: !loading.loading
|
||||
? const Text('Sign in')
|
||||
: SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
valueColor:
|
||||
AlwaysStoppedAnimation<Color>(theme.canvasColor),
|
||||
const SizedBox(height: 5),
|
||||
TextField(
|
||||
controller: passwordController,
|
||||
obscureText: true,
|
||||
focusNode: passwordFocusNode,
|
||||
onSubmitted: (_) => handleSubmit?.call(),
|
||||
autofillHints: const [AutofillHints.password],
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
decoration:
|
||||
InputDecoration(labelText: L10n.of(context)!.password),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: handleSubmit,
|
||||
child: !loading.loading
|
||||
? const Text('Sign in')
|
||||
: SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
valueColor:
|
||||
AlwaysStoppedAnimation<Color>(theme.canvasColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
// TODO: extract to LemmyUrls or something
|
||||
ul.launch('https://${selectedInstance.value}/login');
|
||||
},
|
||||
child: const Text('Register'),
|
||||
),
|
||||
],
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
// TODO: extract to LemmyUrls or something
|
||||
ul.launch('https://${selectedInstance.value}/login');
|
||||
},
|
||||
child: const Text('Register'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -47,7 +47,9 @@ class AddInstancePage extends HookWidget {
|
|||
instanceController.removeListener(debounce);
|
||||
};
|
||||
}, []);
|
||||
|
||||
final inst = normalizeInstanceHost(instanceController.text);
|
||||
|
||||
handleOnAdd() async {
|
||||
try {
|
||||
await accountsStore.addInstance(inst, assumeValid: true);
|
||||
|
@ -59,6 +61,8 @@ class AddInstancePage extends HookWidget {
|
|||
}
|
||||
}
|
||||
|
||||
final handleAdd = isSite.value == true ? handleOnAdd : null;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: const CloseButton(),
|
||||
|
@ -97,6 +101,9 @@ class AddInstancePage extends HookWidget {
|
|||
child: TextField(
|
||||
autofocus: true,
|
||||
controller: instanceController,
|
||||
autofillHints: const [AutofillHints.url],
|
||||
keyboardType: TextInputType.url,
|
||||
onSubmitted: (_) => handleAdd?.call(),
|
||||
autocorrect: false,
|
||||
decoration: const InputDecoration(labelText: 'instance url'),
|
||||
),
|
||||
|
@ -108,7 +115,7 @@ class AddInstancePage extends HookWidget {
|
|||
child: SizedBox(
|
||||
height: 40,
|
||||
child: ElevatedButton(
|
||||
onPressed: isSite.value == true ? handleOnAdd : null,
|
||||
onPressed: handleAdd,
|
||||
child: !debounce.loading
|
||||
? const Text('Add')
|
||||
: SizedBox(
|
||||
|
|
|
@ -66,6 +66,9 @@ class CreatePostPage extends HookWidget {
|
|||
final pictrsDeleteToken = useState<PictrsUploadFile?>(null);
|
||||
final loggedInAction = useLoggedInAction(selectedInstance.value);
|
||||
|
||||
final titleFocusNode = useFocusNode();
|
||||
final bodyFocusNode = useFocusNode();
|
||||
|
||||
final allCommunitiesSnap = useMemoFuture(
|
||||
() => LemmyApiV3(selectedInstance.value)
|
||||
.run(ListCommunities(
|
||||
|
@ -155,77 +158,6 @@ class CreatePostPage extends HookWidget {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: use lazy autocomplete
|
||||
final communitiesDropdown = InputDecorator(
|
||||
decoration: const InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 1, horizontal: 20),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)))),
|
||||
child: DropdownButtonHideUnderline(
|
||||
child: DropdownButton<int>(
|
||||
value: selectedCommunity.value?.community.id,
|
||||
hint: Text(L10n.of(context)!.community),
|
||||
onChanged: (communityId) => selectedCommunity.value =
|
||||
allCommunitiesSnap.data
|
||||
?.firstWhere((e) => e.community.id == communityId),
|
||||
items: communitiesList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final url = Row(children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
enabled: pictrsDeleteToken.value == null,
|
||||
controller: urlController,
|
||||
decoration: InputDecoration(
|
||||
labelText: L10n.of(context)!.url,
|
||||
suffixIcon: const Icon(Icons.link),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
IconButton(
|
||||
icon: imageUploadLoading.value
|
||||
? const CircularProgressIndicator()
|
||||
: Icon(pictrsDeleteToken.value == null
|
||||
? Icons.add_photo_alternate
|
||||
: Icons.close),
|
||||
onPressed: pictrsDeleteToken.value == null
|
||||
? loggedInAction(uploadPicture)
|
||||
: () => removePicture(pictrsDeleteToken.value!),
|
||||
tooltip:
|
||||
pictrsDeleteToken.value == null ? 'Add picture' : 'Delete picture',
|
||||
)
|
||||
]);
|
||||
|
||||
final title = TextField(
|
||||
controller: titleController,
|
||||
minLines: 1,
|
||||
maxLines: 2,
|
||||
decoration: InputDecoration(labelText: L10n.of(context)!.title),
|
||||
);
|
||||
|
||||
final body = IndexedStack(
|
||||
index: showFancy.value ? 1 : 0,
|
||||
children: [
|
||||
TextField(
|
||||
controller: bodyController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
maxLines: null,
|
||||
minLines: 5,
|
||||
decoration: InputDecoration(labelText: L10n.of(context)!.body),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: MarkdownText(
|
||||
bodyController.text,
|
||||
instanceHost: selectedInstance.value,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
handleSubmit(Jwt token) async {
|
||||
if (selectedCommunity.value == null || titleController.text.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||
|
@ -256,6 +188,88 @@ class CreatePostPage extends HookWidget {
|
|||
delayed.cancel();
|
||||
}
|
||||
|
||||
// TODO: use lazy autocomplete
|
||||
final communitiesDropdown = InputDecorator(
|
||||
decoration: const InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 1, horizontal: 20),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)))),
|
||||
child: DropdownButtonHideUnderline(
|
||||
child: DropdownButton<int>(
|
||||
value: selectedCommunity.value?.community.id,
|
||||
hint: Text(L10n.of(context)!.community),
|
||||
onChanged: (communityId) => selectedCommunity.value =
|
||||
allCommunitiesSnap.data
|
||||
?.firstWhere((e) => e.community.id == communityId),
|
||||
items: communitiesList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final url = Row(children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
enabled: pictrsDeleteToken.value == null,
|
||||
controller: urlController,
|
||||
autofillHints: const [AutofillHints.url],
|
||||
keyboardType: TextInputType.url,
|
||||
onSubmitted: (_) => titleFocusNode.requestFocus(),
|
||||
decoration: InputDecoration(
|
||||
labelText: L10n.of(context)!.url,
|
||||
suffixIcon: const Icon(Icons.link),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
IconButton(
|
||||
icon: imageUploadLoading.value
|
||||
? const CircularProgressIndicator()
|
||||
: Icon(pictrsDeleteToken.value == null
|
||||
? Icons.add_photo_alternate
|
||||
: Icons.close),
|
||||
onPressed: pictrsDeleteToken.value == null
|
||||
? loggedInAction(uploadPicture)
|
||||
: () => removePicture(pictrsDeleteToken.value!),
|
||||
tooltip:
|
||||
pictrsDeleteToken.value == null ? 'Add picture' : 'Delete picture',
|
||||
)
|
||||
]);
|
||||
|
||||
final title = TextField(
|
||||
controller: titleController,
|
||||
focusNode: titleFocusNode,
|
||||
keyboardType: TextInputType.text,
|
||||
textCapitalization: TextCapitalization.sentences,
|
||||
onSubmitted: (_) => bodyFocusNode.requestFocus(),
|
||||
minLines: 1,
|
||||
maxLines: 2,
|
||||
decoration: InputDecoration(labelText: L10n.of(context)!.title),
|
||||
);
|
||||
|
||||
final body = IndexedStack(
|
||||
index: showFancy.value ? 1 : 0,
|
||||
children: [
|
||||
TextField(
|
||||
controller: bodyController,
|
||||
focusNode: bodyFocusNode,
|
||||
keyboardType: TextInputType.multiline,
|
||||
textCapitalization: TextCapitalization.sentences,
|
||||
onSubmitted: (_) =>
|
||||
delayed.pending ? () {} : loggedInAction(handleSubmit),
|
||||
maxLines: null,
|
||||
minLines: 5,
|
||||
decoration: InputDecoration(labelText: L10n.of(context)!.body),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: MarkdownText(
|
||||
bodyController.text,
|
||||
instanceHost: selectedInstance.value,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: const CloseButton(),
|
||||
|
|
|
@ -91,6 +91,13 @@ class _ManageAccount extends HookWidget {
|
|||
|
||||
final deleteAccountPasswordController = useTextEditingController();
|
||||
|
||||
final bioFocusNode = useFocusNode();
|
||||
final emailFocusNode = useFocusNode();
|
||||
final matrixUserFocusNode = useFocusNode();
|
||||
final newPasswordFocusNode = useFocusNode();
|
||||
final verifyPasswordFocusNode = useFocusNode();
|
||||
final oldPasswordFocusNode = useFocusNode();
|
||||
|
||||
final token = accountsStore.tokenFor(user.instanceHost, user.person.name)!;
|
||||
|
||||
handleSubmit() async {
|
||||
|
@ -155,6 +162,8 @@ class _ManageAccount extends HookWidget {
|
|||
const SizedBox(height: 10),
|
||||
TextField(
|
||||
controller: deleteAccountPasswordController,
|
||||
autofillHints: const [AutofillHints.password],
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
obscureText: true,
|
||||
decoration:
|
||||
InputDecoration(hintText: L10n.of(context)!.password),
|
||||
|
@ -219,37 +228,64 @@ class _ManageAccount extends HookWidget {
|
|||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(L10n.of(context)!.display_name, style: theme.textTheme.headline6),
|
||||
TextField(controller: displayNameController),
|
||||
TextField(
|
||||
controller: displayNameController,
|
||||
onSubmitted: (_) => bioFocusNode.requestFocus(),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(L10n.of(context)!.bio, style: theme.textTheme.headline6),
|
||||
TextField(
|
||||
controller: bioController,
|
||||
focusNode: bioFocusNode,
|
||||
textCapitalization: TextCapitalization.sentences,
|
||||
onSubmitted: (_) => emailFocusNode.requestFocus(),
|
||||
minLines: 4,
|
||||
maxLines: 10,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(L10n.of(context)!.email, style: theme.textTheme.headline6),
|
||||
TextField(controller: emailController),
|
||||
TextField(
|
||||
focusNode: emailFocusNode,
|
||||
controller: emailController,
|
||||
autofillHints: const [AutofillHints.email],
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
onSubmitted: (_) => matrixUserFocusNode.requestFocus(),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(L10n.of(context)!.matrix_user, style: theme.textTheme.headline6),
|
||||
TextField(controller: matrixUserController),
|
||||
TextField(
|
||||
focusNode: matrixUserFocusNode,
|
||||
controller: matrixUserController,
|
||||
onSubmitted: (_) => newPasswordFocusNode.requestFocus(),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(L10n.of(context)!.new_password, style: theme.textTheme.headline6),
|
||||
TextField(
|
||||
focusNode: newPasswordFocusNode,
|
||||
controller: newPasswordController,
|
||||
autofillHints: const [AutofillHints.newPassword],
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
obscureText: true,
|
||||
onSubmitted: (_) => verifyPasswordFocusNode.requestFocus(),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(L10n.of(context)!.verify_password,
|
||||
style: theme.textTheme.headline6),
|
||||
TextField(
|
||||
focusNode: verifyPasswordFocusNode,
|
||||
controller: newPasswordVerifyController,
|
||||
autofillHints: const [AutofillHints.newPassword],
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
obscureText: true,
|
||||
onSubmitted: (_) => oldPasswordFocusNode.requestFocus(),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(L10n.of(context)!.old_password, style: theme.textTheme.headline6),
|
||||
TextField(
|
||||
focusNode: oldPasswordFocusNode,
|
||||
controller: oldPasswordController,
|
||||
autofillHints: const [AutofillHints.password],
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
obscureText: true,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
|
|
@ -31,47 +31,51 @@ class SearchTab extends HookWidget {
|
|||
);
|
||||
}
|
||||
|
||||
handleSearch() => searchInputController.text.isNotEmpty
|
||||
? goTo(
|
||||
context,
|
||||
(context) => SearchResultsPage(
|
||||
instanceHost: instanceHost.value!,
|
||||
query: searchInputController.text,
|
||||
),
|
||||
)
|
||||
: null;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: GestureDetector(
|
||||
onTapDown: (_) => primaryFocus?.unfocus(),
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
children: [
|
||||
TextField(
|
||||
controller: searchInputController,
|
||||
textAlign: TextAlign.center,
|
||||
decoration: InputDecoration(hintText: L10n.of(context)!.search),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text('instance:',
|
||||
style: Theme.of(context).textTheme.subtitle1),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
children: [
|
||||
TextField(
|
||||
controller: searchInputController,
|
||||
keyboardType: TextInputType.text,
|
||||
textAlign: TextAlign.center,
|
||||
onSubmitted: (_) => handleSearch(),
|
||||
decoration: InputDecoration(hintText: L10n.of(context)!.search),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text('instance:',
|
||||
style: Theme.of(context).textTheme.subtitle1),
|
||||
),
|
||||
Expanded(
|
||||
child: RadioPicker<String>(
|
||||
values: accStore.instances.toList(),
|
||||
groupValue: instanceHost.value!,
|
||||
onChanged: (value) => instanceHost.value = value,
|
||||
),
|
||||
Expanded(
|
||||
child: RadioPicker<String>(
|
||||
values: accStore.instances.toList(),
|
||||
groupValue: instanceHost.value!,
|
||||
onChanged: (value) => instanceHost.value = value,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (searchInputController.text.isNotEmpty)
|
||||
ElevatedButton(
|
||||
onPressed: () => goTo(
|
||||
context,
|
||||
(c) => SearchResultsPage(
|
||||
instanceHost: instanceHost.value!,
|
||||
query: searchInputController.text,
|
||||
)),
|
||||
child: Text(L10n.of(context)!.search),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (searchInputController.text.isNotEmpty)
|
||||
ElevatedButton(
|
||||
onPressed: handleSearch,
|
||||
child: Text(L10n.of(context)!.search),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -86,6 +86,7 @@ class WriteMessagePage extends HookWidget {
|
|||
TextField(
|
||||
controller: bodyController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
textCapitalization: TextCapitalization.sentences,
|
||||
maxLines: null,
|
||||
minLines: 5,
|
||||
autofocus: true,
|
||||
|
|
|
@ -97,6 +97,8 @@ class WriteComment extends HookWidget {
|
|||
children: [
|
||||
TextField(
|
||||
controller: controller,
|
||||
keyboardType: TextInputType.multiline,
|
||||
textCapitalization: TextCapitalization.sentences,
|
||||
autofocus: true,
|
||||
minLines: 5,
|
||||
maxLines: null,
|
||||
|
|
|
@ -392,6 +392,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
keyboard_dismisser:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: keyboard_dismisser
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
latinize:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -47,6 +47,7 @@ dependencies:
|
|||
intl: ^0.17.0
|
||||
matrix4_transform: ^2.0.0
|
||||
json_annotation: ^4.0.1
|
||||
keyboard_dismisser: ^2.0.0
|
||||
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
|
Loading…
Reference in New Issue