mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-16 02:35:59 -07:00
chore: Improved custom keyboard logic
This commit is contained in:
parent
07972ea5ee
commit
117d873683
10 changed files with 203 additions and 202 deletions
|
|
@ -33,19 +33,21 @@ class DashboardNotifier extends StateNotifier<HomeModel> {
|
||||||
ImageType.banner,
|
ImageType.banner,
|
||||||
}.toList();
|
}.toList();
|
||||||
|
|
||||||
|
final fieldsToFetch = [
|
||||||
|
ItemFields.parentid,
|
||||||
|
ItemFields.mediastreams,
|
||||||
|
ItemFields.mediasources,
|
||||||
|
ItemFields.candelete,
|
||||||
|
ItemFields.candownload,
|
||||||
|
ItemFields.primaryimageaspectratio,
|
||||||
|
ItemFields.overview,
|
||||||
|
ItemFields.genres,
|
||||||
|
];
|
||||||
|
|
||||||
if (viewTypes.containsAny([CollectionType.movies, CollectionType.tvshows])) {
|
if (viewTypes.containsAny([CollectionType.movies, CollectionType.tvshows])) {
|
||||||
final resumeVideoResponse = await api.usersUserIdItemsResumeGet(
|
final resumeVideoResponse = await api.usersUserIdItemsResumeGet(
|
||||||
enableImageTypes: imagesToFetch,
|
enableImageTypes: imagesToFetch,
|
||||||
fields: [
|
fields: fieldsToFetch,
|
||||||
ItemFields.parentid,
|
|
||||||
ItemFields.mediastreams,
|
|
||||||
ItemFields.mediasources,
|
|
||||||
ItemFields.candelete,
|
|
||||||
ItemFields.candownload,
|
|
||||||
ItemFields.primaryimageaspectratio,
|
|
||||||
ItemFields.overview,
|
|
||||||
ItemFields.genres,
|
|
||||||
],
|
|
||||||
mediaTypes: [MediaType.video],
|
mediaTypes: [MediaType.video],
|
||||||
enableTotalRecordCount: false,
|
enableTotalRecordCount: false,
|
||||||
);
|
);
|
||||||
|
|
@ -58,16 +60,7 @@ class DashboardNotifier extends StateNotifier<HomeModel> {
|
||||||
if (viewTypes.contains(CollectionType.music)) {
|
if (viewTypes.contains(CollectionType.music)) {
|
||||||
final resumeAudioResponse = await api.usersUserIdItemsResumeGet(
|
final resumeAudioResponse = await api.usersUserIdItemsResumeGet(
|
||||||
enableImageTypes: imagesToFetch,
|
enableImageTypes: imagesToFetch,
|
||||||
fields: [
|
fields: fieldsToFetch,
|
||||||
ItemFields.parentid,
|
|
||||||
ItemFields.mediastreams,
|
|
||||||
ItemFields.mediasources,
|
|
||||||
ItemFields.candelete,
|
|
||||||
ItemFields.candownload,
|
|
||||||
ItemFields.primaryimageaspectratio,
|
|
||||||
ItemFields.overview,
|
|
||||||
ItemFields.genres,
|
|
||||||
],
|
|
||||||
mediaTypes: [MediaType.audio],
|
mediaTypes: [MediaType.audio],
|
||||||
enableTotalRecordCount: false,
|
enableTotalRecordCount: false,
|
||||||
);
|
);
|
||||||
|
|
@ -80,16 +73,7 @@ class DashboardNotifier extends StateNotifier<HomeModel> {
|
||||||
if (viewTypes.contains(CollectionType.books)) {
|
if (viewTypes.contains(CollectionType.books)) {
|
||||||
final resumeBookResponse = await api.usersUserIdItemsResumeGet(
|
final resumeBookResponse = await api.usersUserIdItemsResumeGet(
|
||||||
enableImageTypes: imagesToFetch,
|
enableImageTypes: imagesToFetch,
|
||||||
fields: [
|
fields: fieldsToFetch,
|
||||||
ItemFields.parentid,
|
|
||||||
ItemFields.mediastreams,
|
|
||||||
ItemFields.mediasources,
|
|
||||||
ItemFields.candelete,
|
|
||||||
ItemFields.candownload,
|
|
||||||
ItemFields.primaryimageaspectratio,
|
|
||||||
ItemFields.overview,
|
|
||||||
ItemFields.genres,
|
|
||||||
],
|
|
||||||
mediaTypes: [MediaType.book],
|
mediaTypes: [MediaType.book],
|
||||||
enableTotalRecordCount: false,
|
enableTotalRecordCount: false,
|
||||||
);
|
);
|
||||||
|
|
@ -102,16 +86,7 @@ class DashboardNotifier extends StateNotifier<HomeModel> {
|
||||||
final nextResponse = await api.showsNextUpGet(
|
final nextResponse = await api.showsNextUpGet(
|
||||||
nextUpDateCutoff: DateTime.now().subtract(
|
nextUpDateCutoff: DateTime.now().subtract(
|
||||||
ref.read(clientSettingsProvider.select((value) => value.nextUpDateCutoff ?? const Duration(days: 28)))),
|
ref.read(clientSettingsProvider.select((value) => value.nextUpDateCutoff ?? const Duration(days: 28)))),
|
||||||
fields: [
|
fields: fieldsToFetch,
|
||||||
ItemFields.parentid,
|
|
||||||
ItemFields.mediastreams,
|
|
||||||
ItemFields.mediasources,
|
|
||||||
ItemFields.candelete,
|
|
||||||
ItemFields.candownload,
|
|
||||||
ItemFields.primaryimageaspectratio,
|
|
||||||
ItemFields.overview,
|
|
||||||
ItemFields.genres,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final next = nextResponse.body?.items
|
final next = nextResponse.body?.items
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,16 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
||||||
valueListenable: selectedPoster,
|
valueListenable: selectedPoster,
|
||||||
builder: (_, value, __) {
|
builder: (_, value, __) {
|
||||||
return BackgroundImage(
|
return BackgroundImage(
|
||||||
items: value != null ? [value] : [...homeCarouselItems, ...dashboardData.nextUp, ...allResume],
|
images: (value != null
|
||||||
|
? [value]
|
||||||
|
: [
|
||||||
|
...homeCarouselItems,
|
||||||
|
...dashboardData.nextUp,
|
||||||
|
...allResume,
|
||||||
|
])
|
||||||
|
.map((e) => e.images)
|
||||||
|
.nonNulls
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import 'package:fladder/screens/shared/fladder_snackbar.dart';
|
||||||
import 'package:fladder/util/input_handler.dart';
|
import 'package:fladder/util/input_handler.dart';
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
import 'package:fladder/util/string_extensions.dart';
|
import 'package:fladder/util/string_extensions.dart';
|
||||||
import 'package:fladder/widgets/keyboard/custom_keyboard.dart';
|
import 'package:fladder/widgets/keyboard/slide_in_keyboard.dart';
|
||||||
import 'package:fladder/widgets/navigation_scaffold/components/adaptive_fab.dart';
|
import 'package:fladder/widgets/navigation_scaffold/components/adaptive_fab.dart';
|
||||||
import 'package:fladder/widgets/navigation_scaffold/components/destination_model.dart';
|
import 'package:fladder/widgets/navigation_scaffold/components/destination_model.dart';
|
||||||
import 'package:fladder/widgets/navigation_scaffold/navigation_scaffold.dart';
|
import 'package:fladder/widgets/navigation_scaffold/navigation_scaffold.dart';
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import 'package:fladder/screens/login/login_user_grid.dart';
|
||||||
import 'package:fladder/screens/shared/animated_fade_size.dart';
|
import 'package:fladder/screens/shared/animated_fade_size.dart';
|
||||||
import 'package:fladder/screens/shared/fladder_logo.dart';
|
import 'package:fladder/screens/shared/fladder_logo.dart';
|
||||||
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
|
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
|
||||||
import 'package:fladder/widgets/keyboard/custom_keyboard.dart';
|
import 'package:fladder/widgets/keyboard/slide_in_keyboard.dart';
|
||||||
import 'package:fladder/widgets/navigation_scaffold/components/fladder_app_bar.dart';
|
import 'package:fladder/widgets/navigation_scaffold/components/fladder_app_bar.dart';
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
|
|
|
||||||
|
|
@ -251,7 +251,7 @@ class _PosterImageState extends ConsumerState<PosterImage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if ((widget.poster.unPlayedItemCount != null && widget.poster is SeriesModel) ||
|
if ((widget.poster.unPlayedItemCount != null && widget.poster is SeriesModel) ||
|
||||||
(widget.poster.playAble && !widget.poster.unWatched))
|
(widget.poster.playAble && !widget.poster.unWatched && widget.poster is! PhotoAlbumModel))
|
||||||
IgnorePointer(
|
IgnorePointer(
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.topRight,
|
alignment: Alignment.topRight,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import 'package:fladder/screens/shared/animated_fade_size.dart';
|
||||||
import 'package:fladder/theme.dart';
|
import 'package:fladder/theme.dart';
|
||||||
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
|
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
|
||||||
import 'package:fladder/util/focus_provider.dart';
|
import 'package:fladder/util/focus_provider.dart';
|
||||||
import 'package:fladder/widgets/keyboard/custom_keyboard.dart';
|
import 'package:fladder/widgets/keyboard/slide_in_keyboard.dart';
|
||||||
import 'package:fladder/widgets/shared/ensure_visible.dart';
|
import 'package:fladder/widgets/shared/ensure_visible.dart';
|
||||||
|
|
||||||
class OutlinedTextField extends ConsumerStatefulWidget {
|
class OutlinedTextField extends ConsumerStatefulWidget {
|
||||||
|
|
@ -71,6 +71,7 @@ class OutlinedTextField extends ConsumerStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _OutlinedTextFieldState extends ConsumerState<OutlinedTextField> {
|
class _OutlinedTextFieldState extends ConsumerState<OutlinedTextField> {
|
||||||
|
late final controller = widget.controller ?? TextEditingController();
|
||||||
late final FocusNode _textFocus = widget.focusNode ?? FocusNode();
|
late final FocusNode _textFocus = widget.focusNode ?? FocusNode();
|
||||||
late final FocusNode _wrapperFocus = FocusNode()
|
late final FocusNode _wrapperFocus = FocusNode()
|
||||||
..addListener(() {
|
..addListener(() {
|
||||||
|
|
@ -130,7 +131,7 @@ class _OutlinedTextFieldState extends ConsumerState<OutlinedTextField> {
|
||||||
ref.watch(clientSettingsProvider.select((value) => !value.useSystemIME));
|
ref.watch(clientSettingsProvider.select((value) => !value.useSystemIME));
|
||||||
|
|
||||||
final textField = TextField(
|
final textField = TextField(
|
||||||
controller: widget.controller,
|
controller: controller,
|
||||||
onChanged: widget.onChanged,
|
onChanged: widget.onChanged,
|
||||||
focusNode: _textFocus,
|
focusNode: _textFocus,
|
||||||
onTap: widget.onTap,
|
onTap: widget.onTap,
|
||||||
|
|
@ -199,26 +200,28 @@ class _OutlinedTextFieldState extends ConsumerState<OutlinedTextField> {
|
||||||
ignoring: widget.enabled == false,
|
ignoring: widget.enabled == false,
|
||||||
child: KeyboardListener(
|
child: KeyboardListener(
|
||||||
focusNode: _wrapperFocus,
|
focusNode: _wrapperFocus,
|
||||||
onKeyEvent: (KeyEvent event) {
|
onKeyEvent: (KeyEvent event) async {
|
||||||
if (keyboardFocus || AdaptiveLayout.inputDeviceOf(context) != InputDevice.dPad) return;
|
if (keyboardFocus || AdaptiveLayout.inputDeviceOf(context) != InputDevice.dPad) return;
|
||||||
if (event is KeyDownEvent && acceptKeys.contains(event.logicalKey)) {
|
if (event is KeyDownEvent && acceptKeys.contains(event.logicalKey)) {
|
||||||
if (_textFocus.hasFocus) {
|
if (_textFocus.hasFocus) {
|
||||||
_wrapperFocus.requestFocus();
|
_wrapperFocus.requestFocus();
|
||||||
} else if (_wrapperFocus.hasFocus) {
|
} else if (_wrapperFocus.hasFocus) {
|
||||||
if (useCustomKeyboard) {
|
if (useCustomKeyboard) {
|
||||||
CustomKeyboard.of(context).openKeyboard(
|
await openKeyboard(
|
||||||
textField,
|
context,
|
||||||
onClosed: () {
|
controller,
|
||||||
setState(() {
|
inputType: widget.keyboardType,
|
||||||
keyboardFocus = false;
|
inputAction: widget.textInputAction,
|
||||||
});
|
searchQuery: widget.searchQuery,
|
||||||
_wrapperFocus.requestFocus();
|
onChanged: () {
|
||||||
|
widget.onChanged?.call(controller.text);
|
||||||
},
|
},
|
||||||
query: widget.searchQuery,
|
|
||||||
);
|
);
|
||||||
|
widget.onSubmitted?.call(controller.text);
|
||||||
setState(() {
|
setState(() {
|
||||||
keyboardFocus = true;
|
keyboardFocus = false;
|
||||||
});
|
});
|
||||||
|
_wrapperFocus.requestFocus();
|
||||||
} else {
|
} else {
|
||||||
_textFocus.requestFocus();
|
_textFocus.requestFocus();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import 'package:fladder/util/debug_banner.dart';
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
import 'package:fladder/util/poster_defaults.dart';
|
import 'package:fladder/util/poster_defaults.dart';
|
||||||
import 'package:fladder/util/resolution_checker.dart';
|
import 'package:fladder/util/resolution_checker.dart';
|
||||||
|
import 'package:fladder/widgets/keyboard/slide_in_keyboard.dart';
|
||||||
|
|
||||||
enum InputDevice {
|
enum InputDevice {
|
||||||
touch,
|
touch,
|
||||||
|
|
@ -209,32 +210,40 @@ class _AdaptiveLayoutBuilderState extends ConsumerState<AdaptiveLayoutBuilder> {
|
||||||
|
|
||||||
final mediaQuery = MediaQuery.of(context);
|
final mediaQuery = MediaQuery.of(context);
|
||||||
|
|
||||||
return MediaQuery(
|
return ValueListenableBuilder(
|
||||||
data: mediaQuery.copyWith(
|
valueListenable: isKeyboardOpen,
|
||||||
padding: isDesktop || kIsWeb ? const EdgeInsets.only(top: defaultTitleBarHeight, bottom: 16) : null,
|
builder: (context, value, child) {
|
||||||
viewPadding: isDesktop || kIsWeb ? const EdgeInsets.only(top: defaultTitleBarHeight, bottom: 16) : null,
|
return MediaQuery(
|
||||||
),
|
data: mediaQuery.copyWith(
|
||||||
child: AdaptiveLayout(
|
padding: (isDesktop || kIsWeb
|
||||||
data: currentLayout.copyWith(
|
? const EdgeInsets.only(top: defaultTitleBarHeight, bottom: 16)
|
||||||
viewSize: selectedViewSize,
|
: mediaQuery.padding),
|
||||||
layoutMode: selectedLayoutMode,
|
viewPadding: isDesktop || kIsWeb ? const EdgeInsets.only(top: defaultTitleBarHeight, bottom: 16) : null,
|
||||||
inputDevice: input,
|
),
|
||||||
platform: currentPlatform,
|
child: AdaptiveLayout(
|
||||||
isDesktop: isDesktop,
|
data: currentLayout.copyWith(
|
||||||
controller: scrollControllers,
|
viewSize: selectedViewSize,
|
||||||
posterDefaults: posterDefaults,
|
layoutMode: selectedLayoutMode,
|
||||||
),
|
inputDevice: input,
|
||||||
child: Builder(
|
platform: currentPlatform,
|
||||||
builder: (context) => isDesktop
|
isDesktop: isDesktop,
|
||||||
? ResolutionChecker(
|
controller: scrollControllers,
|
||||||
child:
|
posterDefaults: posterDefaults,
|
||||||
widget.adaptiveLayout == null ? DebugBanner(child: widget.child(context)) : widget.child(context),
|
),
|
||||||
)
|
child: Builder(
|
||||||
: widget.adaptiveLayout == null
|
builder: (context) => isDesktop
|
||||||
? DebugBanner(child: widget.child(context))
|
? ResolutionChecker(
|
||||||
: widget.child(context),
|
child: widget.adaptiveLayout == null
|
||||||
),
|
? DebugBanner(child: widget.child(context))
|
||||||
),
|
: widget.child(context),
|
||||||
|
)
|
||||||
|
: widget.adaptiveLayout == null
|
||||||
|
? DebugBanner(child: widget.child(context))
|
||||||
|
: widget.child(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,6 @@ class FocusButtonState extends State<FocusButton> {
|
||||||
cursor: SystemMouseCursors.click,
|
cursor: SystemMouseCursors.click,
|
||||||
onEnter: (event) => onHover.value = true,
|
onEnter: (event) => onHover.value = true,
|
||||||
onExit: (event) => onHover.value = false,
|
onExit: (event) => onHover.value = false,
|
||||||
hitTestBehavior: HitTestBehavior.translucent,
|
|
||||||
child: Focus(
|
child: Focus(
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
autofocus: widget.autoFocus,
|
autofocus: widget.autoFocus,
|
||||||
|
|
@ -160,7 +159,13 @@ class FocusButtonState extends State<FocusButton> {
|
||||||
onTap: widget.onTap,
|
onTap: widget.onTap,
|
||||||
onSecondaryTapDown: widget.onSecondaryTapDown,
|
onSecondaryTapDown: widget.onSecondaryTapDown,
|
||||||
onLongPress: widget.onLongPress,
|
onLongPress: widget.onLongPress,
|
||||||
child: widget.child,
|
child: Container(
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: widget.borderRadius ?? FladderTheme.smallShape.borderRadius,
|
||||||
|
),
|
||||||
|
child: widget.child,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: ValueListenableBuilder(
|
child: ValueListenableBuilder(
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||||
|
|
||||||
import 'package:fladder/screens/shared/animated_fade_size.dart';
|
import 'package:fladder/screens/shared/animated_fade_size.dart';
|
||||||
|
|
@ -9,128 +10,124 @@ import 'package:fladder/util/focus_provider.dart';
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
import 'package:fladder/widgets/keyboard/alpha_numeric_keyboard.dart';
|
import 'package:fladder/widgets/keyboard/alpha_numeric_keyboard.dart';
|
||||||
|
|
||||||
class CustomKeyboard extends InheritedWidget {
|
ValueNotifier<bool> isKeyboardOpen = ValueNotifier<bool>(false);
|
||||||
final CustomKeyboardState state;
|
|
||||||
|
|
||||||
const CustomKeyboard({
|
double keyboardWidthFactor = 0.2;
|
||||||
required this.state,
|
|
||||||
required super.child,
|
class CustomKeyboardWrapper extends StatelessWidget {
|
||||||
|
final Widget child;
|
||||||
|
const CustomKeyboardWrapper({
|
||||||
|
required this.child,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
static CustomKeyboardState of(BuildContext context) {
|
|
||||||
final inherited = context.dependOnInheritedWidgetOfExactType<CustomKeyboard>();
|
|
||||||
assert(inherited != null, 'No CustomKeyboard found in context');
|
|
||||||
return inherited!.state;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool updateShouldNotify(CustomKeyboard oldWidget) => state != oldWidget.state;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CustomKeyboardWrapper extends StatefulWidget {
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
const CustomKeyboardWrapper({super.key, required this.child});
|
|
||||||
|
|
||||||
@override
|
|
||||||
CustomKeyboardState createState() => CustomKeyboardState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class CustomKeyboardState extends State<CustomKeyboardWrapper> {
|
|
||||||
bool _isOpen = false;
|
|
||||||
TextEditingController? _controller;
|
|
||||||
TextField? _textField;
|
|
||||||
|
|
||||||
bool get isOpen => _isOpen;
|
|
||||||
|
|
||||||
VoidCallback? onCloseCall;
|
|
||||||
|
|
||||||
FutureOr<List<String>> Function(String query)? searchQuery;
|
|
||||||
|
|
||||||
void openKeyboard(
|
|
||||||
TextField textField, {
|
|
||||||
VoidCallback? onClosed,
|
|
||||||
FutureOr<List<String>> Function(String query)? query,
|
|
||||||
}) {
|
|
||||||
onCloseCall = onClosed;
|
|
||||||
|
|
||||||
searchQuery = query;
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
_isOpen = true;
|
|
||||||
_textField = textField;
|
|
||||||
_controller = textField.controller;
|
|
||||||
});
|
|
||||||
_controller?.addListener(() {
|
|
||||||
_textField?.onChanged?.call(_controller?.text ?? "");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void closeKeyboard() {
|
|
||||||
setState(() {
|
|
||||||
_isOpen = false;
|
|
||||||
});
|
|
||||||
onCloseCall?.call();
|
|
||||||
onCloseCall = null;
|
|
||||||
if (_controller != null) {
|
|
||||||
_textField?.onSubmitted?.call(_controller?.text ?? "");
|
|
||||||
_textField?.onEditingComplete?.call();
|
|
||||||
_controller?.removeListener(() {
|
|
||||||
_textField?.onChanged?.call(_controller?.text ?? "");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
super.dispose();
|
|
||||||
_controller?.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final mq = MediaQuery.of(context);
|
return Container(
|
||||||
return BackButtonListener(
|
color: Theme.of(context).colorScheme.surface,
|
||||||
onBackButtonPressed: () async {
|
child: ValueListenableBuilder(
|
||||||
if (!context.mounted) return false;
|
valueListenable: isKeyboardOpen,
|
||||||
if (_isOpen && context.mounted) {
|
builder: (context, value, _) {
|
||||||
closeKeyboard();
|
return AnimatedFractionallySizedBox(
|
||||||
return true;
|
duration: const Duration(milliseconds: 175),
|
||||||
} else {
|
widthFactor: value ? 1.0 - keyboardWidthFactor : 1.0,
|
||||||
return false;
|
heightFactor: 1.0,
|
||||||
}
|
alignment: Alignment.centerRight,
|
||||||
},
|
child: child,
|
||||||
child: CustomKeyboard(
|
);
|
||||||
state: this,
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<T?> openKeyboard<T>(
|
||||||
|
BuildContext context,
|
||||||
|
TextEditingController controller, {
|
||||||
|
TextInputType? inputType,
|
||||||
|
TextInputAction? inputAction,
|
||||||
|
FutureOr<List<String>> Function(String query)? searchQuery,
|
||||||
|
VoidCallback? onChanged,
|
||||||
|
}) async {
|
||||||
|
isKeyboardOpen.value = true;
|
||||||
|
await showGeneralDialog(
|
||||||
|
context: context,
|
||||||
|
transitionDuration: const Duration(milliseconds: 175),
|
||||||
|
barrierDismissible: true,
|
||||||
|
barrierColor: Colors.transparent,
|
||||||
|
barrierLabel: 'Custom keyboard',
|
||||||
|
useRootNavigator: true,
|
||||||
|
fullscreenDialog: true,
|
||||||
|
transitionBuilder: (context, animation, secondaryAnimation, child) {
|
||||||
|
return SlideTransition(
|
||||||
|
position: Tween(begin: const Offset(-1, 0), end: const Offset(0, 0)).animate(
|
||||||
|
animation,
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
pageBuilder: (context, animation1, animation2) {
|
||||||
|
return Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: _SlideInKeyboard(
|
||||||
|
controller: controller,
|
||||||
|
onChanged: onChanged ?? () {},
|
||||||
|
onClose: () {
|
||||||
|
context.router.pop();
|
||||||
|
isKeyboardOpen.value = false;
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
inputType: inputType,
|
||||||
|
inputAction: inputAction,
|
||||||
|
searchQuery: searchQuery,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
isKeyboardOpen.value = false;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SlideInKeyboard extends StatefulWidget {
|
||||||
|
final TextEditingController controller;
|
||||||
|
final Function() onChanged;
|
||||||
|
final Function() onClose;
|
||||||
|
final TextInputType? inputType;
|
||||||
|
final TextInputAction? inputAction;
|
||||||
|
final FutureOr<List<String>> Function(String query)? searchQuery;
|
||||||
|
const _SlideInKeyboard({
|
||||||
|
required this.controller,
|
||||||
|
required this.onChanged,
|
||||||
|
required this.onClose,
|
||||||
|
this.inputType,
|
||||||
|
this.inputAction,
|
||||||
|
this.searchQuery,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_SlideInKeyboard> createState() => __SlideInKeyboardState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class __SlideInKeyboardState extends State<_SlideInKeyboard> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final padding = MediaQuery.paddingOf(context);
|
||||||
|
final width = MediaQuery.sizeOf(context).width * keyboardWidthFactor;
|
||||||
|
return FractionallySizedBox(
|
||||||
|
widthFactor: keyboardWidthFactor,
|
||||||
|
heightFactor: 1.0,
|
||||||
|
child: Padding(
|
||||||
|
padding: padding.copyWith(left: (padding.left - width).clamp(0, padding.left)),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
height: double.infinity,
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
alignment: Alignment.center,
|
child: _CustomKeyboardView(
|
||||||
child: Row(
|
controller: widget.controller,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
onChanged: widget.onChanged,
|
||||||
children: [
|
onClose: widget.onClose,
|
||||||
AnimatedSize(
|
keyboardType: widget.inputType,
|
||||||
duration: const Duration(milliseconds: 125),
|
keyboardActionType: widget.inputAction,
|
||||||
child: _isOpen
|
searchQuery: widget.searchQuery,
|
||||||
? SizedBox(
|
|
||||||
width: mq.size.width * 0.20,
|
|
||||||
height: double.infinity,
|
|
||||||
child: Material(
|
|
||||||
color: Theme.of(context).colorScheme.surface,
|
|
||||||
child: _CustomKeyboardView(
|
|
||||||
controller: _controller!,
|
|
||||||
keyboardType: _textField?.keyboardType,
|
|
||||||
keyboardActionType: _textField?.textInputAction,
|
|
||||||
onClose: closeKeyboard,
|
|
||||||
onChanged: () => _textField?.onChanged?.call(_controller?.text ?? ""),
|
|
||||||
searchQuery: searchQuery,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: const SizedBox.shrink(),
|
|
||||||
),
|
|
||||||
Expanded(child: widget.child),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -187,7 +184,7 @@ class _CustomKeyboardViewState extends State<_CustomKeyboardView> {
|
||||||
node: scope,
|
node: scope,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(12.0).add(MediaQuery.paddingOf(context)),
|
padding: const EdgeInsets.all(12.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
spacing: 16,
|
spacing: 16,
|
||||||
|
|
@ -301,7 +298,10 @@ class _SearchResults extends StatelessWidget {
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
const Icon(IconsaxPlusLinear.search_status_1),
|
const Icon(IconsaxPlusLinear.search_status_1),
|
||||||
Text(context.localized.noResults),
|
Text(
|
||||||
|
context.localized.noResults,
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -31,7 +31,7 @@ class _BackgroundImageState extends ConsumerState<BackgroundImage> {
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(covariant BackgroundImage oldWidget) {
|
void didUpdateWidget(covariant BackgroundImage oldWidget) {
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
if (!oldWidget.items.equals(widget.items)) {
|
if (!oldWidget.items.equals(widget.items) || !oldWidget.images.equals(widget.images)) {
|
||||||
updateItems();
|
updateItems();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -46,7 +46,7 @@ class _BackgroundImageState extends ConsumerState<BackgroundImage> {
|
||||||
ImageData? newImage;
|
ImageData? newImage;
|
||||||
|
|
||||||
if (widget.images.isNotEmpty) {
|
if (widget.images.isNotEmpty) {
|
||||||
newImage = widget.images.shuffled().firstOrNull?.primary;
|
newImage = widget.images.shuffled().firstOrNull?.randomBackDrop;
|
||||||
} else if (widget.items.isNotEmpty) {
|
} else if (widget.items.isNotEmpty) {
|
||||||
final randomItem = widget.items.shuffled().firstOrNull;
|
final randomItem = widget.items.shuffled().firstOrNull;
|
||||||
final itemId = switch (randomItem?.type) {
|
final itemId = switch (randomItem?.type) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue