admin
2022-02-09 96c17fc30ff517de77f0b3b12a6016f3276a7472
flutter模板之邮箱登录、注册、找回密码
3个文件已添加
1303 ■■■■■ 已修改文件
src/main/resources/code/flutter/lib/ui/mine/email_login.dart 422 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/code/flutter/lib/ui/mine/email_pwd_find.dart 419 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/code/flutter/lib/ui/mine/email_register.dart 462 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/code/flutter/lib/ui/mine/email_login.dart
New file
@@ -0,0 +1,422 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:fluwx_no_pay/fluwx_no_pay.dart' as fluwx;
import 'package:hanju/ui/mine/email_pwd_find.dart';
import 'package:hanju/ui/mine/email_register.dart';
import 'package:html/dom.dart' as dom;
import '../../api/http.dart';
import '../../model/user/user_info.dart';
import '../../ui/common/browser.dart';
import '../../ui/widget/button.dart';
import '../../utils/event_bus_util.dart';
import '../../utils/pageutils.dart';
import '../../utils/push_util.dart';
import '../../utils/string_util.dart';
import '../../utils/ui_constant.dart';
import '../../utils/ui_utils.dart';
import '../../utils/user_util.dart';
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '登录',
      theme: ThemeData(primaryColor: Color(0xFFF5F5F5)),
      home: EmailLoginPage(title: ''),
    );
  }
}
class EmailLoginPage extends StatefulWidget {
  //阿里云一键登录
  static const messageChannel =
      BasicMessageChannel('AliyunPhoneNumberAuth', StandardMessageCodec());
  EmailLoginPage({Key? key, required this.title}) : super(key: key);
  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.
  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".
  final String title;
  @override
  _EmailLoginPageState createState() => _EmailLoginPageState();
}
class _EmailLoginPageState extends State<EmailLoginPage>
    with SingleTickerProviderStateMixin {
  bool oneKeyLogin = false;
  bool checked = false;
  TextEditingController? emailController = TextEditingController();
  TextEditingController? codeController = TextEditingController();
  String phone = "";
  String code = "";
  Timer? timer;
  //重新发送验证码倒计时
  int? reSendSMSTimeLeft;
  @override
  void initState() {
    super.initState();
    reSendSMSTimeLeft = -1;
    //初始化微信登录监听
    fluwx.weChatResponseEventHandler.distinct((a, b) => a == b).listen((res) {
      if (res is fluwx.WeChatAuthResponse) {
        int errCode = res.errCode;
        if (errCode == 0) {
          String? code = res.code;
          //TODO 把微信登录返回的code传给后台,剩下的事就交给后台处理
        } else if (errCode == -4) {
          //showToast("用户拒绝授权");
        } else if (errCode == -2) {
          // showToast("用户取消授权");
        }
      }
    });
  }
  bool _aliyunLogin = false;
  void aliyunOneKeyLogin() async {
    if (_aliyunLogin) {
      return;
    }
    _aliyunLogin = true;
    Future.delayed(const Duration(milliseconds: 1000), () {
      _aliyunLogin = false;
    });
    showLoading(context);
    Map value =
        await EmailLoginPage.messageChannel.send({"method": "checkEnv"}) as Map;
    dismissDialog(context);
    if (value["code"] == 0) {
      value = await EmailLoginPage.messageChannel.send({"method": "startLogin"})
          as Map;
      print("返回内容:${jsonEncode(value)}");
      if (value["code"] == 0) {
        Map<String, dynamic>? resultValue =
            await UserApiUtil.login(context, "", "", value["token"]);
        EmailLoginPage.messageChannel.send({"method": "closeLogin"});
        if (resultValue == null) return;
        if (resultValue["code"] == 0) {
          UserInfo user = UserInfo.fromJson(resultValue["data"]);
          _loginSuccess(user);
        } else {
          Fluttertoast.showToast(msg: value["msg"]);
        }
      } else if (value["code"] == 700000) {
        //Fluttertoast.showToast(msg: "取消登录了");
      }
    } else {
      Fluttertoast.showToast(msg: value["msg"]);
      setState(() {
        oneKeyLogin = false;
      });
    }
  }
  void qqLogin() async {
    Map value = await UserUtil.loginQQ();
  }
  void _loginSuccess(UserInfo user) {
    UserUtil.setUserInfo(user).then((value) {
      print("登录成功");
      eventBus.fire(LoginEventBus(true));
      PushUtil.setAlias(user.id!.toString()).then((value) {
        Navigator.pop(context);
      });
    });
  }
  @override
  void dispose() {
    if (timer != null) {
      timer!.cancel();
    }
    super.dispose();
  }
  BoxDecoration getItemDecoration(Color bgColor, Color shadowColor) {
    return BoxDecoration(
        borderRadius: const BorderRadius.all(Radius.elliptical(10, 10)),
        color: bgColor,
        boxShadow: [
          BoxShadow(
              color: shadowColor,
              blurRadius: 2.0,
              offset: Offset(0.0, 5.0), //阴影y轴偏移量
              spreadRadius: 1 //阴影扩散程度
              )
        ]);
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        resizeToAvoidBottomInset: false,
        backgroundColor: Colors.white,
        body: Stack(
          children: [
            Column(children: [
              //登录内容区域
              Expanded(
                  child: SingleChildScrollView(
                      child: Padding(
                          padding: EdgeInsets.fromLTRB(40, 120, 40, 14),
                          child: Column(
                              crossAxisAlignment: CrossAxisAlignment.center,
                              children: [
                                Image.asset(
                                  "assets/imgs/login/ic_login_logo.png",
                                  width: 131,
                                ),
                                Container(
                                  height: 70,
                                ),
                                Container(
                                    constraints: BoxConstraints(minHeight: 180),
                                    child: getLoginContent()),
                                Row(
                                  children: [
                                    InkWell(
                                      child: const Text(
                                        "注册",
                                        style: TextStyle(
                                            color: ColorConstant.theme),
                                      ),
                                      onTap: () {
                                        NavigatorUtil.navigateToNextPage(
                                            context,
                                            EmailRegisterPage(title: ""),
                                            (data) {
                                              FocusScope.of(context).requestFocus(FocusNode());
                                            });
                                      },
                                    ),
                                    Expanded(child: Container()),
                                    InkWell(
                                      child: const Text("找回密码?",
                                          style: TextStyle(
                                              color: ColorConstant.theme)),
                                      onTap: () {
                                        NavigatorUtil.navigateToNextPage(
                                            context,
                                            EmailFindPwdPage(title: ""),
                                                (data) {
                                                  FocusScope.of(context).requestFocus(FocusNode());
                                                });
                                      },
                                    ),
                                  ],
                                )
                              ])))),
              //用户协议与隐私政策
              Row(children: [
                Container(
                  width: 25,
                ),
                RoundCheckBox(
                  value: checked,
                  onChanged: (value) {
                    setState(() {
                      checked = value;
                    });
                  },
                ),
                Expanded(
                    child: Container(
                        child: HtmlWidget(
                            "<p>登录即表明同意&nbsp;<a href='${Constant.PROTOCOL_URL}'>用户协议</a>&nbsp;和&nbsp;<a href='${Constant.PRIVACY_URL}'>隐私政策</a>&nbsp;</p>",
                            textStyle:
                                const TextStyle(color: Color(0xFF999999)),
                            customStylesBuilder: (element) {
                  // print(element.localName);
                  // if (element.localName == 'a') {
                  //   return {'color': 'red'};
                  // }
                  return null;
                }, onTapUrl: (url) {
                  String? title = "";
                  if (url == Constant.PROTOCOL_URL) {
                    title = "用户协议";
                  } else {
                    title = "隐私政策";
                  }
                  NavigatorUtil.navigateToNextPage(
                      context,
                      BrowserPage(
                        title: title,
                        url: url,
                      ),
                      (data) {});
                  return true;
                }))),
              ])
            ]),
            //关闭按钮
            Positioned(
                top: 30,
                left: 20,
                child: InkWell(
                    onTap: () {
                      Navigator.of(context).pop();
                    },
                    child: Icon(
                      Icons.close,
                      size: 30,
                    )))
          ],
        ));
  }
  Widget getLoginContent() {
    return  Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Container(
                alignment: Alignment.centerLeft,
                padding: EdgeInsets.fromLTRB(20, 0, 5, 0),
                decoration: BoxDecoration(
                    color: const Color(0xFFF5F5F5),
                    borderRadius: BorderRadius.circular(10)),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Image.asset(
                      "assets/imgs/login/icon_email.png",
                      width: 17,
                      height: 15,
                    ),
                    Container(width: 14),
                    Expanded(
                        child: TextField(
                      style: TextStyle(color: Color(0xFF333333), fontSize: 17),
                      onChanged: (value) {
                        setState(() {
                          phone = value;
                        });
                      },
                      textAlign: TextAlign.start,
                      keyboardType: TextInputType.emailAddress,
                      controller: emailController,
                      maxLength: 60,
                      decoration: InputDecoration(
                        counterText: "",
                        hintText: "请输入邮箱",
                        hintStyle:
                            TextStyle(color: Color(0xFFCCCCCC), fontSize: 17),
                        contentPadding: EdgeInsets.only(bottom: 3),
                        border: InputBorder.none,
                        focusedBorder: InputBorder.none,
                      ),
                    )),
                  ],
                ),
              ),
              Container(height: 10),
              Container(
                alignment: Alignment.centerLeft,
                padding: const EdgeInsets.fromLTRB(20, 0, 7, 0),
                decoration: BoxDecoration(
                    color: const Color(0xFFF5F5F5),
                    borderRadius: BorderRadius.circular(10)),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Image.asset(
                      "assets/imgs/login/icon_pwd.png",
                      width: 16.5,
                    ),
                    Container(width: 14),
                    Expanded(
                        child: TextField(
                      style:const TextStyle(color:  Color(0xFF333333), fontSize: 17),
                      onChanged: (value) {
                        setState(() {
                          phone = value;
                        });
                      },
                      textAlign: TextAlign.start,
                      keyboardType: TextInputType.visiblePassword,
                      controller: codeController,
                      maxLength: 32,
                      decoration: const InputDecoration(
                        counterText: "",
                        hintText: "请输入密码",
                        hintStyle:
                            TextStyle(color: Color(0xFFCCCCCC), fontSize: 17),
                        contentPadding: EdgeInsets.only(bottom: 3),
                        border: InputBorder.none,
                        focusedBorder: InputBorder.none,
                      ),
                    )),
                  ],
                ),
              ),
              Container(height: 20),
              MyFillButton(
                "登    录",
                10,
                height: 45,
                color: const Color(0xFFFF2B4B),
                fontSize: 17,
                enable: StringUtil.isEmail(emailController!.value.text) &&
                    codeController!.value.text.length >= 6,
                onClick: () {
                  if (!(StringUtil.isEmail(emailController!.value.text) &&
                      codeController!.value.text.length >= 6)) {
                    return;
                  }
                  if (!checked) {
                    Fluttertoast.showToast(msg: "请同意用户协议与隐私政策");
                    return;
                  }
                  UserApiUtil.login(context, emailController!.value.text,
                          codeController!.value.text, "")
                      .then((value) {
                    print("结果: $value");
                    if (value!["code"] == 0) {
                      UserInfo user = UserInfo.fromJson(value["data"]);
                      _loginSuccess(user);
                    } else {
                      Fluttertoast.showToast(msg: value["msg"]);
                    }
                  });
                },
              ),
            ],
          );
  }
}
src/main/resources/code/flutter/lib/ui/mine/email_pwd_find.dart
New file
@@ -0,0 +1,419 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:fluwx_no_pay/fluwx_no_pay.dart' as fluwx;
import 'package:html/dom.dart' as dom;
import '../../api/http.dart';
import '../../model/user/user_info.dart';
import '../../ui/common/browser.dart';
import '../../ui/widget/button.dart';
import '../../utils/event_bus_util.dart';
import '../../utils/pageutils.dart';
import '../../utils/push_util.dart';
import '../../utils/string_util.dart';
import '../../utils/ui_constant.dart';
import '../../utils/ui_utils.dart';
import '../../utils/user_util.dart';
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '注册',
      theme: ThemeData(primaryColor: Color(0xFFF5F5F5)),
      home: EmailFindPwdPage(title: ''),
    );
  }
}
class EmailFindPwdPage extends StatefulWidget {
  //阿里云一键登录
  static const messageChannel =
      BasicMessageChannel('AliyunPhoneNumberAuth', StandardMessageCodec());
  EmailFindPwdPage({Key? key, required this.title}) : super(key: key);
  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.
  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".
  final String title;
  @override
  _EmailFindPwdPageState createState() => _EmailFindPwdPageState();
}
class _EmailFindPwdPageState extends State<EmailFindPwdPage>
    with SingleTickerProviderStateMixin {
  TextEditingController? emailController = TextEditingController();
  TextEditingController? codeController = TextEditingController();
  TextEditingController? pwdController = TextEditingController();
  String email = "";
  String code = "";
  Timer? timer;
  //重新发送验证码倒计时
  int? reSendSMSTimeLeft;
  @override
  void initState() {
    super.initState();
    reSendSMSTimeLeft = -1;
    //初始化微信登录监听
    fluwx.weChatResponseEventHandler.distinct((a, b) => a == b).listen((res) {
      if (res is fluwx.WeChatAuthResponse) {
        int errCode = res.errCode;
        if (errCode == 0) {
          String? code = res.code;
          //TODO 把微信登录返回的code传给后台,剩下的事就交给后台处理
        } else if (errCode == -4) {
          //showToast("用户拒绝授权");
        } else if (errCode == -2) {
          // showToast("用户取消授权");
        }
      }
    });
  }
  bool _aliyunLogin = false;
  void aliyunOneKeyLogin() async {
    if (_aliyunLogin) {
      return;
    }
    _aliyunLogin = true;
    Future.delayed(const Duration(milliseconds: 1000), () {
      _aliyunLogin = false;
    });
    showLoading(context);
    Map value =
        await EmailFindPwdPage.messageChannel.send({"method": "checkEnv"}) as Map;
    dismissDialog(context);
    if (value["code"] == 0) {
      value =
          await EmailFindPwdPage.messageChannel.send({"method": "startLogin"}) as Map;
      print("返回内容:${jsonEncode(value)}");
      if (value["code"] == 0) {
        Map<String, dynamic>? resultValue =
            await UserApiUtil.login(context, "", "", value["token"]);
        EmailFindPwdPage.messageChannel.send({"method": "closeLogin"});
        if (resultValue == null) return;
        if (resultValue["code"] == 0) {
          UserInfo user = UserInfo.fromJson(resultValue["data"]);
          _loginSuccess(user);
        } else {
          Fluttertoast.showToast(msg: value["msg"]);
        }
      } else if (value["code"] == 700000) {
        //Fluttertoast.showToast(msg: "取消登录了");
      }
    } else {
      Fluttertoast.showToast(msg: value["msg"]);
    }
  }
  void qqLogin() async {
    Map value = await UserUtil.loginQQ();
  }
  void _loginSuccess(UserInfo user) {
    UserUtil.setUserInfo(user).then((value) {
      print("登录成功");
      eventBus.fire(LoginEventBus(true));
      PushUtil.setAlias(user.id!.toString()).then((value) {
        Navigator.pop(context);
      });
    });
  }
  @override
  void dispose() {
    if (timer != null) {
      timer!.cancel();
    }
    super.dispose();
  }
  BoxDecoration getItemDecoration(Color bgColor, Color shadowColor) {
    return BoxDecoration(
        borderRadius: const BorderRadius.all(Radius.elliptical(10, 10)),
        color: bgColor,
        boxShadow: [
          BoxShadow(
              color: shadowColor,
              blurRadius: 2.0,
              offset: Offset(0.0, 5.0), //阴影y轴偏移量
              spreadRadius: 1 //阴影扩散程度
              )
        ]);
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        resizeToAvoidBottomInset: false,
        backgroundColor: Colors.white,
        body: Stack(
          children: [
            Column(children: [
              //登录内容区域
              Expanded(
                  child: SingleChildScrollView(
                      child: Padding(
                          padding: EdgeInsets.fromLTRB(40, 120, 40, 14),
                          child: Column(
                              crossAxisAlignment: CrossAxisAlignment.center,
                              children: [
                                Image.asset(
                                  "assets/imgs/login/ic_login_logo.png",
                                  width: 131,
                                ),
                                Container(
                                  height: 70,
                                ),
                                Container(
                                    constraints: BoxConstraints(minHeight: 200),
                                    child: getLoginContent()),
                                Container(
                                  height: 30,
                                )
                              ])))),
            ]),
            //关闭按钮
            Positioned(
                top: 30,
                left: 20,
                child: InkWell(
                    onTap: () {
                      Navigator.of(context).pop();
                    },
                    child: const Icon(
                      Icons.arrow_back_ios_new,
                      size: 25,
                    )))
          ],
        ));
  }
  Widget getLoginContent() {
    return  Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Container(
                alignment: Alignment.centerLeft,
                padding: EdgeInsets.fromLTRB(20, 0, 5, 0),
                decoration: BoxDecoration(
                    color: const Color(0xFFF5F5F5),
                    borderRadius: BorderRadius.circular(10)),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Image.asset(
                      "assets/imgs/login/icon_email.png",
                      width: 17,
                      height: 15,
                    ),
                    Container(width: 14),
                    Expanded(
                        child: TextField(
                      style: TextStyle(color: Color(0xFF333333), fontSize: 17),
                      onChanged: (value) {
                        setState(() {
                          email = value;
                        });
                      },
                      textAlign: TextAlign.start,
                      keyboardType: TextInputType.emailAddress,
                      controller: emailController,
                      maxLength: 60,
                      decoration: InputDecoration(
                        counterText: "",
                        hintText: "请输入邮箱",
                        hintStyle:
                            TextStyle(color: Color(0xFFCCCCCC), fontSize: 17),
                        contentPadding: EdgeInsets.only(bottom: 3),
                        border: InputBorder.none,
                        focusedBorder: InputBorder.none,
                      ),
                    )),
                  ],
                ),
              ),
              Container(height: 10),
              Container(
                alignment: Alignment.centerLeft,
                padding: EdgeInsets.fromLTRB(20, 0, 7, 0),
                decoration: BoxDecoration(
                    color: const Color(0xFFF5F5F5),
                    borderRadius: BorderRadius.circular(10)),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Image.asset(
                      "assets/imgs/login/icon_code.png",
                      width: 16.5,
                    ),
                    Container(width: 14),
                    Expanded(
                        child: TextField(
                      style: TextStyle(color: Color(0xFF333333), fontSize: 17),
                      onChanged: (value) {
                        setState(() {
                          email = value;
                        });
                      },
                      textAlign: TextAlign.start,
                      keyboardType: TextInputType.phone,
                      controller: codeController,
                      maxLength: 8,
                      decoration: const InputDecoration(
                        counterText: "",
                        hintText: "请输入验证码",
                        hintStyle:
                            TextStyle(color: Color(0xFFCCCCCC), fontSize: 17),
                        contentPadding: EdgeInsets.only(bottom: 3),
                        border: InputBorder.none,
                        focusedBorder: InputBorder.none,
                      ),
                    )),
                    MyFillButton(
                      (reSendSMSTimeLeft! > 0
                              ? (reSendSMSTimeLeft.toString() + "S重新获取")
                              : reSendSMSTimeLeft == -1
                                  ? "获取验证码"
                                  : "重新获取")
                          .toString(),
                      10,
                      height: 34,
                      padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
                      enable: (reSendSMSTimeLeft! < 1 &&
                          StringUtil.isEmail(emailController!.value.text)),
                      onClick: () {
                        if (!(reSendSMSTimeLeft! < 1 &&
                            StringUtil.isEmail(emailController!.value.text))) {
                          return;
                        }
                        //发送验证码
                        UserApiUtil.sendSMS(
                                context, emailController!.value.text)
                            .then((value) {
                          if (value != null && value["code"] == 0) {
                            setState(() {
                              reSendSMSTimeLeft = 60;
                              //倒计时
                              timer = Timer.periodic(const Duration(seconds: 1),
                                  (timer) {
                                if (reSendSMSTimeLeft! > 0) {
                                  setState(() {
                                    reSendSMSTimeLeft = reSendSMSTimeLeft! - 1;
                                  });
                                } else {
                                  timer.cancel();
                                }
                              });
                            });
                          } else {
                            ToastUtil.toast(value!["msg"]);
                          }
                        });
                      },
                    ),
                  ],
                ),
              ),
              Container(height: 10),
              Container(
                alignment: Alignment.centerLeft,
                padding: const EdgeInsets.fromLTRB(20, 0, 7, 0),
                decoration: BoxDecoration(
                    color: const Color(0xFFF5F5F5),
                    borderRadius: BorderRadius.circular(10)),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Image.asset(
                      "assets/imgs/login/icon_pwd.png",
                      width: 16.5,
                    ),
                    Container(width: 14),
                    Expanded(
                        child: TextField(
                          style: TextStyle(color: Color(0xFF333333), fontSize: 17),
                          onChanged: (value) {
                            setState(() {
                              email = value;
                            });
                          },
                          obscureText: true,
                          textAlign: TextAlign.start,
                          keyboardType: TextInputType.visiblePassword,
                          controller: pwdController,
                          maxLength: 32,
                          decoration: const InputDecoration(
                            counterText: "",
                            hintText: "请输入密码",
                            hintStyle:
                            TextStyle(color: Color(0xFFCCCCCC), fontSize: 17),
                            contentPadding: EdgeInsets.only(bottom: 3),
                            border: InputBorder.none,
                            focusedBorder: InputBorder.none,
                          ),
                        )),
                  ],
                ),
              ),
              Container(height: 52),
              MyFillButton(
                "设置密码",
                10,
                height: 45,
                color: ColorConstant.theme,
                fontSize: 17,
                enable: StringUtil.isEmail(emailController!.value.text) &&
                    codeController!.value.text.length >= 4&&pwdController!.value.text.length >= 6,
                onClick: () {
                  if (!(StringUtil.isEmail(emailController!.value.text) &&
                      codeController!.value.text.length >= 4&&pwdController!.value.text.length >= 6)) {
                    return;
                  }
                  UserApiUtil.login(context, emailController!.value.text,
                          codeController!.value.text, "")
                      .then((value) {
                    print("结果: $value");
                    if (value!["code"] == 0) {
                      UserInfo user = UserInfo.fromJson(value["data"]);
                      _loginSuccess(user);
                    } else {
                      Fluttertoast.showToast(msg: value["msg"]);
                    }
                  });
                },
              ),
            ],
          );
  }
}
src/main/resources/code/flutter/lib/ui/mine/email_register.dart
New file
@@ -0,0 +1,462 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:fluwx_no_pay/fluwx_no_pay.dart' as fluwx;
import 'package:html/dom.dart' as dom;
import '../../api/http.dart';
import '../../model/user/user_info.dart';
import '../../ui/common/browser.dart';
import '../../ui/widget/button.dart';
import '../../utils/event_bus_util.dart';
import '../../utils/pageutils.dart';
import '../../utils/push_util.dart';
import '../../utils/string_util.dart';
import '../../utils/ui_constant.dart';
import '../../utils/ui_utils.dart';
import '../../utils/user_util.dart';
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '注册',
      theme: ThemeData(primaryColor: Color(0xFFF5F5F5)),
      home: EmailRegisterPage(title: ''),
    );
  }
}
class EmailRegisterPage extends StatefulWidget {
  //阿里云一键登录
  static const messageChannel =
      BasicMessageChannel('AliyunPhoneNumberAuth', StandardMessageCodec());
  EmailRegisterPage({Key? key, required this.title}) : super(key: key);
  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.
  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".
  final String title;
  @override
  _EmailRegisterPageState createState() => _EmailRegisterPageState();
}
class _EmailRegisterPageState extends State<EmailRegisterPage>
    with SingleTickerProviderStateMixin {
  bool checked = false;
  TextEditingController? emailController = TextEditingController();
  TextEditingController? codeController = TextEditingController();
  TextEditingController? pwdController = TextEditingController();
  String email = "";
  String code = "";
  Timer? timer;
  //重新发送验证码倒计时
  int? reSendSMSTimeLeft;
  @override
  void initState() {
    super.initState();
    reSendSMSTimeLeft = -1;
    //初始化微信登录监听
    fluwx.weChatResponseEventHandler.distinct((a, b) => a == b).listen((res) {
      if (res is fluwx.WeChatAuthResponse) {
        int errCode = res.errCode;
        if (errCode == 0) {
          String? code = res.code;
          //TODO 把微信登录返回的code传给后台,剩下的事就交给后台处理
        } else if (errCode == -4) {
          //showToast("用户拒绝授权");
        } else if (errCode == -2) {
          // showToast("用户取消授权");
        }
      }
    });
  }
  bool _aliyunLogin = false;
  void aliyunOneKeyLogin() async {
    if (_aliyunLogin) {
      return;
    }
    _aliyunLogin = true;
    Future.delayed(const Duration(milliseconds: 1000), () {
      _aliyunLogin = false;
    });
    showLoading(context);
    Map value =
        await EmailRegisterPage.messageChannel.send({"method": "checkEnv"}) as Map;
    dismissDialog(context);
    if (value["code"] == 0) {
      value =
          await EmailRegisterPage.messageChannel.send({"method": "startLogin"}) as Map;
      print("返回内容:${jsonEncode(value)}");
      if (value["code"] == 0) {
        Map<String, dynamic>? resultValue =
            await UserApiUtil.login(context, "", "", value["token"]);
        EmailRegisterPage.messageChannel.send({"method": "closeLogin"});
        if (resultValue == null) return;
        if (resultValue["code"] == 0) {
          UserInfo user = UserInfo.fromJson(resultValue["data"]);
          _loginSuccess(user);
        } else {
          Fluttertoast.showToast(msg: value["msg"]);
        }
      } else if (value["code"] == 700000) {
        //Fluttertoast.showToast(msg: "取消登录了");
      }
    } else {
      Fluttertoast.showToast(msg: value["msg"]);
    }
  }
  void qqLogin() async {
    Map value = await UserUtil.loginQQ();
  }
  void _loginSuccess(UserInfo user) {
    UserUtil.setUserInfo(user).then((value) {
      print("登录成功");
      eventBus.fire(LoginEventBus(true));
      PushUtil.setAlias(user.id!.toString()).then((value) {
        Navigator.pop(context);
      });
    });
  }
  @override
  void dispose() {
    if (timer != null) {
      timer!.cancel();
    }
    super.dispose();
  }
  BoxDecoration getItemDecoration(Color bgColor, Color shadowColor) {
    return BoxDecoration(
        borderRadius: const BorderRadius.all(Radius.elliptical(10, 10)),
        color: bgColor,
        boxShadow: [
          BoxShadow(
              color: shadowColor,
              blurRadius: 2.0,
              offset: Offset(0.0, 5.0), //阴影y轴偏移量
              spreadRadius: 1 //阴影扩散程度
              )
        ]);
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        resizeToAvoidBottomInset: false,
        backgroundColor: Colors.white,
        body: Stack(
          children: [
            Column(children: [
              //登录内容区域
              Expanded(
                  child: SingleChildScrollView(
                      child: Padding(
                          padding: EdgeInsets.fromLTRB(40, 120, 40, 14),
                          child: Column(
                              crossAxisAlignment: CrossAxisAlignment.center,
                              children: [
                                Image.asset(
                                  "assets/imgs/login/ic_login_logo.png",
                                  width: 131,
                                ),
                                Container(
                                  height: 70,
                                ),
                                Container(
                                    constraints: BoxConstraints(minHeight: 200),
                                    child: getLoginContent()),
                                Container(
                                  height: 30,
                                )
                              ])))),
              //用户协议与隐私政策
              Row(children: [
                Container(
                  width: 25,
                ),
                RoundCheckBox(
                  value: checked,
                  onChanged: (value) {
                    setState(() {
                      checked = value;
                    });
                  },
                ),
                Expanded(
                    child: Container(
                        child: HtmlWidget(
                            "<p>登录即表明同意&nbsp;<a href='${Constant.PROTOCOL_URL}'>用户协议</a>&nbsp;和&nbsp;<a href='${Constant.PRIVACY_URL}'>隐私政策</a>&nbsp;</p>",
                            textStyle:
                                const TextStyle(color: Color(0xFF999999)),
                            onTapUrl: (url) {
                  String? title = "";
                  if (url == Constant.PROTOCOL_URL) {
                    title = "用户协议";
                  } else {
                    title = "隐私政策";
                  }
                  NavigatorUtil.navigateToNextPage(
                      context,
                      BrowserPage(
                        title: title,
                        url: url,
                      ),
                      (data) {});
                  return true;
                }))),
              ])
            ]),
            //关闭按钮
            Positioned(
                top: 30,
                left: 20,
                child: InkWell(
                    onTap: () {
                      Navigator.of(context).pop();
                    },
                    child: const Icon(
                      Icons.arrow_back_ios_new,
                      size: 25,
                    )))
          ],
        ));
  }
  Widget getLoginContent() {
    return  Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Container(
                alignment: Alignment.centerLeft,
                padding: EdgeInsets.fromLTRB(20, 0, 5, 0),
                decoration: BoxDecoration(
                    color: const Color(0xFFF5F5F5),
                    borderRadius: BorderRadius.circular(10)),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Image.asset(
                      "assets/imgs/login/icon_email.png",
                      width: 17,
                      height: 15,
                    ),
                    Container(width: 14),
                    Expanded(
                        child: TextField(
                      style: TextStyle(color: Color(0xFF333333), fontSize: 17),
                      onChanged: (value) {
                        setState(() {
                          email = value;
                        });
                      },
                      textAlign: TextAlign.start,
                      keyboardType: TextInputType.emailAddress,
                      controller: emailController,
                      maxLength: 60,
                      decoration: InputDecoration(
                        counterText: "",
                        hintText: "请输入邮箱",
                        hintStyle:
                            TextStyle(color: Color(0xFFCCCCCC), fontSize: 17),
                        contentPadding: EdgeInsets.only(bottom: 3),
                        border: InputBorder.none,
                        focusedBorder: InputBorder.none,
                      ),
                    )),
                  ],
                ),
              ),
              Container(height: 10),
              Container(
                alignment: Alignment.centerLeft,
                padding: EdgeInsets.fromLTRB(20, 0, 7, 0),
                decoration: BoxDecoration(
                    color: const Color(0xFFF5F5F5),
                    borderRadius: BorderRadius.circular(10)),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Image.asset(
                      "assets/imgs/login/icon_code.png",
                      width: 16.5,
                    ),
                    Container(width: 14),
                    Expanded(
                        child: TextField(
                      style: TextStyle(color: Color(0xFF333333), fontSize: 17),
                      onChanged: (value) {
                        setState(() {
                          email = value;
                        });
                      },
                      textAlign: TextAlign.start,
                      keyboardType: TextInputType.phone,
                      controller: codeController,
                      maxLength: 8,
                      decoration: const InputDecoration(
                        counterText: "",
                        hintText: "请输入验证码",
                        hintStyle:
                            TextStyle(color: Color(0xFFCCCCCC), fontSize: 17),
                        contentPadding: EdgeInsets.only(bottom: 3),
                        border: InputBorder.none,
                        focusedBorder: InputBorder.none,
                      ),
                    )),
                    MyFillButton(
                      (reSendSMSTimeLeft! > 0
                              ? (reSendSMSTimeLeft.toString() + "S重新获取")
                              : reSendSMSTimeLeft == -1
                                  ? "获取验证码"
                                  : "重新获取")
                          .toString(),
                      10,
                      height: 34,
                      padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
                      enable: (reSendSMSTimeLeft! < 1 &&
                          StringUtil.isEmail(emailController!.value.text)),
                      onClick: () {
                        if (!(reSendSMSTimeLeft! < 1 &&
                            StringUtil.isEmail(emailController!.value.text))) {
                          return;
                        }
                        //发送验证码
                        UserApiUtil.sendSMS(
                                context, emailController!.value.text)
                            .then((value) {
                          if (value != null && value["code"] == 0) {
                            setState(() {
                              reSendSMSTimeLeft = 60;
                              //倒计时
                              timer = Timer.periodic(const Duration(seconds: 1),
                                  (timer) {
                                if (reSendSMSTimeLeft! > 0) {
                                  setState(() {
                                    reSendSMSTimeLeft = reSendSMSTimeLeft! - 1;
                                  });
                                } else {
                                  timer.cancel();
                                }
                              });
                            });
                          } else {
                            ToastUtil.toast(value!["msg"]);
                          }
                        });
                      },
                    ),
                  ],
                ),
              ),
              Container(height: 10),
              Container(
                alignment: Alignment.centerLeft,
                padding: const EdgeInsets.fromLTRB(20, 0, 7, 0),
                decoration: BoxDecoration(
                    color: const Color(0xFFF5F5F5),
                    borderRadius: BorderRadius.circular(10)),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    Image.asset(
                      "assets/imgs/login/icon_pwd.png",
                      width: 16.5,
                    ),
                    Container(width: 14),
                    Expanded(
                        child: TextField(
                          style: TextStyle(color: Color(0xFF333333), fontSize: 17),
                          onChanged: (value) {
                            setState(() {
                              email = value;
                            });
                          },
                          obscureText: true,
                          textAlign: TextAlign.start,
                          keyboardType: TextInputType.visiblePassword,
                          controller: pwdController,
                          maxLength: 32,
                          decoration: const InputDecoration(
                            counterText: "",
                            hintText: "请输入密码",
                            hintStyle:
                            TextStyle(color: Color(0xFFCCCCCC), fontSize: 17),
                            contentPadding: EdgeInsets.only(bottom: 3),
                            border: InputBorder.none,
                            focusedBorder: InputBorder.none,
                          ),
                        )),
                  ],
                ),
              ),
              Container(height: 52),
              MyFillButton(
                "注    册",
                10,
                height: 45,
                color: ColorConstant.theme,
                fontSize: 17,
                enable: StringUtil.isEmail(emailController!.value.text) &&
                    codeController!.value.text.length >= 4&&pwdController!.value.text.length >= 6,
                onClick: () {
                  if (!(StringUtil.isEmail(emailController!.value.text) &&
                      codeController!.value.text.length >= 4&&pwdController!.value.text.length >= 6)) {
                    return;
                  }
                  if (!checked) {
                    Fluttertoast.showToast(msg: "请同意用户协议与隐私政策");
                    return;
                  }
                  UserApiUtil.login(context, emailController!.value.text,
                          codeController!.value.text, "")
                      .then((value) {
                    print("结果: $value");
                    if (value!["code"] == 0) {
                      UserInfo user = UserInfo.fromJson(value["data"]);
                      _loginSuccess(user);
                    } else {
                      Fluttertoast.showToast(msg: value["msg"]);
                    }
                  });
                },
              ),
            ],
          );
  }
}