Flutter实现手机验证码登录
关于这个功能实现的灵感来自于Flutter实现百度语音转文字功能这篇博客,以为我以前出过一篇android怎么实现手机号登录的博客,这篇博客主要是针对Android端的实现,所以我们可以通过flutter和Android实现相互通信,从而达到flutter也可以实现手机验证码登录的效果
。
废话少说,我们先看一下效果,因为个人隐私问题,我把手机号码设置为了密码类型,但是不影响效果:
虽然白嫖固然好,但是首先声明一下,因为这个平台的每个账号,一天只能发送10次验证码
。
所以我建议你自己去注册这个平台,建立一个账号来获取验证码登录功能的App Key和App Secret。关于如何注册这个平台应用,你可以去看一下我的Android实现手机验证码登录这篇博客,说的挺详细的。
第一步:在mob平台配置SMSSDK环境
因为下面那篇博客已经说的很清楚了,所以我就不在做详细说明。
关于如何注册这个平台应用,你可以去看一下我的Android实现手机验证码登录:https://blog.csdn.net/qq_45137584/article/details/111414308
这篇博客,说的挺详细的,大概从开始看到下图红色圆圈处就可以了。
第二步:建立flutter项目和android的library文件
关于这个步骤因为我以前的博客有说过,就不在进行说明了,可以看一下我的Flutter实现百度语音转文字功能,从开头看到下图红色圆圈处部分就可以了。
记得仔细阅读每一步,如果你缺少了一步,可以就会导致你的功能无法实现。
下面的文字是补充,可以不需要看
。
看一下我们建立的项目结构吧,关于Android端那里爆红是因为我这个是flutter的项目,没有Android端相关的jar文件,只需要我们把那个爆红的文件,用Android studio打开即可。
下面就我们通过Android studio打开的文件,然后我们在他里面建立的Android的library的文件,名字是mob_plugin。
第三步:在Android的library文件中部署mob+SMSSDK环境
1.在project的build.gradle文件中加入如下代码
代码如下:
classpath "com.mob.sdk:MobSDK:2018.0319.1724"
maven { url 'https://jitpack.io' }
2.在你建立的library中加入如下代码
代码如下:
配置mob环境的代码
:
id 'com.mob.sdk'
建议修改为自己的appKey和appSecret
MobSDK {
appKey "31d18b327d099" //修改为你自己的appKey
appSecret "5e6a2e16f58f9c1e374acf77abb70b70" //修改为你自己的appSecret
SMSSDK {}
}
配置flutter的环境:因为我在与flutter端通信的时候需要用到flutter的库,所以需要配置flutter的环境。
代码如下:
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
flutter {
source '../..'
}
3.在你的AndroidMainfest.xml文件中加入权限
代码如下:
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
第四步:在flutter的Android文件中配置环境
1.修改版本
首先把build.gradle(Module:app)文件中图中红色圆圈处改为与build.gradle(Module:mob_plugin)文件中版本相同
2.flutter的Android文件中导入android端的library和配置mob环境
配置mob环境:
代码如下:
配置mob环境
apply plugin: 'com.mob.sdk'
导入library库
mob_plugin改为你自己的library库名即可
implementation project(':mob_plugin')
第五步:实现flutter验证码功能
1.在flutter端首先UI设计(view层)
这个页面的实现对于学过flutter的人来说比较简单,毕竟我们主要是用于测试功能,所以没必要做的过于复杂。主要就两个输入框和两个按钮。
这里我就说一下主要的一个部分吧,代码如下
///这个方法主要是把你输入的手机号码传输过去
ArsManager.telephone(myController.text);
///这个方法主要是把你输入的手机号码和验证码存入Map里面,然后传输过去。
Map ages={};
ages['phone']=myController.text;
ages['code']=myController1.text;
ArsManager.correct(ages).then((result){
int code = result["code"];
String message = result["message"];
if(message=="提交验证码正确") {
Navigator.of(context).push( MaterialPageRoute(builder: (context)=>Login()));
}else{
print(message);
}
});
main.dart页面
import 'package:flutter/material.dart';
import 'package:mob_app/asr_manger.dart';
import 'package:mob_app/login.dart';
void main() {
runApp(MaterialApp(
home: MyApp(),
));
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final myController = TextEditingController();
final myController1=TextEditingController();
@override
void dispose() {
// TODO: implement dispose
myController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: myController,
obscureText: true,
decoration: InputDecoration(
labelText: "请输入手机号码",
hintText: "请输入手机号码",
prefixIcon: Icon(Icons.people_alt_rounded)),
),
),
Container(
height: 40,
child:OutlineButton(
borderSide:new BorderSide(color: Theme.of(context).primaryColor),
child: new Text('获取验证码',style: new TextStyle(color: Theme.of(context).primaryColor),),
onPressed: (){
_data();
},
),
),
Padding(
padding: const EdgeInsets.all(4.0),
child: TextField(
controller: myController1,
decoration: InputDecoration(
labelText: "请输入验证码",
prefixIcon: Icon(Icons.lock),
hintText: "请输入验证码",
),
),
),
Container(
height: 40,
child:OutlineButton(
borderSide:new BorderSide(color: Theme.of(context).primaryColor),
child: new Text('登录',style: new TextStyle(color: Theme.of(context).primaryColor),),
onPressed: (){
_login();
},
),
),
],
),
);
}
_data() {
setState(() {
ArsManager.telephone(myController.text);
});
}
_login() {
setState(() {
Map ages={};
ages['phone']=myController.text;
ages['code']=myController1.text;
ArsManager.correct(ages).then((result){
int code = result["code"];
String message = result["message"];
if(message=="提交验证码正确") {
Navigator.of(context).push( MaterialPageRoute(builder: (context)=>Login()));
}else{
print(message);
}
});
});
}
}
这个页面用于我们验证码验证成功的时候跳转页面。
login.dart页面:
import 'package:flutter/material.dart';
class Login extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Text('登录成功',style: TextStyle(decoration: TextDecoration.none,fontSize: 20,color:Color(0xFFFFFFFF)),),
);
}
}
2.配置flutter端的MethodChannel
这里我们主要设置了两个方法:
telephone方法:把手机号码传输给Android端,然后发送验证码的方法
。
传送的值是String类型,返回的值也是String类型
correct方法:把手机号码和你输入的验证码传输给Android端,然后实现验证验证码输入是否正确
。
传送的值是Map类型,返回的值是dynamic类型
如果对于那个基础类型不是很懂,可以去看一下我的这篇文章dart的基础类型。
代码如下:
import 'dart:async';
import 'package:flutter/services.dart';
class ArsManager{
static const MethodChannel _channel=const MethodChannel('asr_plugin');
static Future<String> telephone(String phone) async{
return await _channel.invokeMethod('telephone',phone);
}
static Future<dynamic> correct(Map map) async{
return await _channel.invokeMethod('correct',map);
}
}
3.在Android端配置实现发送验证码和验证验证码需要的方法
主要使用到三个方法:
send方法:输入值是phone;主要功能就是通过手机号码获取验证码
submit方法:主要输入值是phone,code;主要功能就是通过手机号码和输入的验证码来验证验证码是否输入正确。
verification方法:主要输入值是message;主要功能就是回调验证码的输入是否正确,然后把结果发送给flutter端
。
package com.example.mob_plugin;
import android.app.Activity;
import android.text.TextUtils;
import android.util.Log;
import com.mob.MobSDK;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
import cn.smssdk.EventHandler;
import cn.smssdk.SMSSDK;
import io.flutter.plugin.common.MethodChannel;
class ArsManger extends Activity {
EventHandler handler;
boolean f1=false;
public void submit(String phone, String code, Activity activity, MethodChannel.Result result) {
SMSSDK.submitVerificationCode("86",phone,code);
verification(activity,result);
}
//点击发送验证码
public void send(String phone) {
//获取验证码
SMSSDK.getVerificationCode("86",phone);
}
public void verification(Activity activity,MethodChannel.Result message){
MobSDK.init(activity, "31d18b327d099","5e6a2e16f58f9c1e374acf77abb70b70");
handler = new EventHandler(){
@Override
public void afterEvent(int event, int result, Object data) {
if (result == SMSSDK.RESULT_COMPLETE){
//回调完成
if (event == SMSSDK.EVENT_SUBMIT_VERIFICATION_CODE) {
//提交验证码成功
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.e("123","提交验证码正确");
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("message", "提交验证码正确");
resultMap.put("code", 200);
//发消息至 Flutter
//此方法只能使用一次
message.success(resultMap);
}
});
}else if (event == SMSSDK.EVENT_GET_VERIFICATION_CODE){
//获取验证码成功
runOnUiThread(new Runnable() {
@Override
public void run() {
// Toast.makeText(MainActivity.this,"验证码已发送", Toast.LENGTH_SHORT).show();
Log.e("123","验证码已发送");
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("message", "验证码已发送");
resultMap.put("code", 200);
//发消息至 Flutter
//此方法只能使用一次
message.success(resultMap);
}
});
}else if (event == SMSSDK.EVENT_GET_SUPPORTED_COUNTRIES){
}
}else{
((Throwable)data).printStackTrace();
Throwable throwable = (Throwable) data;
try {
JSONObject obj = new JSONObject(throwable.getMessage());
final String des = obj.optString("detail");
if (!TextUtils.isEmpty(des)){
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.e("123","提交错误信息");
// Toast.makeText(MainActivity.this,"提交错误信息", Toast.LENGTH_SHORT).show();
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("message", "提交错误信息");
resultMap.put("code", 200);
//发消息至 Flutter
//此方法只能使用一次
message.success(resultMap);
}
});
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
};
SMSSDK.registerEventHandler(handler);
}
}
说一下上面代码中的一段代码:
代码如下:
主要用于把我们verification方法的回调结果返回给flutter,这里属于Android发送给flutter端的一段代码
。
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("message", "验证码正确");
resultMap.put("code", 200);
//发消息至 Flutter
//此方法只能使用一次
message.success(resultMap);
4.在Android端配置MethodChannel
主要使用的两个方法:
registerWith方法:等下会用到,主要用于注册plugin,实现通信功能。
onMethodCall方法:这个方法是我们继承MethodChannel.MethodCallHandler类来使用的方法,主要是功能是接受来自flutter发送过来的数据。
package com.example.mob_plugin;
import android.app.Activity;
import androidx.annotation.NonNull;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
public class AsrPlugin implements MethodChannel.MethodCallHandler {
private final Activity activity;
ArsManger arsManger=new ArsManger();
public static void registerWith(Activity activity, BinaryMessenger messenger) {
MethodChannel channel = new MethodChannel(messenger, "asr_plugin");
AsrPlugin instance = new AsrPlugin(activity);
channel.setMethodCallHandler(instance);
}
public AsrPlugin(Activity activity) {
this.activity = activity;
}
@Override
public void onMethodCall(@NonNull MethodCall methodCall, @NonNull MethodChannel.Result result) {
switch (methodCall.method) {
case "telephone":
arsManger.send(methodCall.arguments.toString());
break;
case "correct":
arsManger.submit(methodCall.argument("phone"),methodCall.argument("code"),activity,result);
break;
default:
result.notImplemented();
}
}
}
5.最后注册plugin,实现数据通信
代码如下:
package com.example.mob_app;
import android.os.Bundle;
import androidx.annotation.NonNull;
import com.example.mob_plugin.AsrPlugin;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
//flutter sdk >= v1.17.0 时使用下面方法注册自定义plugin
AsrPlugin.registerWith(this, flutterEngine.getDartExecutor().getBinaryMessenger());
}
@Override
protected void onCreate(Bundle savedInstanceState ) {
super.onCreate(savedInstanceState);
}
}
过程还是比较多的,其实全部代码都已经写出来,如果你觉得结构不够明了,可以找我要这个demo,发送你的电子邮箱或者私聊等方法都可以。