前言:
今天讲述的最主要的内容是login,也就是flutter登录验证模块,很多的小伙伴觉得登记验证不就是那么回事,发送验证码,在60秒之内判断验证码是否正确,对的就登录,不对从新获取。从主题的框架来看,的确是这样的,但是在用户登录的时候,能够做到比较有交互的,人性的,那么在处理一些逻辑上面可能就要废点功夫了。我不喜欢把一些东西讲的云里雾里,其实一些难的东西,无非就是你不知道嘛,你知道的话那么还存在难点吗?首先我把这个登录模块用到的一些主要知识点,我罗列一下,大家可以做个小Dome玩一玩。
记得在《三体》里面听到过一句话,西方文明之所以比东方文明要发展的先进,是因为西方文明懂得做个例拆分
提示:以下是本篇文章正文内容,下面案例可供参考
一、这个登录模块用到了什么?
- 登录模块肯定要有提示框(我们这里采用 fluttertoast )
- 阿里巴巴第三方短信(没有用过的可以省略)
- 短信验证框(一位大神写的,不过被我修改一丢丢源码,千万不要慌的😘非常之简单 flutter_verification_box )
- flutter的input输入框 TextFeild
- flutter切换动态组件StatefulWidget
- 最后就是自己分装的路由,非常经典,非常好用哦!
二、组件详情介绍
1.自己封装的路由,大家可以当成一个工具类使用
代码如下(源代码):
import 'dart:async';
import 'package:flutter/material.dart';
class RouteHelper {
static Future<T> push<T>(BuildContext context, Widget widget) {
return Navigator.of(context).push(MaterialPageRoute(builder: (ctx) => widget));
}
///删除当前的实例replaceCurrent
static Future<T> pushReplacement<T>(BuildContext context, Widget widget) {
return Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (ctx) => widget));
}
///删除先前的所有实例replaceRoot
static Future<T> pushReplaceRoot<T>(BuildContext context, Widget widget) {
return Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (ctx) => widget), (Route<dynamic> route) => false);
}
}
2.封装输入手机号的表单
代码如下(示例):
class MyLoginButton extends StatelessWidget {
//登录按钮上下左右的距离
double left;
double right;
double bottom;
double top;
double all;
//按钮的透明图
double opacity;
Color color;
//按钮中间显示的文字
String text;
//点击时的回调函数
final GestureTapCallback onTap;
MyLoginButton({
this.color = MyColor.inputColor,
this.text = "登录",//默认中间的字是登陆
this.opacity = 1,
this.onTap,
this.all = 0,
this.top = 0,
this.bottom = 0,
this.right = 28,//默认距离
this.left = 28,
});
@override
Widget build(BuildContext context) {
return Opacity(
opacity: this.opacity,
child: InkWell(
child: Container(
margin: all != 0
? EdgeInsets.all(all)
: EdgeInsets.fromLTRB(
this.left, this.top, this.right, this.bottom),
height: ScreenUtil().setHeight(60),
decoration: BoxDecoration(
color: this.color, borderRadius: BorderRadius.circular(5)),
child: Center(
child: Text(
this.text,
style: TextStyle(
//这个是我方便使用的颜色,大家可以随意使用其他的颜色
color: MyColor.inputBcColor,
fontSize: 16,
),
),
),
),
onTap: this.onTap
),
);
}
}
3.提示框
代码如下(示例):
所有的第三方组件地址 :https://pub.flutter-io.cn/flutter/packages?q=fluttertoast
//先去get下来
fluttertoast: ^8.0.4
//先去做个Dome,按钮点击事件,把这段代码放在里面
Fluttertoast.showToast(
msg: "请输入正确的手机号",
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.TOP,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0,
);
//---------------------------------------------------------------------
Fluttertoast.showToast(
msg: "网络不稳定",
gravity: ToastGravity.CENTER
);
3.大神写的短信验证框
代码如下(示例):
pub文件(源代码):
//pub一下
flutter_verification_box: ^1.0.4
业务需求短信验证框的核心代码:
Container(
padding: EdgeInsets.only(left: 15,right: 15),
height: ScreenUtil().setHeight(50),
child: VerificationBox(
count: 4,
itemWidget:ScreenUtil().setHeight(50),
showCursor: true,
cursorColor: MyColor.inputColor,
focusBorderColor: MyColor.inputColor,
textStyle: TextStyle(fontSize: ScreenUtil().setSp(28),fontWeight: FontWeight.w600),
onSubmitted: (code){
showCode(code);
},
),
)
对业务有特殊的需求,改完之后的源码:
library flutter_verification_box;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_verification_box/src/verification_box_item.dart';
///
/// 验证码输入框
///
class VerificationBox extends StatefulWidget {
VerificationBox(
{this.count = 6,
this.itemWidget = 45,
this.onSubmitted,
this.type = VerificationBoxItemType.box,
this.decoration,
this.borderWidth = 2.0,
this.borderRadius = 5.0,
this.textStyle,
this.focusBorderColor,
this.borderColor,
this.unfocus = true,
this.autoFocus = true,
this.showCursor = false,
this.cursorWidth = 2,
this.cursorColor,
this.cursorIndent = 10,
this.cursorEndIndent = 10,
this.isFllu = true // <====================================== 这里被改过
});
///
/// 几位验证码,一般6位,还有4位的
///
final int count;
///
/// 没一个item的宽
///
final double itemWidget;
///
/// 输入完成回调
///
final ValueChanged onSubmitted;
///
/// 每个item的装饰类型,[VerificationBoxItemType]
///
final VerificationBoxItemType type;
///
/// 每个item的样式
///
final Decoration decoration;
///
/// 边框宽度
///
final double borderWidth;
///
/// 边框颜色
///
final Color borderColor;
///
/// 获取焦点边框的颜色
///
final Color focusBorderColor;
///
/// [VerificationBoxItemType.box] 边框圆角
///
final double borderRadius;
///
/// 文本样式
///
final TextStyle textStyle;
///
/// 输入完成后是否失去焦点,默认true,失去焦点后,软键盘消失
///
final bool unfocus;
///
/// 是否自动获取焦点
///
final bool autoFocus;
///
/// 是否显示光标
///
final bool showCursor;
///
/// 光标颜色
///
final Color cursorColor;
///
/// 光标宽度
///
final double cursorWidth;
///
/// 光标距离顶部距离
///
final double cursorIndent;
///
/// 光标距离底部距离
///
final double cursorEndIndent;
///
/// 是否要满足位数才返回
///
final bool isFllu;// <====================================== 这里被改过
@override
State<StatefulWidget> createState() => _VerificationBox();
}
class _VerificationBox extends State<VerificationBox> {
TextEditingController _controller;
FocusNode _focusNode;
List _contentList = [];
@override
void initState() {
List.generate(widget.count, (index) {
_contentList.add('');
});
_controller = TextEditingController();
_focusNode = FocusNode();
super.initState();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(_focusNode);
},
child: Stack(
children: <Widget>[
Positioned.fill(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: List.generate(widget.count, (index) {
return Container(
width: widget.itemWidget,
child: VerificationBoxItem(
data: _contentList[index],
textStyle: widget.textStyle,
type: widget.type,
decoration: widget.decoration,
borderRadius: widget.borderRadius,
borderWidth: widget.borderWidth,
borderColor: (_controller.text.length == index
? widget.focusBorderColor
: widget.borderColor) ??
widget.borderColor,
showCursor: widget.showCursor && _controller.text.length == index,
cursorColor: widget.cursorColor,
cursorWidth: widget.cursorWidth,
cursorIndent: widget.cursorIndent,
cursorEndIndent: widget.cursorEndIndent,
),
);
}),
)),
_buildTextField(),
],
),
);
}
///
/// 构建TextField
///
_buildTextField() {
return TextField(
controller: _controller,
focusNode: _focusNode,
decoration: InputDecoration(
border: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.transparent)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.transparent)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.transparent)),
),
cursorWidth: 0,
autofocus: widget.autoFocus,
inputFormatters: [
WhitelistingTextInputFormatter(RegExp("[0-9]")),
],
maxLength: widget.count,
buildCounter: (
BuildContext context, {
int currentLength,
int maxLength,
bool isFocused,
}) {
return Text('');
},
keyboardType: TextInputType.number,
style: TextStyle(color: Colors.transparent),
onChanged: _onValueChange,
);
}
_onValueChange(value) {
for (int i = 0; i < widget.count; i++) {
if (i < value.length) {
_contentList[i] = value.substring(i, i + 1);
} else {
_contentList[i] = '';
}
}
setState(() {});
if(widget.isFllu){
widget.onSubmitted(value);
}
if (value.length == widget.count) {
if (widget.unfocus) {
_focusNode.unfocus();
}
if (widget.onSubmitted != null) {
widget.onSubmitted(value);
}
}
}
}
4.原生的http
代码如下(示例):
import 'package:flutter/material.dart';
import 'dart:io';
import 'dart:convert';
_get() async {
print("_get---");
var url = 'https://jsonplaceholder.typicode.com/posts/1';
var httpClient = new HttpClient();
String result;
try {
var request = await httpClient.getUrl(Uri.parse(url));
var response = await request.close();
print("statusCode----${response.statusCode}");
if (response.statusCode == HttpStatus.ok) {
var responseBody = await response.transform(utf8.decoder).join();
var json = responseBody;
var data = jsonDecode(json);
print(data.toString());
print("data----$data");
result = 'HttpStatus.ok';
} else {
result = 'Error getting IP address:\nHttp status ${response.statusCode}';
}
} catch (exception) {
result = 'Failed getting IP address';
}
print("result----$result");
}
_post() async {
print("_post---");
var httpClient = new HttpClient();
String result;
try {
var url = "http://api.juheapi.com/japi/toh";
var request = await httpClient.postUrl(Uri.parse(url));
// 添加请求体
Map jsonMap = {'shopperId': 9356,'machineId':5117,'orderType':2,'orderId':108};
Map<String, String> map1 = new Map();
map1["v"] = "1.0";
map1["month"] = "7";
map1["day"] = "25";
map1["key"] = "bd6e35a2691ae5bb8425c8631e475c2a";
request.add(utf8.encode(json.encode(map1)));
HttpClientResponse response = await request.close();
String responseBody = await response.transform(utf8.decoder).join();
print("statusCode----${response.statusCode}");
if (response.statusCode == HttpStatus.ok) {
print('请求成功');
print(response.headers);//打印头部信息
print("post------$responseBody");
result = 'HttpStatus.ok';
} else {
result = 'Error getting IP address:\nHttp status ${response.statusCode}';
}
} catch (exception) {
result = 'Failed getting IP address';
}
print("result----$result");
}
class HttpClientTest extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('HttpClientTest'),
),
body: Center(
child:Column(
children: <Widget>[
SizedBox(height: 32.0),
Text("HttpClient Get Post 练习"),
SizedBox(height: 32.0),
RaisedButton(
onPressed: _get,
child: new Text('Get 请求'),
),
RaisedButton(
onPressed: _post,
child: new Text('Post 请求'),
),
],
),
),
);
}
}
总结
好了,以上就是大家的武器了,一定要多去尝试,其他的业务逻辑,下一篇在讲的🥰🥰🥰🥰