Flutter高仿微信系列共59篇,从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。
效果图:
/** * Author : wangning * Email : maoning20080809@163.com * Date : 2022/9/23 22:51 * Description : 登录 */ class Login extends StatelessWidget { const Login({super.key}); @override Widget build(BuildContext context) { return const LoginPage(title: '登录页面'); } } class LoginPage extends StatefulWidget { const LoginPage({super.key, required this.title}); final String title; @override State<LoginPage> createState() => _LoginPageState(); } class _LoginPageState extends State<LoginPage> { String account = SpUtils.getString(CommonUtils.LOGIN_ACCOUNT); final TextEditingController _usernameController = TextEditingController(text: ""); final TextEditingController _passwordController = TextEditingController(text: ""); String _username = '', _password = ''; bool loadingVisible = false; //登录时loading对话框 void _showLoadingDialog() { setState(() { loadingVisible = true; }); Future.delayed(const Duration(seconds: 30),(){ if(mounted){ setState(() { loadingVisible = false; }); } }); } @override void initState() { super.initState(); //界面build完成后执行回调函数, WidgetsBinding.instance.addPostFrameCallback((_) => showServiceTermsDialog()); } //第一次进入,弹出服务条款对话框 void showServiceTermsDialog(){ bool isServiceTerms = SpUtils.getBool(CommonUtils.SERVICE_TERMS); if(!isServiceTerms){ WnBaseDialog.showServiceTermsDialog(context); } } //登录 void _login(String account, String password) async { bool isNetwork = await CommonNetwork.isNetwork(); if(!isNetwork) { CommonUtils.showNetworkError(context); return; } if(account.isEmpty){ CommonToast.show(context, "账号不能为空"); return; } else if(password.isEmpty){ CommonToast.show(context, "密码不能为空"); return; } _showLoadingDialog(); LogUtils.d('登录: username=${account.trim()}, password=${password.trim()}'); XmppManager.getInstance().connect(account, password, loginCallback: (data) async{ LogUtils.d("登录返回状态:${data} , ${mounted}"); if(mounted){ if(data){ VideoCallUtils.getInstance().connect(context); SpUtils.setString(CommonUtils.LOGIN_ACCOUNT, account); SpUtils.setString(CommonUtils.LOGIN_PASSWORD, password); await UserRepository.getInstance().syncFirst(account, AppManager.getInstance().packageName); _joinMucGroup(); CommonToast.show(context, "登录成功!"); Navigator.pop(context, true); eventBus.emit(BaseEvent(BaseEvent.TYPE_LOGIN, result: {})); } else { CommonToast.show(context, "登录失败!"); } setState(() { loadingVisible = false; }); } }); } //登录成功自动加入群, void _joinMucGroup() async { List<GroupBean>? list = await GroupRepository.getInstance().findAllGroup(); list?.forEach((groupBean) { XmppManager.getInstance().joinMucGroup(groupBean.groupId??""); }); } @override Widget build(BuildContext context) { return WillPopScope( child: Stack( children: [ Scaffold( appBar: WnAppBar.getAppBar(context, Text(widget.title), type : WnAppBar.TYPE_LOGIN), body: Center( child: SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[ _getRoundImage('assets/icons/app_icon.png', 100.0), SizedBox( height: 60, ), _usernameAndPasswordWidget(), SizedBox(height: 30,), _getLoginButton(), SizedBox(height: 30,), _getRegisterButton(), ], ), ), ), ), Offstage( child: CommonLoadingDialog(msg: "请稍后。。。",), offstage: !loadingVisible, ), ], ), onWillPop: () async { //点击返回按钮,直接退出 exit(0); } ); } //账号、密码 Widget _usernameAndPasswordWidget(){ return Container( margin: const EdgeInsets.only(left: 12, right: 12), child: Column( children: [ TextField( controller: _usernameController, keyboardType: TextInputType.emailAddress, decoration: InputDecoration( hintText: "请输入账号", icon: const Icon( Icons.account_box, size: 20.0, ), border: InputBorder.none, //使用 GestureDetector 实现手势识别 suffixIcon: GestureDetector( child: Offstage( child: Icon(Icons.clear), offstage: _username == '', ), //点击清除文本框内容 onTap: () { setState(() { _username = ''; _usernameController.clear(); }); }, ), ), onChanged: (value) { setState(() { _username = value; }); }, ), SizedBox(height:10), Container( height: 1, color: Colors.grey.shade400, ), // 密码输入框 TextField( obscureText: true, controller: _passwordController, keyboardType: TextInputType.text, decoration: InputDecoration( hintText: "请输入密码", icon: const Icon( Icons.lock_open, size: 20.0, ), suffixIcon: GestureDetector( child: Offstage( child: Icon(Icons.clear), offstage: _password == '', ), onTap: () { setState(() { _password = ''; _passwordController.clear(); }); }, ), border: InputBorder.none, ), onChanged: (value) { setState(() { _password = value; }); }, ), Container( height: 1, color: Colors.grey.shade400, ), ], ), ); } //头像按钮 Widget _getRoundImage(String imageName, double size) { return Container( width: size, height: size, clipBehavior: Clip.antiAlias, decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(size / 2)), ), child: Image.asset( imageName, fit: BoxFit.fitWidth, ), ); } //登录按钮 Widget _getLoginButton() { return MaterialButton( color: Colors.blue, textColor: Colors.white, padding: EdgeInsets.only(left: 28, top: 8, right: 28, bottom: 8), child: Text('登录',style: TextStyle(fontSize: 16),), onPressed: () { var username = _usernameController.text; var password = _passwordController.text; _login(username, password); }, ); } //注册按钮 Widget _getRegisterButton() { return MaterialButton( color: Colors.blue, textColor: Colors.white, padding: EdgeInsets.only(left: 28, top: 8, right: 28, bottom: 8), child: Text('注册',style: TextStyle(fontSize: 16),), onPressed: () { Navigator.push(context, MaterialPageRoute(builder: (context) => Register())); //Navigator.pop(context, false); }, ); } }