Files
iroute/lib/go_router/examples/input/input.dart
toly 8fb4bf57d6 v8
2023-11-08 09:35:29 +08:00

216 lines
5.3 KiB
Dart

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'decoration/code_decoration.dart';
import 'input_painter.dart';
typedef AsyncSubmit = Future<bool> Function(String value);
/// @desc 短信验证码输入框
/// @time 2019-05-14 16:16
/// @author Cheney
class TolyCodeInput extends StatefulWidget {
final int count;
final AsyncSubmit onSubmit;
final CodeDecoration decoration;
final TextInputType keyboardType;
final bool autoFocus;
final FocusNode? focusNode;
final TextInputAction textInputAction;
const TolyCodeInput(
{this.count = 6,
required this.onSubmit,
this.decoration = const UnderlineDecoration(),
this.keyboardType = TextInputType.number,
this.focusNode,
this.autoFocus = false,
this.textInputAction = TextInputAction.done,
super.key});
@override
State createState() {
return TolyCodeInputState();
}
}
class TolyCodeInputState extends State<TolyCodeInput>
with SingleTickerProviderStateMixin {
///输入监听器
final TextEditingController _controller = TextEditingController();
late AnimationController _animaCtrl;
late Animation<int> _animation;
late FocusNode _focusNode;
@override
void initState() {
_focusNode = widget.focusNode ?? FocusNode();
_controller.addListener(_listenValueChange);
initAnimation();
super.initState();
}
void initAnimation() {
_animaCtrl = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_animation = IntTween(begin: 0, end: 255).animate(_animaCtrl)
..addStatusListener(_stateChange);
_animaCtrl.forward();
}
void _listenValueChange() {
submit(_controller.text);
}
void submit(String text) async{
if (text.length >= widget.count) {
bool success = await widget.onSubmit.call(text.substring(0, widget.count));
if(success){
_focusNode.unfocus();
}else{
_controller.text = "";
}
}
}
@override
void dispose() {
/// Only execute when the controller is autoDispose.
_controller.dispose();
_animaCtrl.dispose();
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return CustomPaint(
foregroundPainter: InputPainter(
RRectCodeDecoration(
height: 56,
strokeWidth: 1,
activeColor: const Color(0xff010101),
inactiveColor: const Color(0xffB6B6B6),
cursorColor: Color(0xff3776E9),
textStyle: TextStyle(color: Colors.black),
cursorSize: Size(1, 24),
),
controller: _controller,
count: widget.count,
decoration: widget.decoration,
alpha: _animation,
),
child: RepaintBoundary(
child: TextField(
controller: _controller,
/// Fake the text style.
style: const TextStyle(
color: Colors.transparent,
),
cursorColor: Colors.transparent,
cursorWidth: 0.0,
autocorrect: false,
textAlign: TextAlign.center,
enableInteractiveSelection: false,
maxLength: widget.count,
onSubmitted: submit,
keyboardType: widget.keyboardType,
focusNode: _focusNode,
autofocus: widget.autoFocus,
textInputAction: widget.textInputAction,
obscureText: true,
decoration: const InputDecoration(
counterText: '',
contentPadding: EdgeInsets.symmetric(vertical: 24),
border: OutlineInputBorder(
borderSide: BorderSide.none,
),
),
),
),
);
}
void _stateChange(AnimationStatus status) {
if (status == AnimationStatus.completed) {
_animaCtrl.reverse();
} else if (status == AnimationStatus.dismissed) {
_animaCtrl.forward();
}
}
}
/// 默认的样式
const TextStyle defaultStyle = TextStyle(
/// Default text color.
color: Color(0x80000000),
/// Default text size.
fontSize: 24.0,
);
abstract class CodeDecoration {
/// The style of painting text.
final TextStyle? textStyle;
final ObscureStyle? obscureStyle;
const CodeDecoration({
this.textStyle,
this.obscureStyle,
});
}
/// The object determine the obscure display
class ObscureStyle {
/// Determine whether replace [obscureText] with number.
final bool isTextObscure;
/// The display text when [isTextObscure] is true
final String obscureText;
const ObscureStyle({
this.isTextObscure = false,
this.obscureText = '*',
}) : assert(obscureText.length == 1);
}
/// The object determine the underline color etc.
class UnderlineDecoration extends CodeDecoration {
/// The space between text and underline.
final double gapSpace;
/// The color of the underline.
final Color color;
/// The height of the underline.
final double lineHeight;
/// The underline changed color when user enter pin.
final Color? enteredColor;
const UnderlineDecoration({
TextStyle? textStyle,
ObscureStyle? obscureStyle,
this.enteredColor = const Color(0xff3776E9),
this.gapSpace = 15.0,
this.color = const Color(0x24000000),
this.lineHeight = 0.5,
}) : super(
textStyle: textStyle,
obscureStyle: obscureStyle,
);
}