Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
ethan
/
appframe
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
Commit 990951ff
authored
2026-01-19 14:42:27 +0800
by
tanghuan
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
增加通过Apple登录的功能,以及优化了toast方式的错误信息提示
1 parent
75241a4c
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
449 additions
and
265 deletions
lib/bloc/login_main_cubit.dart
lib/bloc/login_phone_cubit.dart
lib/bloc/login_qr_cubit.dart
lib/bloc/setting/account_user_cubit.dart
lib/bloc/web_cubit.dart
lib/config/locator.dart
lib/data/repositories/phone_auth_repository.dart
lib/data/repositories/user_auth_repository.dart
lib/data/repositories/wechat_auth_repository.dart
lib/ui/pages/login_main_page.dart
lib/ui/pages/login_phone_page.dart
lib/ui/pages/login_qr_page.dart
lib/ui/pages/web_page.dart
lib/bloc/login_main_cubit.dart
View file @
990951f
import
'package:appframe/config/constant.dart'
;
import
'package:appframe/config/locator.dart'
;
import
'package:appframe/config/routes.dart'
;
import
'package:appframe/data/repositories/user_auth_repository.dart'
;
import
'package:appframe/data/repositories/wechat_auth_repository.dart'
;
import
'package:equatable/equatable.dart'
;
import
'package:flutter/
foundation
.dart'
;
import
'package:flutter/
material
.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:fluttertoast/fluttertoast.dart'
;
import
'package:fluwx/fluwx.dart'
;
import
'package:shared_preferences/shared_preferences.dart'
;
import
'package:sign_in_with_apple/sign_in_with_apple.dart'
;
class
LoginMainState
extends
Equatable
{
final
bool
agreed
;
final
bool
showAgreed
;
final
bool
showNeedWechatForApple
;
final
int
loginType
;
// 1=Wechat,2=Apple
final
String
appleUserIdentifier
;
final
bool
loading
;
final
bool
showSnackBar
;
final
String
snackBarMsg
;
const
LoginMainState
({
this
.
agreed
=
false
,
this
.
showAgreed
=
false
,
this
.
showNeedWechatForApple
=
false
,
this
.
loginType
=
0
,
this
.
appleUserIdentifier
=
''
,
this
.
loading
=
false
,
this
.
showSnackBar
=
false
,
this
.
snackBarMsg
=
''
,
});
LoginMainState
copyWith
({
bool
?
agreed
,
bool
?
showAgreed
,
bool
?
showNeedWechatForApple
,
int
?
loginType
,
bool
?
loading
,
bool
?
showSnackBar
,
String
?
snackBarMsg
,
String
?
appleUserIdentifier
,
})
{
return
LoginMainState
(
agreed:
agreed
??
this
.
agreed
,
showAgreed:
showAgreed
??
this
.
showAgreed
,
showNeedWechatForApple:
showNeedWechatForApple
??
this
.
showNeedWechatForApple
,
loginType:
loginType
??
this
.
loginType
,
loading:
loading
??
this
.
loading
,
appleUserIdentifier:
appleUserIdentifier
??
this
.
appleUserIdentifier
,
);
}
...
...
@@ -42,8 +51,9 @@ class LoginMainState extends Equatable {
agreed
,
showAgreed
,
loading
,
showSnackBar
,
snackBarMsg
,
showNeedWechatForApple
,
loginType
,
appleUserIdentifier
,
];
}
...
...
@@ -51,11 +61,13 @@ class LoginMainCubit extends Cubit<LoginMainState> {
late
final
Fluwx
_fluwx
;
late
final
FluwxCancelable
_fluwxCancelable
;
late
final
WechatAuthRepository
_wechatAuthRepository
;
late
final
UserAuthRepository
_userAuthRepository
;
LoginMainCubit
(
super
.
initialState
)
{
_fluwx
=
getIt
.
get
<
Fluwx
>();
_fluwxCancelable
=
_fluwx
.
addSubscriber
(
_responseListener
);
_wechatAuthRepository
=
getIt
.
get
<
WechatAuthRepository
>();
_userAuthRepository
=
getIt
.
get
<
UserAuthRepository
>();
}
void
toggleAgreed
(
bool
value
)
{
...
...
@@ -64,16 +76,79 @@ class LoginMainCubit extends Cubit<LoginMainState> {
void
confirmAgreed
()
{
emit
(
state
.
copyWith
(
agreed:
true
,
showAgreed:
false
));
wechatAuth
();
if
(
state
.
loginType
==
1
)
{
wechatAuth
();
}
else
if
(
state
.
loginType
==
2
)
{
appleAuth
();
}
}
void
cancelAgreed
()
{
emit
(
state
.
copyWith
(
showAgreed:
false
));
}
void
wechatAuth
()
async
{
///
/// 通过 Apple 登录
///
void
appleAuth
()
async
{
if
(!
state
.
agreed
)
{
emit
(
state
.
copyWith
(
showAgreed:
true
));
emit
(
state
.
copyWith
(
showAgreed:
true
,
loginType:
2
));
return
;
}
AuthorizationCredentialAppleID
credential
=
await
SignInWithApple
.
getAppleIDCredential
(
scopes:
[
AppleIDAuthorizationScopes
.
email
,
AppleIDAuthorizationScopes
.
fullName
,
],
);
debugPrint
(
'用户唯一标识:
${credential.userIdentifier}
'
);
debugPrint
(
'用户邮箱:
${credential.email}
'
);
debugPrint
(
'用户姓名:
${credential.givenName}
${credential.familyName}
'
);
// 应将 credential.authorizationCode 发送给后端服务器,由后端与 Apple 服务器验证该码的有效性
debugPrint
(
'授权码 (authorizationCode):
${credential.authorizationCode}
'
);
debugPrint
(
'身份令牌 (identityToken):
${credential.identityToken}
'
);
if
(
credential
.
userIdentifier
==
null
)
{
Fluttertoast
.
showToast
(
msg:
'授权失败'
,
backgroundColor:
Colors
.
red
);
return
;
}
///
/// 处理 credential,发送到服务器获取用户信息。有关联用户信息,则登录成功;没有关联用户信息,则弹出提示框提示用户授权微信认证
///
var
resultData
=
await
_userAuthRepository
.
appleLogin
(
credential
.
userIdentifier
!,
credential
.
authorizationCode
,
credential
.
identityToken
!)
as
Map
<
String
,
dynamic
>?;
if
(
resultData
==
null
)
{
Fluttertoast
.
showToast
(
msg:
'登录请求处理失败'
,
backgroundColor:
Colors
.
red
);
return
;
}
if
(
resultData
[
'code'
]
!=
0
)
{
Fluttertoast
.
showToast
(
msg:
resultData
[
'error'
],
backgroundColor:
Colors
.
red
);
return
;
}
var
data
=
resultData
[
'data'
]
as
Map
<
String
,
dynamic
>;
int
binding
=
resultData
[
'binding'
];
if
(
binding
==
1
)
{
_handleLoginSuccess
(
data
);
}
else
{
// 设置 appleUserIdentifier 状态
emit
(
state
.
copyWith
(
appleUserIdentifier:
data
[
'appleUid'
]!));
// 通知用户需要授权微信认证
emit
(
state
.
copyWith
(
showNeedWechatForApple:
true
));
}
}
Future
<
void
>
wechatAuthForApple
()
async
{
emit
(
state
.
copyWith
(
showNeedWechatForApple:
false
));
wechatAuth
();
}
Future
<
void
>
wechatAuth
()
async
{
if
(!
state
.
agreed
)
{
emit
(
state
.
copyWith
(
showAgreed:
true
,
loginType:
1
));
return
;
}
...
...
@@ -82,8 +157,7 @@ class LoginMainCubit extends Cubit<LoginMainState> {
);
if
(!
authResult
)
{
emit
(
state
.
copyWith
(
showSnackBar:
true
,
snackBarMsg:
'微信授权处理失败'
));
emit
(
state
.
copyWith
(
showSnackBar:
false
));
Fluttertoast
.
showToast
(
msg:
'微信授权处理失败'
,
backgroundColor:
Colors
.
red
);
return
;
}
...
...
@@ -110,94 +184,99 @@ class LoginMainCubit extends Cubit<LoginMainState> {
// 请求接口异常
if
(
resultData
==
null
)
{
emit
(
state
.
copyWith
(
loading:
false
,
showSnackBar:
true
,
snackBarMsg:
'登录请求处理失败'
));
emit
(
state
.
copyWith
(
showSnackBar:
false
));
Fluttertoast
.
showToast
(
msg:
'登录请求处理失败'
,
backgroundColor:
Colors
.
red
);
return
;
}
// 状态码错误
if
(
resultData
[
'resultCode'
]
!=
'001'
)
{
emit
(
state
.
copyWith
(
loading:
false
,
showSnackBar:
true
,
snackBarMsg:
'登录请求状态失败'
));
emit
(
state
.
copyWith
(
showSnackBar:
false
));
Fluttertoast
.
showToast
(
msg:
'登录请求状态失败'
,
backgroundColor:
Colors
.
red
);
return
;
}
var
data
=
resultData
[
'data'
]
as
Map
<
String
,
dynamic
>;
var
roles
=
data
[
'roles'
];
// 过滤出家长角色的数据
roles
.
removeWhere
((
element
)
=>
element
[
'userType'
]
!=
2
);
var
sessionCode
=
data
[
'sessionCode'
];
var
userCode
=
data
[
'userCode'
];
var
classCode
=
''
;
var
userType
=
0
;
var
stuId
=
''
;
var
sharedPreferences
=
getIt
.
get
<
SharedPreferences
>();
if
(
roles
.
isNotEmpty
)
{
var
role
=
roles
[
0
];
classCode
=
role
[
'classCode'
];
userType
=
role
[
'userType'
];
stuId
=
role
[
'stuId'
];
// 将角色中的班级数据处理后,进行缓存
List
<
String
>
classIdList
=
[];
for
(
var
role
in
roles
)
{
classIdList
.
add
(
role
[
'classCode'
]
as
String
);
}
debugPrint
(
'classCodeIds:--------------
$classIdList
'
);
sharedPreferences
.
setStringList
(
Constant
.
classIdSetKey
,
classIdList
);
}
else
{
sharedPreferences
.
setStringList
(
Constant
.
classIdSetKey
,
[]);
_handleLoginSuccess
(
data
);
}
}
void
_handleLoginSuccess
(
Map
<
String
,
dynamic
>
data
)
{
var
roles
=
data
[
'roles'
];
// 过滤出家长角色的数据
roles
.
removeWhere
((
element
)
=>
element
[
'userType'
]
!=
2
);
var
sessionCode
=
data
[
'sessionCode'
];
var
userCode
=
data
[
'userCode'
];
var
classCode
=
''
;
var
userType
=
0
;
var
stuId
=
''
;
var
sharedPreferences
=
getIt
.
get
<
SharedPreferences
>();
if
(
roles
.
isNotEmpty
)
{
var
role
=
roles
[
0
];
classCode
=
role
[
'classCode'
];
userType
=
role
[
'userType'
];
stuId
=
role
[
'stuId'
];
List
<
String
>
classIdList
=
[];
for
(
var
role
in
roles
)
{
classIdList
.
add
(
role
[
'classCode'
]
as
String
);
}
debugPrint
(
'classCodeIds:--------------
$classIdList
'
);
sharedPreferences
.
setStringList
(
Constant
.
classIdSetKey
,
classIdList
);
}
else
{
sharedPreferences
.
setStringList
(
Constant
.
classIdSetKey
,
[]);
}
var
preUserCode
=
sharedPreferences
.
getString
(
'pre_userCode'
)
??
''
;
if
(
userCode
!=
preUserCode
)
{
sharedPreferences
.
setString
(
'pre_userCode'
,
userCode
);
sharedPreferences
.
setString
(
'pre_classCode'
,
classCode
);
sharedPreferences
.
setInt
(
'pre_userType'
,
userType
);
sharedPreferences
.
setString
(
'pre_stuId'
,
stuId
);
}
else
{
var
preClassCode
=
sharedPreferences
.
getString
(
'pre_classCode'
)
??
''
;
var
preUserType
=
sharedPreferences
.
getInt
(
'pre_userType'
)
??
0
;
var
preStuId
=
sharedPreferences
.
getString
(
'pre_stuId'
)
??
''
;
var
preUserCode
=
sharedPreferences
.
getString
(
'pre_userCode'
)
??
''
;
if
(
userCode
!=
preUserCode
)
{
// 新用户登录
if
(
preClassCode
!=
''
&&
roles
.
any
((
element
)
=>
element
[
'classCode'
]
==
preClassCode
&&
element
[
'userType'
]
==
preUserType
&&
element
[
'stuId'
]
==
preStuId
))
{
classCode
=
preClassCode
;
userType
=
preUserType
;
stuId
=
preStuId
;
}
else
{
sharedPreferences
.
setString
(
'pre_userCode'
,
userCode
);
sharedPreferences
.
setString
(
'pre_classCode'
,
classCode
);
sharedPreferences
.
setInt
(
'pre_userType'
,
userType
);
sharedPreferences
.
setString
(
'pre_stuId'
,
stuId
);
}
else
{
// 前一个登录用户重新登录
var
preClassCode
=
sharedPreferences
.
getString
(
'pre_classCode'
)
??
''
;
var
preUserType
=
sharedPreferences
.
getInt
(
'pre_userType'
)
??
0
;
var
preStuId
=
sharedPreferences
.
getString
(
'pre_stuId'
)
??
''
;
if
(
preClassCode
!=
''
&&
roles
.
any
((
element
)
=>
element
[
'classCode'
]
==
preClassCode
&&
element
[
'userType'
]
==
preUserType
&&
element
[
'stuId'
]
==
preStuId
))
{
classCode
=
preClassCode
;
userType
=
preUserType
;
stuId
=
preStuId
;
}
else
{
sharedPreferences
.
setString
(
'pre_userCode'
,
userCode
);
sharedPreferences
.
setString
(
'pre_classCode'
,
classCode
);
sharedPreferences
.
setInt
(
'pre_userType'
,
userType
);
sharedPreferences
.
setString
(
'pre_stuId'
,
stuId
);
}
}
}
sharedPreferences
.
setString
(
'auth_sessionCode'
,
sessionCode
);
sharedPreferences
.
setString
(
'auth_userCode'
,
userCode
);
sharedPreferences
.
setString
(
'auth_classCode'
,
classCode
);
sharedPreferences
.
setInt
(
'auth_userType'
,
userType
);
sharedPreferences
.
setString
(
'auth_stuId'
,
stuId
);
sharedPreferences
.
setString
(
'auth_sessionCode'
,
sessionCode
);
sharedPreferences
.
setString
(
'auth_userCode'
,
userCode
);
sharedPreferences
.
setString
(
'auth_classCode'
,
classCode
);
sharedPreferences
.
setInt
(
'auth_userType'
,
userType
);
sharedPreferences
.
setString
(
'auth_stuId'
,
stuId
);
router
.
go
(
'/web'
,
extra:
{
'sessionCode'
:
sessionCode
,
'userCode'
:
userCode
,
'classCode'
:
classCode
,
'userType'
:
userType
,
'stuId'
:
stuId
,
},
);
// 针对 Apple 登录
if
(
state
.
loginType
==
2
&&
state
.
appleUserIdentifier
.
isNotEmpty
)
{
// appleUserIdentifier未绑定,则进行绑定
_userAuthRepository
.
newBinding
(
state
.
appleUserIdentifier
,
userCode
);
}
router
.
go
(
'/web'
,
extra:
{
'sessionCode'
:
sessionCode
,
'userCode'
:
userCode
,
'classCode'
:
classCode
,
'userType'
:
userType
,
'stuId'
:
stuId
,
},
);
}
@override
...
...
lib/bloc/login_phone_cubit.dart
View file @
990951f
...
...
@@ -7,22 +7,19 @@ import 'package:appframe/data/repositories/phone_auth_repository.dart';
import
'package:equatable/equatable.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:fluttertoast/fluttertoast.dart'
;
import
'package:shared_preferences/shared_preferences.dart'
;
class
LoginPhoneState
extends
Equatable
{
final
bool
agreed
;
final
bool
showAgreed
;
final
bool
showSnackBar
;
final
String
snackBarMsg
;
final
bool
allowSend
;
final
int
seconds
;
const
LoginPhoneState
({
this
.
agreed
=
false
,
this
.
showAgreed
=
false
,
this
.
showSnackBar
=
false
,
this
.
snackBarMsg
=
''
,
this
.
allowSend
=
true
,
this
.
seconds
=
0
,
});
...
...
@@ -30,16 +27,12 @@ class LoginPhoneState extends Equatable {
LoginPhoneState
copyWith
({
bool
?
agreed
,
bool
?
showAgreed
,
bool
?
showSnackBar
,
String
?
snackBarMsg
,
bool
?
allowSend
,
int
?
seconds
,
})
{
return
LoginPhoneState
(
agreed:
agreed
??
this
.
agreed
,
showAgreed:
showAgreed
??
this
.
showAgreed
,
showSnackBar:
showSnackBar
??
this
.
showSnackBar
,
snackBarMsg:
snackBarMsg
??
this
.
snackBarMsg
,
allowSend:
allowSend
??
this
.
allowSend
,
seconds:
seconds
??
this
.
seconds
,
);
...
...
@@ -49,8 +42,6 @@ class LoginPhoneState extends Equatable {
List
<
Object
?>
get
props
=>
[
agreed
,
showAgreed
,
showSnackBar
,
snackBarMsg
,
allowSend
,
seconds
,
];
...
...
@@ -100,16 +91,15 @@ class LoginPhoneCubit extends Cubit<LoginPhoneState> {
// 验证手机号码
String
phone
=
_phoneController
.
text
;
if
(!
RegExp
(
r'^1[3-9][0-9]{9}$'
).
hasMatch
(
phone
))
{
emit
(
state
.
copyWith
(
showSnackBar:
true
,
snackBarMsg:
'请输入正确的手机号码'
)
);
emit
(
state
.
copyWith
(
showSnackBar:
false
));
Fluttertoast
.
showToast
(
msg:
'请输入正确的手机号码'
,
backgroundColor:
Colors
.
red
);
return
;
}
// 发送验证码
var
result
=
await
_phoneAuthRepository
.
verifyCode
(
phone
,
0
);
if
(
result
[
'code'
]
!=
0
)
{
emit
(
state
.
copyWith
(
showSnackBar:
true
,
snackBarMsg:
result
[
'error'
]));
emit
(
state
.
copyWith
(
showSnackBar:
false
));
Fluttertoast
.
showToast
(
msg:
result
[
'error'
],
backgroundColor:
Colors
.
red
);
return
;
}
...
...
@@ -125,14 +115,12 @@ class LoginPhoneCubit extends Cubit<LoginPhoneState> {
String
verifyCode
=
_codeController
.
text
;
if
(!
RegExp
(
r'^1[3-9][0-9]{9}$'
).
hasMatch
(
phone
))
{
emit
(
state
.
copyWith
(
showSnackBar:
true
,
snackBarMsg:
'请输入正确的手机号码'
));
emit
(
state
.
copyWith
(
showSnackBar:
false
));
Fluttertoast
.
showToast
(
msg:
'请输入正确的手机号码'
,
backgroundColor:
Colors
.
red
);
return
;
}
if
(!
RegExp
(
r'^\d{4}$'
).
hasMatch
(
verifyCode
))
{
emit
(
state
.
copyWith
(
showSnackBar:
true
,
snackBarMsg:
'请输入正确的验证码'
));
emit
(
state
.
copyWith
(
showSnackBar:
false
));
Fluttertoast
.
showToast
(
msg:
'请输入正确的验证码'
,
backgroundColor:
Colors
.
red
);
return
;
}
...
...
@@ -143,17 +131,19 @@ class LoginPhoneCubit extends Cubit<LoginPhoneState> {
var
resultData
=
await
_phoneAuthRepository
.
login
(
phone
,
verifyCode
);
if
(
resultData
==
null
)
{
emit
(
state
.
copyWith
(
showSnackBar:
true
,
snackBarMsg:
'登录请求失败'
));
emit
(
state
.
copyWith
(
showSnackBar:
false
));
Fluttertoast
.
showToast
(
msg:
'登录请求失败'
,
backgroundColor:
Colors
.
red
);
return
;
}
if
(
resultData
[
'code'
]
!=
0
)
{
emit
(
state
.
copyWith
(
showSnackBar:
true
,
snackBarMsg:
resultData
[
'error'
]));
emit
(
state
.
copyWith
(
showSnackBar:
false
));
Fluttertoast
.
showToast
(
msg:
resultData
[
'error'
],
backgroundColor:
Colors
.
red
);
return
;
}
var
data
=
resultData
[
'data'
]
as
Map
<
String
,
dynamic
>;
_handleLoginSuccess
(
data
);
}
void
_handleLoginSuccess
(
Map
<
String
,
dynamic
>
data
)
{
var
roles
=
data
[
'roles'
];
// 过滤出家长角色的数据
roles
.
removeWhere
((
element
)
=>
element
[
'userType'
]
!=
2
);
...
...
lib/bloc/login_qr_cubit.dart
View file @
990951f
import
'dart:convert'
;
import
'dart:typed_data'
;
import
'package:appframe/config/constant.dart'
;
import
'package:appframe/config/locator.dart'
;
...
...
@@ -8,7 +7,9 @@ import 'package:appframe/data/repositories/wechat_auth_repository.dart';
import
'package:crypto/crypto.dart'
;
import
'package:equatable/equatable.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:fluttertoast/fluttertoast.dart'
;
import
'package:fluwx/fluwx.dart'
;
import
'package:shared_preferences/shared_preferences.dart'
;
...
...
@@ -16,30 +17,22 @@ class LoginQrState extends Equatable {
final
int
status
;
final
Uint8List
?
image
;
final
String
tip
;
final
bool
showSnackBar
;
final
String
snackBarMsg
;
const
LoginQrState
({
this
.
status
=
0
,
this
.
image
,
this
.
tip
=
''
,
this
.
showSnackBar
=
false
,
this
.
snackBarMsg
=
''
,
});
LoginQrState
copyWith
({
int
?
status
,
Uint8List
?
image
,
String
?
tip
,
bool
?
showSnackBar
,
String
?
snackBarMsg
,
})
{
return
LoginQrState
(
status:
status
??
this
.
status
,
image:
image
??
this
.
image
,
tip:
tip
??
this
.
tip
,
showSnackBar:
showSnackBar
??
this
.
showSnackBar
,
snackBarMsg:
snackBarMsg
??
this
.
snackBarMsg
,
);
}
...
...
@@ -48,8 +41,6 @@ class LoginQrState extends Equatable {
status
,
image
,
tip
,
showSnackBar
,
snackBarMsg
,
];
}
...
...
@@ -71,15 +62,13 @@ class LoginQrCubit extends Cubit<LoginQrState> {
var
resultData
=
await
_wechatAuthRepository
.
getTicket
()
as
Map
<
String
,
dynamic
>?;
// 请求接口异常
if
(
resultData
==
null
)
{
emit
(
state
.
copyWith
(
showSnackBar:
true
,
snackBarMsg:
'生成二维码失败'
));
emit
(
state
.
copyWith
(
showSnackBar:
false
));
Fluttertoast
.
showToast
(
msg:
'生成二维码失败'
,
backgroundColor:
Colors
.
red
);
return
;
}
// 状态码错误
if
(
resultData
[
'resultCode'
]
!=
'001'
)
{
emit
(
state
.
copyWith
(
showSnackBar:
true
,
snackBarMsg:
'生成二维码状态错误'
));
emit
(
state
.
copyWith
(
showSnackBar:
false
));
Fluttertoast
.
showToast
(
msg:
'生成二维码状态错误'
,
backgroundColor:
Colors
.
red
);
return
;
}
...
...
@@ -100,8 +89,7 @@ class LoginQrCubit extends Cubit<LoginQrState> {
);
if
(!
authResult
)
{
emit
(
state
.
copyWith
(
showSnackBar:
true
,
snackBarMsg:
'请求微信失败'
));
emit
(
state
.
copyWith
(
showSnackBar:
false
));
Fluttertoast
.
showToast
(
msg:
'请求微信失败'
,
backgroundColor:
Colors
.
red
);
return
;
}
}
...
...
@@ -151,15 +139,13 @@ class LoginQrCubit extends Cubit<LoginQrState> {
// 请求接口异常
if
(
resultData
==
null
)
{
emit
(
state
.
copyWith
(
showSnackBar:
true
,
snackBarMsg:
'登录请求处理失败'
));
emit
(
state
.
copyWith
(
showSnackBar:
false
));
Fluttertoast
.
showToast
(
msg:
'登录请求处理失败'
,
backgroundColor:
Colors
.
red
);
return
;
}
// 状态码错误
if
(
resultData
[
'resultCode'
]
!=
'001'
)
{
emit
(
state
.
copyWith
(
showSnackBar:
true
,
snackBarMsg:
'登录请求状态失败'
));
emit
(
state
.
copyWith
(
showSnackBar:
false
));
Fluttertoast
.
showToast
(
msg:
'登录请求状态失败'
,
backgroundColor:
Colors
.
red
);
return
;
}
...
...
lib/bloc/setting/account_user_cubit.dart
View file @
990951f
...
...
@@ -5,7 +5,7 @@ import 'package:appframe/config/constant.dart';
import
'package:appframe/config/env_config.dart'
;
import
'package:appframe/config/locator.dart'
;
import
'package:appframe/config/routes.dart'
;
import
'package:appframe/data/repositories/
phone
_auth_repository.dart'
;
import
'package:appframe/data/repositories/
user
_auth_repository.dart'
;
import
'package:appframe/services/api_service.dart'
;
import
'package:dio/dio.dart'
;
import
'package:equatable/equatable.dart'
;
...
...
@@ -68,7 +68,7 @@ class AccountUserCubit extends Cubit<AccountUserState> {
late
TextEditingController
_nameController
;
late
TextEditingController
_nickNameController
;
late
final
PhoneAuthRepository
_phone
AuthRepository
;
late
final
UserAuthRepository
_user
AuthRepository
;
TextEditingController
get
nameController
=>
_nameController
;
...
...
@@ -78,7 +78,7 @@ class AccountUserCubit extends Cubit<AccountUserState> {
_nameController
=
TextEditingController
(
text:
state
.
name
);
_nickNameController
=
TextEditingController
(
text:
state
.
nickname
);
_
phoneAuthRepository
=
getIt
.
get
<
Phone
AuthRepository
>();
_
userAuthRepository
=
getIt
.
get
<
User
AuthRepository
>();
}
void
updateAvatar
(
String
avatarPath
)
{
...
...
@@ -110,7 +110,7 @@ class AccountUserCubit extends Cubit<AccountUserState> {
var
sharedPreferences
=
getIt
.
get
<
SharedPreferences
>();
var
userCode
=
sharedPreferences
.
getString
(
'auth_userCode'
)
??
''
;
var
result
=
await
_
phone
AuthRepository
.
updateUser
(
userCode
,
name
,
nickname
,
imgPath
);
var
result
=
await
_
user
AuthRepository
.
updateUser
(
userCode
,
name
,
nickname
,
imgPath
);
emit
(
state
.
copyWith
(
isLoading:
false
));
...
...
lib/bloc/web_cubit.dart
View file @
990951f
...
...
@@ -17,6 +17,7 @@ import 'package:dio/dio.dart';
import
'package:equatable/equatable.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:fluttertoast/fluttertoast.dart'
;
import
'package:fluwx/fluwx.dart'
;
import
'package:path_provider/path_provider.dart'
;
import
'package:permission_handler/permission_handler.dart'
;
...
...
@@ -66,10 +67,6 @@ class WebState extends Equatable {
final
bool
chooseVideoCmdFlag
;
final
String
chooseVideoCmdMessage
;
/// 提示信息
final
bool
showSnackBar
;
final
String
snackBarMsg
;
final
String
h5Version
;
/// 用于测试监测问题
...
...
@@ -99,8 +96,6 @@ class WebState extends Equatable {
this
.
chooseImageCmdMessage
=
''
,
this
.
chooseVideoCmdFlag
=
false
,
this
.
chooseVideoCmdMessage
=
''
,
this
.
showSnackBar
=
false
,
this
.
snackBarMsg
=
''
,
this
.
h5Version
=
''
,
this
.
testMsg
=
''
,
});
...
...
@@ -130,8 +125,6 @@ class WebState extends Equatable {
String
?
chooseImageCmdMessage
,
bool
?
chooseVideoCmdFlag
,
String
?
chooseVideoCmdMessage
,
bool
?
showSnackBar
,
String
?
snackBarMsg
,
String
?
h5Version
,
String
?
testMsg
,
})
{
...
...
@@ -159,8 +152,6 @@ class WebState extends Equatable {
chooseImageCmdMessage:
chooseImageCmdMessage
??
this
.
chooseImageCmdMessage
,
chooseVideoCmdFlag:
chooseVideoCmdFlag
??
this
.
chooseVideoCmdFlag
,
chooseVideoCmdMessage:
chooseVideoCmdMessage
??
this
.
chooseVideoCmdMessage
,
showSnackBar:
showSnackBar
??
this
.
showSnackBar
,
snackBarMsg:
snackBarMsg
??
this
.
snackBarMsg
,
h5Version:
h5Version
??
this
.
h5Version
,
testMsg:
testMsg
??
this
.
testMsg
,
);
...
...
@@ -190,8 +181,6 @@ class WebState extends Equatable {
chooseImageCmdMessage
,
chooseVideoCmdFlag
,
chooseVideoCmdMessage
,
showSnackBar
,
snackBarMsg
,
h5Version
,
testMsg
,
];
...
...
@@ -1128,12 +1117,22 @@ class WebCubit extends Cubit<WebState> with WidgetsBindingObserver {
var
vol
=
await
volumeController
.
getVolume
();
debugPrint
(
'检测音量:
$vol
'
);
if
(
vol
==
0
)
{
emit
(
state
.
copyWith
(
showSnackBar:
true
,
snackBarMsg:
'设备处于静音状态,请调整音量'
));
emit
(
state
.
copyWith
(
showSnackBar:
false
,
snackBarMsg:
''
));
// emit(state.copyWith(showSnackBar: true, snackBarMsg: '设备处于静音状态,请调整音量'));
// emit(state.copyWith(showSnackBar: false, snackBarMsg: ''));
Fluttertoast
.
showToast
(
msg:
'设备处于静音状态,请调整音量'
,
backgroundColor:
Colors
.
red
,
gravity:
ToastGravity
.
TOP
,
);
_isFirstTimeOfCheck
=
false
;
}
else
if
(
vol
<=
0.15
)
{
emit
(
state
.
copyWith
(
showSnackBar:
true
,
snackBarMsg:
'设备音量过低,建议调高音量'
));
emit
(
state
.
copyWith
(
showSnackBar:
false
,
snackBarMsg:
''
));
// emit(state.copyWith(showSnackBar: true, snackBarMsg: '设备音量过低,建议调高音量'));
// emit(state.copyWith(showSnackBar: false, snackBarMsg: ''));
Fluttertoast
.
showToast
(
msg:
'设备音量过低,建议调高音量'
,
backgroundColor:
Colors
.
red
,
gravity:
ToastGravity
.
TOP
,
);
_isFirstTimeOfCheck
=
false
;
}
}
...
...
lib/config/locator.dart
View file @
990951f
...
...
@@ -34,6 +34,7 @@ import 'package:appframe/data/repositories/message/video_info_handler.dart';
import
'package:appframe/data/repositories/message/wifi_info_handler.dart'
;
import
'package:appframe/data/repositories/message/window_info_handler.dart'
;
import
'package:appframe/data/repositories/phone_auth_repository.dart'
;
import
'package:appframe/data/repositories/user_auth_repository.dart'
;
import
'package:appframe/data/repositories/wechat_auth_repository.dart'
;
import
'package:appframe/services/api_service.dart'
;
import
'package:appframe/services/dispatcher.dart'
;
...
...
@@ -219,4 +220,5 @@ Future<void> setupLocator() async {
/// repository
getIt
.
registerLazySingleton
<
WechatAuthRepository
>(()
=>
WechatAuthRepository
());
getIt
.
registerLazySingleton
<
PhoneAuthRepository
>(()
=>
PhoneAuthRepository
());
getIt
.
registerLazySingleton
<
UserAuthRepository
>(()
=>
UserAuthRepository
());
}
lib/data/repositories/phone_auth_repository.dart
View file @
990951f
...
...
@@ -112,23 +112,4 @@ class PhoneAuthRepository {
);
return
resp
.
data
;
}
///
/// {
/// "code": 0,
/// "error": "操作成功"
/// }
///
Future
<
dynamic
>
updateUser
(
String
userid
,
String
name
,
String
nickName
,
String
avatar
)
async
{
Response
resp
=
await
_appService
.
post
(
'/api/v1/comm/user/update'
,
{
"userid"
:
userid
,
"name"
:
name
,
"nickName"
:
nickName
,
"avatar"
:
avatar
,
},
);
return
resp
.
data
;
}
}
lib/data/repositories/user_auth_repository.dart
0 → 100644
View file @
990951f
import
'dart:io'
;
import
'package:appframe/config/locator.dart'
;
import
'package:appframe/services/api_service.dart'
;
import
'package:dio/dio.dart'
;
class
UserAuthRepository
{
late
final
ApiService
_appService
;
UserAuthRepository
()
{
_appService
=
getIt
<
ApiService
>(
instanceName:
'appApiService'
);
}
///
/// {
/// "code": 0,
/// "error": "操作成功"
/// }
///
Future
<
dynamic
>
updateUser
(
String
userid
,
String
name
,
String
nickName
,
String
avatar
)
async
{
Response
resp
=
await
_appService
.
post
(
'/api/v1/comm/user/update'
,
{
"userid"
:
userid
,
"name"
:
name
,
"nickName"
:
nickName
,
"avatar"
:
avatar
,
},
);
return
resp
.
statusCode
==
HttpStatus
.
ok
?
resp
.
data
:
null
;
}
Future
<
dynamic
>
appleLogin
(
String
userid
,
String
authorizationCode
,
String
identityToken
)
async
{
Response
resp
=
await
_appService
.
post
(
'/api/v1/comm/user/applelogin'
,
{
"user"
:
userid
,
"authorizationCode"
:
authorizationCode
,
"identityToken"
:
identityToken
,
},
);
return
resp
.
statusCode
==
HttpStatus
.
ok
?
resp
.
data
:
null
;
}
///
/// {
/// "code": 1,
/// "data":"bxe userid" // 存在会返回
/// "error": "",
/// }
Future
<
dynamic
>
exchangeId
(
String
userid
)
async
{
Response
resp
=
await
_appService
.
post
(
'/api/v1/comm/user/exchangeid'
,
{
"userId"
:
userid
,
"type"
:
"apple"
,
},
);
return
resp
.
statusCode
==
HttpStatus
.
ok
?
resp
.
data
:
null
;
}
///
/// {
/// "error": "",
/// "code": 0,
/// }
Future
<
dynamic
>
newBinding
(
String
userid
,
String
bxeUserId
)
async
{
Response
resp
=
await
_appService
.
post
(
'/api/v1/comm/user/newbinding'
,
{
"userId"
:
userid
,
"bxeUserId"
:
userid
,
"type"
:
"apple"
,
},
);
return
resp
.
statusCode
==
HttpStatus
.
ok
?
resp
.
data
:
null
;
}
}
lib/data/repositories/wechat_auth_repository.dart
View file @
990951f
import
'dart:io'
;
import
'package:appframe/config/locator.dart'
;
import
'package:appframe/services/api_service.dart'
;
import
'package:dio/dio.dart'
;
...
...
@@ -21,7 +23,7 @@ class WechatAuthRepository {
debugPrint
(
'登录结果:
$resp
'
);
return
resp
.
statusCode
==
200
?
resp
.
data
:
null
;
return
resp
.
statusCode
==
HttpStatus
.
ok
?
resp
.
data
:
null
;
}
Future
<
dynamic
>
getTicket
()
async
{
...
...
@@ -35,6 +37,6 @@ class WechatAuthRepository {
debugPrint
(
'获取ticket:
$resp
'
);
return
resp
.
statusCode
==
200
?
resp
.
data
:
null
;
return
resp
.
statusCode
==
HttpStatus
.
ok
?
resp
.
data
:
null
;
}
}
lib/ui/pages/login_main_page.dart
View file @
990951f
import
'dart:io'
;
import
'package:appframe/bloc/login_main_cubit.dart'
;
import
'package:appframe/config/locator.dart'
;
import
'package:appframe/config/routes.dart'
;
import
'package:appframe/ui/widgets/login/login_page_agreed_widget.dart'
;
import
'package:appframe/ui/widgets/tip_overlay_widget.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:fluwx/fluwx.dart'
;
import
'package:sign_in_with_apple/sign_in_with_apple.dart'
;
class
LoginMainPage
extends
StatelessWidget
{
const
LoginMainPage
({
super
.
key
});
...
...
@@ -18,100 +20,121 @@ class LoginMainPage extends StatelessWidget {
child:
BlocConsumer
<
LoginMainCubit
,
LoginMainState
>(
builder:
(
context
,
state
)
{
var
loginMainCubit
=
context
.
read
<
LoginMainCubit
>();
return
Stack
(
children:
[
Scaffold
(
backgroundColor:
Colors
.
white
,
body:
SafeArea
(
top:
false
,
child:
SingleChildScrollView
(
child:
Column
(
children:
[
SizedBox
(
height:
100
),
Center
(
child:
Image
.
asset
(
'assets/images/login_v2/banner_1.png'
,
var
appleLoginBtn
=
Platform
.
isIOS
?
[
Padding
(
padding:
EdgeInsets
.
symmetric
(
horizontal:
42.5
),
child:
SizedBox
(
width:
double
.
infinity
,
height:
50
,
child:
SignInWithAppleButton
(
text:
'通过Apple登录'
,
borderRadius:
BorderRadius
.
circular
(
27
),
onPressed:
()
{
loginMainCubit
.
appleAuth
();
},
),
),
),
SizedBox
(
height:
15
),
]
:
[];
var
scaffold
=
Scaffold
(
backgroundColor:
Colors
.
white
,
body:
SafeArea
(
top:
false
,
child:
SingleChildScrollView
(
child:
Column
(
children:
[
SizedBox
(
height:
100
),
Center
(
child:
Image
.
asset
(
'assets/images/login_v2/banner_1.png'
,
),
),
SizedBox
(
height:
30
),
Center
(
child:
Image
.
asset
(
'assets/images/login_v2/main.png'
,
),
),
SizedBox
(
height:
40
),
...
appleLoginBtn
,
Padding
(
padding:
EdgeInsets
.
symmetric
(
horizontal:
42.5
),
child:
SizedBox
(
width:
double
.
infinity
,
height:
50
,
child:
ElevatedButton
(
onPressed:
()
async
{
if
(
await
getIt
.
get
<
Fluwx
>().
isWeChatInstalled
)
{
loginMainCubit
.
wechatAuth
();
}
else
{
if
(!
context
.
mounted
)
return
;
_showWechatNotInstallDialog
(
context
);
}
},
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
Color
(
0xFF26C445
),
foregroundColor:
Colors
.
white
,
textStyle:
TextStyle
(
fontSize:
19
),
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
27
),
),
),
),
SizedBox
(
height:
30
),
Center
(
child:
Image
.
asset
(
'assets/images/login_v2/main.png'
,
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
Image
.
asset
(
'assets/images/login_v2/wechat_white_icon.png'
),
SizedBox
(
width:
4.5
),
Text
(
'微信登录'
),
],
),
),
SizedBox
(
height:
40
),
Padding
(
padding:
EdgeInsets
.
symmetric
(
horizontal:
42.5
),
child:
SizedBox
(
width:
double
.
infinity
,
height:
50
,
child:
ElevatedButton
(
onPressed:
()
async
{
if
(
await
getIt
.
get
<
Fluwx
>().
isWeChatInstalled
)
{
loginMainCubit
.
wechatAuth
();
}
else
{
if
(!
context
.
mounted
)
return
;
_showWechatNotInstallDialog
(
context
);
}
},
style:
ElevatedButton
.
styleFrom
(
backgroundColor:
Color
(
0xFF26C445
),
foregroundColor:
Colors
.
white
,
textStyle:
TextStyle
(
fontSize:
19
),
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
27
),
),
),
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
Image
.
asset
(
'assets/images/login_v2/wechat_white_icon.png'
),
SizedBox
(
width:
4.5
),
Text
(
'微信登录'
),
],
),
),
),
),
SizedBox
(
height:
15
),
_buildAgreement
(
context
,
loginMainCubit
,
state
.
agreed
),
SizedBox
(
height:
82.5
),
Text
(
'其他方式登录'
,
style:
TextStyle
(
fontSize:
14
,
color:
Color
(
0xFF999999
),
),
strutStyle:
StrutStyle
(
height:
16
/
14
),
),
SizedBox
(
height:
15
),
Row
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
InkWell
(
onTap:
()
{
context
.
read
<
LoginMainCubit
>().
goLoginQr
();
},
child:
Image
.
asset
(
'assets/images/login_v2/qr_green_icon.png'
,
),
),
SizedBox
(
height:
15
),
_buildAgreement
(
context
,
loginMainCubit
,
state
.
agreed
),
SizedBox
(
height:
82.5
),
Text
(
'其他方式登录'
,
style:
TextStyle
(
fontSize:
14
,
color:
Color
(
0xFF999999
),
SizedBox
(
width:
25
),
InkWell
(
onTap:
()
{
context
.
read
<
LoginMainCubit
>().
goLoginPhone
();
},
child:
Image
.
asset
(
'assets/images/login_v2/phone_blue_icon.png'
,
),
strutStyle:
StrutStyle
(
height:
16
/
14
),
),
SizedBox
(
height:
15
),
Row
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
[
InkWell
(
onTap:
()
{
context
.
read
<
LoginMainCubit
>().
goLoginQr
();
},
child:
Image
.
asset
(
'assets/images/login_v2/qr_green_icon.png'
,
),
),
SizedBox
(
width:
25
),
InkWell
(
onTap:
()
{
context
.
read
<
LoginMainCubit
>().
goLoginPhone
();
},
child:
Image
.
asset
(
'assets/images/login_v2/phone_blue_icon.png'
,
),
),
],
),
],
),
)
,
]
,
),
),
),
);
return
Stack
(
children:
[
scaffold
,
state
.
loading
?
Container
(
color:
Colors
.
black54
,
...
...
@@ -134,8 +157,8 @@ class LoginMainPage extends StatelessWidget {
listener:
(
context
,
state
)
{
if
(
state
.
showAgreed
)
{
_showAgreementDialog
(
context
,
context
.
read
<
LoginMainCubit
>());
}
else
if
(
state
.
show
SnackBar
)
{
TipOverlayUtil
.
showTip
(
context
,
state
.
snackBarMsg
);
}
else
if
(
state
.
show
NeedWechatForApple
)
{
_showNeedWechatDialogForApple
(
context
,
context
.
read
<
LoginMainCubit
>()
);
}
},
),
...
...
@@ -352,4 +375,58 @@ class LoginMainPage extends StatelessWidget {
},
);
}
void
_showNeedWechatDialogForApple
(
BuildContext
context
,
LoginMainCubit
loginMainCubit
)
{
showDialog
(
context:
context
,
barrierDismissible:
false
,
builder:
(
BuildContext
ctx
)
{
return
PopScope
(
canPop:
false
,
child:
AlertDialog
(
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
5
),
),
),
title:
Text
(
'温馨提示'
,
style:
TextStyle
(
fontSize:
17
,
color:
Color
(
0xFF000000
),
// fontWeight: FontWeight.bold,
),
textAlign:
TextAlign
.
center
,
),
content:
Text
.
rich
(
TextSpan
(
text:
'为了避免您之前在微信小程序的使用数据不丢失,必须绑定微信才可以继续!'
,
style:
TextStyle
(
color:
Color
(
0xFF666666
),
fontSize:
14
),
),
),
actions:
[
Center
(
child:
TextButton
(
onPressed:
()
{
Navigator
.
of
(
ctx
).
pop
(
'OK'
);
loginMainCubit
.
wechatAuthForApple
();
},
style:
TextButton
.
styleFrom
(
foregroundColor:
Color
(
0xFF7691FA
),
textStyle:
TextStyle
(
fontSize:
17
),
minimumSize:
Size
.
fromHeight
(
40
),
padding:
EdgeInsets
.
zero
,
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
zero
,
),
),
child:
Text
(
'绑定微信'
),
),
),
],
),
);
},
);
}
}
lib/ui/pages/login_phone_page.dart
View file @
990951f
import
'package:appframe/bloc/login_phone_cubit.dart'
;
import
'package:appframe/config/routes.dart'
;
import
'package:appframe/ui/widgets/login/login_page_agreed_widget.dart'
;
import
'package:appframe/ui/widgets/tip_overlay_widget.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/services.dart'
;
...
...
@@ -81,8 +80,6 @@ class LoginPhonePage extends StatelessWidget {
listener:
(
context
,
state
)
{
if
(
state
.
showAgreed
)
{
_showAgreementDialog
(
context
,
context
.
read
<
LoginPhoneCubit
>());
}
else
if
(
state
.
showSnackBar
)
{
TipOverlayUtil
.
showTip
(
context
,
state
.
snackBarMsg
);
}
},
),
...
...
lib/ui/pages/login_qr_page.dart
View file @
990951f
import
'package:appframe/bloc/login_qr_cubit.dart'
;
import
'package:appframe/ui/widgets/tip_overlay_widget.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
...
...
@@ -61,11 +60,7 @@ class LoginQrPage extends StatelessWidget {
),
);
},
listener:
(
context
,
state
)
{
if
(
state
.
showSnackBar
)
{
TipOverlayUtil
.
showTip
(
context
,
state
.
snackBarMsg
);
}
},
listener:
(
context
,
state
)
{},
));
}
...
...
lib/ui/pages/web_page.dart
View file @
990951f
...
...
@@ -151,8 +151,6 @@ class WebPage extends StatelessWidget {
context
.
read
<
WebCubit
>().
chooseImage
(
context
);
}
else
if
(
state
.
chooseVideoCmdFlag
)
{
context
.
read
<
WebCubit
>().
chooseVideo
(
context
);
}
else
if
(
state
.
showSnackBar
)
{
TipOverlayUtil
.
showTip
(
context
,
state
.
snackBarMsg
);
}
},
),
...
...
Write
Preview
Styling with
Markdown
is supported
Attach a file
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to post a comment