mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-14 17:55:58 -07:00
feat: Android TV support (#503)
Co-authored-by: PartyDonut <PartyDonut@users.noreply.github.com>
This commit is contained in:
parent
7ab8c015b9
commit
c299492d6d
168 changed files with 12019 additions and 3073 deletions
|
|
@ -3,8 +3,11 @@ import 'package:flutter/services.dart';
|
|||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:fladder/providers/arguments_provider.dart';
|
||||
import 'package:fladder/screens/shared/animated_fade_size.dart';
|
||||
import 'package:fladder/theme.dart';
|
||||
import 'package:fladder/util/focus_provider.dart';
|
||||
import 'package:fladder/widgets/shared/ensure_visible.dart';
|
||||
|
||||
class OutlinedTextField extends ConsumerStatefulWidget {
|
||||
final String? label;
|
||||
|
|
@ -24,6 +27,9 @@ class OutlinedTextField extends ConsumerStatefulWidget {
|
|||
final TextAlign textAlign;
|
||||
final TextInputType? keyboardType;
|
||||
final TextInputAction? textInputAction;
|
||||
final InputDecoration? decoration;
|
||||
final String? placeHolder;
|
||||
final String? suffix;
|
||||
final String? errorText;
|
||||
final bool? enabled;
|
||||
|
||||
|
|
@ -46,6 +52,9 @@ class OutlinedTextField extends ConsumerStatefulWidget {
|
|||
this.keyboardType,
|
||||
this.textInputAction,
|
||||
this.errorText,
|
||||
this.placeHolder,
|
||||
this.decoration,
|
||||
this.suffix,
|
||||
this.enabled,
|
||||
super.key,
|
||||
});
|
||||
|
|
@ -55,7 +64,26 @@ class OutlinedTextField extends ConsumerStatefulWidget {
|
|||
}
|
||||
|
||||
class _OutlinedTextFieldState extends ConsumerState<OutlinedTextField> {
|
||||
late FocusNode focusNode = widget.focusNode ?? FocusNode();
|
||||
late final FocusNode _textFocus = widget.focusNode ?? FocusNode();
|
||||
late final FocusNode _wrapperFocus = FocusNode()
|
||||
..addListener(() {
|
||||
setState(() {
|
||||
hasFocus = _wrapperFocus.hasFocus;
|
||||
if (hasFocus) {
|
||||
context.ensureVisible();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
bool hasFocus = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_textFocus.dispose();
|
||||
_wrapperFocus.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
bool _obscureText = true;
|
||||
void _toggle() {
|
||||
setState(() {
|
||||
|
|
@ -65,101 +93,103 @@ class _OutlinedTextFieldState extends ConsumerState<OutlinedTextField> {
|
|||
|
||||
Color getColor() {
|
||||
if (widget.errorText != null) return Theme.of(context).colorScheme.errorContainer;
|
||||
return Theme.of(context).colorScheme.secondaryContainer.withValues(alpha: 0.25);
|
||||
return Theme.of(context).colorScheme.surfaceContainerHighest.withValues(alpha: 0.35);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isPasswordField = widget.keyboardType == TextInputType.visiblePassword;
|
||||
final leanBackMode = ref.watch(argumentsStateProvider).leanBackMode;
|
||||
if (widget.autoFocus) {
|
||||
focusNode.requestFocus();
|
||||
if (leanBackMode) {
|
||||
_wrapperFocus.requestFocus();
|
||||
} else {
|
||||
_textFocus.requestFocus();
|
||||
}
|
||||
}
|
||||
focusNode.addListener(
|
||||
() {},
|
||||
);
|
||||
return Column(
|
||||
children: [
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 6),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 250),
|
||||
decoration: BoxDecoration(
|
||||
color: widget.fillColor ?? getColor(),
|
||||
borderRadius: FladderTheme.defaultShape.borderRadius,
|
||||
),
|
||||
),
|
||||
),
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 175),
|
||||
decoration: BoxDecoration(
|
||||
color: widget.decoration == null ? widget.fillColor ?? getColor() : null,
|
||||
borderRadius: FladderTheme.smallShape.borderRadius,
|
||||
border: BoxBorder.all(
|
||||
width: 2,
|
||||
color: hasFocus ? Theme.of(context).colorScheme.primaryFixed : Colors.transparent,
|
||||
),
|
||||
IgnorePointer(
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: IgnorePointer(
|
||||
ignoring: widget.enabled == false,
|
||||
child: TextField(
|
||||
controller: widget.controller,
|
||||
onChanged: widget.onChanged,
|
||||
focusNode: focusNode,
|
||||
onTap: widget.onTap,
|
||||
autofillHints: widget.autoFillHints,
|
||||
keyboardType: widget.keyboardType,
|
||||
autocorrect: widget.autocorrect,
|
||||
onSubmitted: widget.onSubmitted,
|
||||
textInputAction: widget.textInputAction,
|
||||
obscureText: isPasswordField ? _obscureText : false,
|
||||
style: widget.style,
|
||||
maxLines: widget.maxLines,
|
||||
inputFormatters: widget.inputFormatters,
|
||||
textAlign: widget.textAlign,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0),
|
||||
width: widget.borderWidth,
|
||||
),
|
||||
child: KeyboardListener(
|
||||
focusNode: _wrapperFocus,
|
||||
onKeyEvent: (KeyEvent event) {
|
||||
if (event is KeyUpEvent && acceptKeys.contains(event.logicalKey)) {
|
||||
if (_textFocus.hasFocus) {
|
||||
_textFocus.unfocus();
|
||||
_wrapperFocus.requestFocus();
|
||||
} else if (_wrapperFocus.hasFocus) {
|
||||
_textFocus.requestFocus();
|
||||
}
|
||||
}
|
||||
},
|
||||
child: ExcludeFocusTraversal(
|
||||
child: TextField(
|
||||
controller: widget.controller,
|
||||
onChanged: widget.onChanged,
|
||||
focusNode: _textFocus,
|
||||
onTap: widget.onTap,
|
||||
autofillHints: widget.autoFillHints,
|
||||
keyboardType: widget.keyboardType,
|
||||
autocorrect: widget.autocorrect,
|
||||
onSubmitted: widget.onSubmitted != null
|
||||
? (value) {
|
||||
widget.onSubmitted?.call(value);
|
||||
Future.microtask(() async {
|
||||
await Future.delayed(const Duration(milliseconds: 125));
|
||||
_wrapperFocus.requestFocus();
|
||||
});
|
||||
}
|
||||
: null,
|
||||
textInputAction: widget.textInputAction,
|
||||
obscureText: isPasswordField ? _obscureText : false,
|
||||
style: widget.style,
|
||||
maxLines: widget.maxLines,
|
||||
inputFormatters: widget.inputFormatters,
|
||||
textAlign: widget.textAlign,
|
||||
canRequestFocus: true,
|
||||
decoration: widget.decoration ??
|
||||
InputDecoration(
|
||||
border: InputBorder.none,
|
||||
filled: widget.fillColor != null,
|
||||
fillColor: widget.fillColor,
|
||||
labelText: widget.label,
|
||||
suffix: widget.suffix != null
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 6),
|
||||
child: Text(widget.suffix!),
|
||||
)
|
||||
: null,
|
||||
hintText: widget.placeHolder,
|
||||
// errorText: widget.errorText,
|
||||
suffixIcon: isPasswordField
|
||||
? InkWell(
|
||||
onTap: _toggle,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
child: Icon(
|
||||
_obscureText ? Icons.visibility : Icons.visibility_off,
|
||||
size: 16.0,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0),
|
||||
width: widget.borderWidth,
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0),
|
||||
width: widget.borderWidth,
|
||||
),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0),
|
||||
width: widget.borderWidth,
|
||||
),
|
||||
),
|
||||
focusedErrorBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).colorScheme.primary.withValues(alpha: 0),
|
||||
width: widget.borderWidth,
|
||||
),
|
||||
),
|
||||
filled: widget.fillColor != null,
|
||||
fillColor: widget.fillColor,
|
||||
labelText: widget.label,
|
||||
// errorText: widget.errorText,
|
||||
suffixIcon: isPasswordField
|
||||
? InkWell(
|
||||
onTap: _toggle,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
child: Icon(
|
||||
_obscureText ? Icons.visibility : Icons.visibility_off,
|
||||
size: 16.0,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
AnimatedFadeSize(
|
||||
child: widget.errorText != null
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue