登录初版

This commit is contained in:
WoNiu 2024-04-05 18:09:40 +08:00
parent 0f139adcdd
commit f6af40701c
7 changed files with 608 additions and 17 deletions

96
lib/login/login_page.dart Normal file
View File

@ -0,0 +1,96 @@
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:common/utils/platform_utils.dart';
import 'package:common/utils/toast_utils.dart';
import 'package:flutter/material.dart';
import 'package:web_synchronization_tool/login/login_socket_utils.dart';
import 'package:web_synchronization_tool/login/widget/account_number_login_widget.dart';
import 'package:web_synchronization_tool/windows/windows_main_page.dart';
import 'package:window_manager/window_manager.dart';
import '../windows/socket_tool.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
@override
void initState() {
super.initState();
initWindow();
LoginSocketUtils.getInstance().connect();
}
initWindow(){
//
windowManager.setResizable(false);
const double width = 800;
const double height = 600;
//
const windowSize = Size(width, height);
windowManager.setSize(windowSize);
appWindow.minSize = windowSize;
windowManager.center();
windowManager.focus();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xff272b38),
body: Padding(
padding: const EdgeInsets.all( 140 ),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: PlatformUtils.isPhoneWeb(context) ? MediaQuery.of(context).size.width - 40 : 400,
child: AccountNumberLoginWidget(
loginTap: Login,
),
),
],
),
),
);
}
///
Login(String account, String password) {
ToastUtils.showLoading();
///
LoginSocketUtils.getInstance().login(account, password,(data){
ToastUtils.dismissLoading();
if(data['err'] == 1){
ToastUtils.showToast('登录失败!请检查账号和密码');
return;
}
///
LoginSocketUtils.getInstance().heartbeat();
/// socket
SocketUtils.getInstance().connect();
SocketUtils.getInstance().heartbeat();
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => const WindowsPage()),
(route) => false);
});
}
}

View File

@ -0,0 +1,156 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:common/utils/toast_utils.dart';
import 'package:web_synchronization_tool/windows/socket_tool.dart';
import '../windows/code.dart';
class LoginSocketUtils extends LoginSocket {
//
LoginSocketUtils._();
//
static LoginSocketUtils? _instance;
//
static LoginSocketUtils getInstance() {
_instance ??= LoginSocketUtils._();
return _instance!;
}
}
typedef loginBlockFun = Function(Map);
class LoginSocket{
String url = '192.168.200.17';
static int port = 37785;
String uuid = '';
RawDatagramSocket? socket;
int heartTime = 0;
connect() async {
socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 0);
//
socket?.listen((RawSocketEvent e) {
Datagram? d = socket?.receive();
if (d == null) return;
print(d.data);
Map data = json.decode(cutecode(d.data));// dataCute();
print(data);
//
if (data['op'] == 2){
loginBlock(data);
}
//
if (data['op'] == 4){
try{
if(data['time']> (3 * 60 * 1000) ){ // > 3
socketError();
}
}catch(e){
socketError();
}
}
});
// 30
socket?.timeout(const Duration(seconds: 30), onTimeout: (eventSink) {
ToastUtils.dismissLoading();
ToastUtils.showToast('连接超时');
eventSink.close(); //
});
}
socketError(){
///
ToastUtils.showLoading(msg: '重连中');
/// socket
SocketUtils.getInstance().socket?.close();
}
late loginBlockFun loginBlock;
login(String account,String passwoord,loginBlockFun blockFun){
loginBlock = blockFun;
Map map = {
'account':account,
'passwoord':passwoord,
'uuid':uuid // 0 c盘
};
List<int> data = jsonEncode(map).codeUnits;
socket?.send(data, InternetAddress(url), port);
}
///
heartbeat(){
DateTime now = DateTime.now();
int timestamp = now.millisecondsSinceEpoch; //
heartTime = timestamp;
Timer timer = Timer(const Duration(seconds: 20), () async {
if (socket == null){
//
await connect();
}
Map map = {
"op": 3,
};
List<int> data = jsonEncode(map).codeUnits;
socket?.send(data, InternetAddress(url), port);
heartbeat();
});
}
// ///
// List<int> dataMake(Map map){
// // Map对象转换为JSON字符串
// String json = jsonEncode(map);
// //
// String ps = makecode(json, skey);
// //
// Uint8List uinData = Uint8List.fromList(ps.codeUnits);
// // 4
// uinData = uinData.toLittle(value: uinData.length);
// return uinData.toList();
// }
//
// ///
// Map dataCute(Uint8List data){
//
// if (data.length <= 4) return {};
//
// Uint8List pData = data.sublist(4,data.length);
//
// String str = cutecode(pData);
//
// Map map = json.decode(str);
//
// return map;
// }
}

View File

@ -0,0 +1,108 @@
import 'package:common/utils/toast_utils.dart';
import 'login_text_field_widget.dart';
import 'package:flutter/material.dart';
import 'package:common/utils/widget_utils.dart';
class LoginController{
set name(String newText) {
if (nameChang != null){
nameChang!(newText);
}
}
Function(String name)? nameChang;
set password(String newText) {
if (passwordChang != null){
passwordChang!(newText);
}
}
Function(String password)? passwordChang;
}
class AccountNumberLoginWidget extends StatefulWidget {
const AccountNumberLoginWidget({Key? key, required this.loginTap, this.controller }) : super(key: key);
final LoginController? controller;
final Function(String,String) loginTap;
@override
State<AccountNumberLoginWidget> createState() =>
_AccountNumberLoginWidgetState();
}
class _AccountNumberLoginWidgetState extends State<AccountNumberLoginWidget> {
TextEditingController phoneController = TextEditingController();
TextEditingController passwordController = TextEditingController();
_login() {
if (phoneController.text.isEmpty || passwordController.text.isEmpty){
ToastUtils.showToast('请输入账号和密码');
return;
}
widget.loginTap(phoneController.text,passwordController.text);
}
@override
void initState() {
super.initState();
widget.controller?.nameChang = (name){
phoneController.text = name;
};
widget.controller?.passwordChang = (password){
passwordController.text = password;
};
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
LoginPhoneTextField(controller: phoneController),
WidgetUtils.spacer(height: 15),
LoginPasswordTextField(controller: passwordController,onSubmitted: (_){
_login();
},),
WidgetUtils.spacer(height: 15),
const Text('注册请直接输入账号密码 再点击注册',style: TextStyle(fontSize: 14,color: Colors.grey),),
WidgetUtils.spacer(height: 15),
TextButton(
onPressed: _login,
style: TextButton.styleFrom(padding: EdgeInsets.zero),
child: Container(
height: 40,
alignment: Alignment.center,
decoration: const BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(5))),
child: const Text(
'登录',
style: TextStyle(
fontSize: 16,
color: Colors.white,
),
),
),
),
],
);
}
}

View File

@ -0,0 +1,210 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class LoginPhoneTextField extends StatelessWidget {
const LoginPhoneTextField(
{Key? key, required this.controller, this.helperText, this.rightWidget})
: super(key: key);
final TextEditingController controller;
final String? helperText;
final Widget? rightWidget;
@override
Widget build(BuildContext context) {
Widget textField = SizedBox(
height: 40,
child: TextFieldBase(
keyboardType: TextInputType.number,
leftWidget: const Padding(
padding: EdgeInsets.only(left: 10),
child: Icon(
Icons.perm_contact_calendar_sharp,
color: Colors.grey,
size: 20,
),
),
rightWidget: rightWidget,
hintText: '请输入账号',
style: const TextStyle(color: Colors.white),
controller: controller,
),
);
return helperText == null
? textField
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
textField,
Padding(
padding: const EdgeInsets.only(top: 5),
child: Text(
helperText!,
style: const TextStyle(fontSize: 12, color: Colors.grey),
),
)
],
);
}
}
class LoginPasswordTextField extends StatefulWidget {
const LoginPasswordTextField(
{Key? key,
required this.controller,
this.hintText = '请输入密码',
this.onSubmitted})
: super(key: key);
final TextEditingController controller;
final String hintText;
final ValueChanged<String>? onSubmitted;
@override
State<LoginPasswordTextField> createState() => _LoginPasswordTextFieldState();
}
class _LoginPasswordTextFieldState extends State<LoginPasswordTextField> {
bool hideCancel = true;
bool obsureText = true;
@override
Widget build(BuildContext context) {
Widget cancel = Offstage(
offstage: hideCancel,
child: TextButton(
style: ButtonStyle(
overlayColor: MaterialStateProperty.all(Colors.transparent),
),
onPressed: () {
setState(() {
obsureText = !obsureText;
});
},
child: Icon(
Icons.remove_red_eye_rounded,
color: Colors.grey,
size: 20,
),
),
);
widget.controller.addListener(() {
setState(() {
hideCancel = !widget.controller.text.isNotEmpty;
});
});
return SizedBox(
height: 40,
child: TextFieldBase(
hintText: widget.hintText,
controller: widget.controller,
obsureText: obsureText,
onSubmitted: widget.onSubmitted,
leftWidget: const Padding(
padding: EdgeInsets.only(left: 10),
child: Icon(
Icons.lock_sharp,
color: Colors.grey,
size: 20,
),
),
style: const TextStyle(color: Colors.white),
rightWidget: cancel,
),
);
}
}
class TextFieldBase extends StatefulWidget {
const TextFieldBase(
{Key? key,
this.leftWidget,
this.rightWidget,
this.hintText = '',
required this.controller,
this.obsureText = false,
this.keyboardType,
this.textAlign = TextAlign.start,
this.onChanged,
this.onSubmitted,
this.inputFormatters,
this.focusNode,
this.decoration,
this.style,
this.noFocusSubmitted = false})
: super(key: key);
final TextEditingController controller;
final FocusNode? focusNode;
final Widget? leftWidget;
final Widget? rightWidget;
final InputDecoration? decoration;
final TextStyle? style;
final TextInputType? keyboardType;
final String hintText;
///
final bool obsureText;
final TextAlign textAlign;
final List<TextInputFormatter>? inputFormatters;
///
final bool noFocusSubmitted;
final ValueChanged<String>? onChanged;
final ValueChanged<String>? onSubmitted;
@override
State<TextFieldBase> createState() => _TextFieldBaseState();
}
class _TextFieldBaseState extends State<TextFieldBase> {
late FocusNode focusNode;
@override
void initState() {
super.initState();
focusNode = widget.focusNode ?? FocusNode();
if (widget.onSubmitted != null && widget.noFocusSubmitted){
focusNode.addListener(() {
if (focusNode.hasFocus == false){
widget.onSubmitted!(widget.controller.text);
}
});
}
}
@override
Widget build(BuildContext context) {
final decoration = widget.decoration ??
InputDecoration(
prefixIcon: widget.leftWidget,
prefixIconConstraints: const BoxConstraints(minWidth: 4),
suffixIcon: widget.rightWidget,
hintText: widget.hintText,
hintStyle: const TextStyle(color: Colors.grey),
border: MaterialStateOutlineInputBorder.resolveWith((states) =>
const OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey))),
contentPadding:
const EdgeInsets.only(left: 0, top: 0, bottom: 0, right: 15),
);
return TextField(
focusNode: focusNode,
controller: widget.controller,
inputFormatters: widget.inputFormatters,
style: widget.style ?? const TextStyle(fontSize: 14),
keyboardType: widget.keyboardType,
obscureText: widget.obsureText,
textAlign: widget.textAlign,
cursorColor: Colors.blue,
onChanged: widget.onChanged,
onSubmitted: widget.onSubmitted,
decoration: decoration,
);
}
}

View File

@ -1,6 +1,9 @@
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:common/utils/toast_utils.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart';
import 'package:web_synchronization_tool/windows/windows_main_page.dart';
import 'package:web_synchronization_tool/login/login_page.dart';
import 'package:web_synchronization_tool/login/login_socket_utils.dart';
import 'package:window_manager/window_manager.dart';
void main() async {
@ -32,17 +35,14 @@ class _MyAppState extends State<MyApp> {
void initState() {
super.initState();
//
windowManager.setResizable(false);
info();
}
info() async {
final deviceInfo = await DeviceInfoPlugin().deviceInfo;
LoginSocketUtils.getInstance().uuid = deviceInfo.data.toString();
const double width = 1920;
const double height = 1000;
//
const windowSize = Size(width, height);
windowManager.setSize(windowSize);
appWindow.minSize = windowSize;
windowManager.center();
windowManager.focus();
}
@override
@ -54,7 +54,9 @@ class _MyAppState extends State<MyApp> {
useMaterial3: true,
),
// home: const MainPage(),
home: const WindowsPage(),
home: const LoginPage(),
navigatorObservers: [FlutterSmartDialog.observer],
builder: FlutterSmartDialog.init(),
);
}
}

View File

@ -1,7 +1,9 @@
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:flutter/material.dart';
import 'package:web_synchronization_tool/windows/socket_tool.dart';
import 'package:web_synchronization_tool/windows/web_grid_view.dart';
import 'package:webview_windows/webview_windows.dart';
import 'package:window_manager/window_manager.dart';
class WindowsPage extends StatefulWidget {
const WindowsPage({super.key});
@ -24,12 +26,27 @@ class _WindowsPageState extends State<WindowsPage> {
void initState() {
super.initState();
SocketUtils.getInstance().connect();
SocketUtils.getInstance().heartbeat();
initWindow();
controllerInit();
}
initWindow(){
//
windowManager.setResizable(false);
const double width = 1920;
const double height = 1000;
//
const windowSize = Size(width, height);
windowManager.setSize(windowSize);
appWindow.minSize = windowSize;
windowManager.center();
windowManager.focus();
}
Future controllerInit() async {
await mainController.initialize();

View File

@ -11,11 +11,13 @@ dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
common:
path: E:\Code\FlutterProjectCode\common
webview_windows:
path: ../flutter-webview-windows-main
bitsdojo_window: ^0.1.6
window_manager: ^0.3.7
device_info_plus: ^9.1.2
dev_dependencies:
flutter_test: