From b1c17c9173da1cd7a246835b74817e34589aba53 Mon Sep 17 00:00:00 2001 From: josephgcedeno Date: Tue, 28 May 2024 09:49:56 +0800 Subject: [PATCH] feat: stack textfield widget --- lib/input/stack_textfield.dart | 158 ++++++++++++++++++++++ widgetbook/lib/input/stack_textfield.dart | 60 ++++++++ 2 files changed, 218 insertions(+) create mode 100644 lib/input/stack_textfield.dart create mode 100644 widgetbook/lib/input/stack_textfield.dart diff --git a/lib/input/stack_textfield.dart b/lib/input/stack_textfield.dart new file mode 100644 index 0000000..14e4e56 --- /dev/null +++ b/lib/input/stack_textfield.dart @@ -0,0 +1,158 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class StackTextField extends StatefulWidget { + const StackTextField({ + required this.hintText, + this.controller, + this.validator, + this.textInputAction, + this.inputFormatters, + this.focusNode, + this.onChanged, + this.label, + this.keyboardType, + this.onTap, + this.readOnly, + this.suffixIcon, + this.scrollPadding, + this.maxLines, + this.autovalidateMode, + this.style, + this.isDense, + this.prefixIcon, + this.suffixText, + this.suffixStyle, + this.onFieldSubmitted, + this.enabled, + this.textField, + this.obscuringText = false, + this.onEditingComplete, + this.onFocusChange, + this.fillColor, + this.labelColor, + super.key, + }); + + final TextEditingController? controller; + final String? Function(String?)? validator; + final String hintText; + final String? label; + final List? inputFormatters; + final TextInputAction? textInputAction; + final FocusNode? focusNode; + final TextInputType? keyboardType; + final void Function(String)? onChanged; + final void Function()? onTap; + final bool? readOnly; + final Widget? suffixIcon; + final EdgeInsets? scrollPadding; + final int? maxLines; + final AutovalidateMode? autovalidateMode; + final TextStyle? style; + final bool? isDense; + final Widget? prefixIcon; + final String? suffixText; + final TextStyle? suffixStyle; + final bool? enabled; + final void Function(String)? onFieldSubmitted; + final Widget? textField; + final bool obscuringText; + final void Function()? onEditingComplete; + final void Function(bool)? onFocusChange; + final Color? fillColor; + final Color? labelColor; + + @override + State createState() => _StackTextFieldState(); +} + +class _StackTextFieldState extends State { + late bool isEmpty = widget.controller?.text.isEmpty ?? true; + + @override + void initState() { + super.initState(); + + widget.controller?.addListener(() { + final String value = widget.controller?.text.trim() ?? ''; + setState(() { + isEmpty = value.isEmpty; + }); + }); + } + + @override + void dispose() { + super.dispose(); + widget.controller?.dispose(); + } + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + return Stack( + children: [ + Focus( + canRequestFocus: false, + onFocusChange: widget.onFocusChange, + child: widget.textField ?? + TextFormField( + onEditingComplete: widget.onEditingComplete, + obscureText: widget.obscuringText, + enabled: widget.enabled, + controller: widget.controller, + onFieldSubmitted: widget.onFieldSubmitted, + onTap: widget.onTap, + focusNode: widget.focusNode, + validator: widget.validator, + decoration: InputDecoration( + filled: true, + fillColor: widget.fillColor, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide.none, + ), + hintText: widget.hintText, + hintStyle: TextStyle( + color: widget.labelColor ?? + theme.colorScheme.primary.withOpacity(0.7), + ), + suffixText: widget.suffixText, + prefixIcon: widget.prefixIcon, + isDense: widget.isDense, + suffixIcon: widget.suffixIcon, + suffixStyle: widget.suffixStyle, + ), + style: widget.style ?? const TextStyle(color: Colors.black), + inputFormatters: widget.inputFormatters, + textInputAction: widget.textInputAction, + textAlignVertical: !isEmpty + ? TextAlignVertical.bottom + : TextAlignVertical.center, + onChanged: widget.onChanged, + keyboardType: widget.keyboardType, + readOnly: widget.readOnly ?? false, + scrollPadding: + widget.scrollPadding ?? const EdgeInsets.all(20.0), + maxLines: widget.maxLines ?? 1, + autovalidateMode: widget.autovalidateMode, + ), + ), + if (!isEmpty) + Positioned( + left: widget.prefixIcon != null ? 40.5 : 12.5, + top: widget.textField != null ? 6 : 9, + child: Text( + widget.label ?? widget.hintText, + style: TextStyle( + fontSize: 10, + color: widget.labelColor ?? + theme.colorScheme.primary.withOpacity(0.7), + ), + ), + ) + ], + ); + } +} diff --git a/widgetbook/lib/input/stack_textfield.dart b/widgetbook/lib/input/stack_textfield.dart new file mode 100644 index 0000000..c78090d --- /dev/null +++ b/widgetbook/lib/input/stack_textfield.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:nuxify_widgetbook/input/stack_textfield.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; + +@widgetbook.UseCase(name: 'Default', type: StackTextField) +Widget defaultUseCase(BuildContext context) { + final double width = MediaQuery.of(context).size.width; + + return Center( + child: SizedBox(width: width * 0.5, child: const _StackTextField()), + ); +} + +class _StackTextField extends StatefulWidget { + const _StackTextField(); + + @override + State<_StackTextField> createState() => __StackTextFieldState(); +} + +class __StackTextFieldState extends State<_StackTextField> { + final TextEditingController textEditingController = TextEditingController(); + @override + Widget build(BuildContext context) { + final String label = + context.knobs.string(label: 'Label', initialValue: 'Label here'); + return StackTextField( + controller: textEditingController, + hintText: label, + label: label, + readOnly: context.knobs.boolean(label: 'Read only', initialValue: false), + enabled: + context.knobs.boolean(label: 'Enabled TextField', initialValue: true), + maxLines: context.knobs.int.slider( + label: 'Max Lines', + initialValue: 1, + max: 10, + min: 1, + ), + suffixIcon: context.knobs + .boolean(label: 'Display Suffix Icon', initialValue: false) + ? const Icon(Icons.search) + : null, + prefixIcon: context.knobs + .boolean(label: 'Display Prefix Icon', initialValue: false) + ? const Icon(Icons.person) + : null, + isDense: context.knobs.boolean(label: 'Is Dense', initialValue: false), + labelColor: + context.knobs.color(label: 'Label Color', initialValue: Colors.black), + fillColor: + context.knobs.color(label: 'Fill Color', initialValue: Colors.white), + style: TextStyle( + color: context.knobs + .color(label: 'Text Color', initialValue: Colors.black), + ), + ); + } +}