Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Hoàng Văn Đạt
mypoint_flutter_app
Commits
e8a305af
Commit
e8a305af
authored
Apr 17, 2025
by
DatHV
Browse files
update auth logic
parent
5d865668
Changes
36
Show whitespace changes
Inline
Side-by-side
lib/screen/create_pass/signup_create_password_repository.dart
View file @
e8a305af
...
...
@@ -5,6 +5,10 @@ import 'package:mypoint_flutter_app/networking/restful_api_request.dart';
import
'package:mypoint_flutter_app/screen/login/login_screen.dart'
;
import
'../../base/base_response_model.dart'
;
import
'../../base/restful_api_viewmodel.dart'
;
import
'../../permission/biometric_manager.dart'
;
import
'../../preference/data_preference.dart'
;
import
'../biometric/biometric_screen.dart'
;
import
'../main_tab_screen/main_tab_screen.dart'
;
import
'../splash/splash_screen_viewmodel.dart'
;
abstract
class
ICreatePasswordRepository
{
...
...
@@ -15,8 +19,8 @@ abstract class ICreatePasswordRepository {
class
SignUpCreatePasswordRepository
extends
RestfulApiViewModel
implements
ICreatePasswordRepository
{
@override
late
String
phoneNumber
;
SignUpCreatePasswordRepository
(
this
.
phoneNumber
);
final
BiometricManager
_biometricManager
=
BiometricManager
();
@override
Future
<
BaseResponseModel
<
EmptyCodable
>>
setPassword
(
String
password
)
async
{
...
...
@@ -25,9 +29,41 @@ class SignUpCreatePasswordRepository extends RestfulApiViewModel implements ICre
hideLoading
();
if
(
value
.
status
==
"success"
||
value
.
code
==
200
)
{
print
(
"signup success"
);
Get
.
off
(()
=>
LoginScreen
(
phoneNumber:
phoneNumber
)
);
_autoLogin
(
password
);
}
return
value
;
});
}
void
_autoLogin
(
String
password
)
{
showLoading
();
client
.
login
(
phoneNumber
,
password
).
then
((
response
)
async
{
hideLoading
();
if
(
response
.
isSuccess
&&
response
.
data
!=
null
)
{
await
DataPreference
.
instance
.
saveLoginToken
(
response
.
data
!);
_getUserProfile
();
}
else
{
Get
.
off
(()
=>
LoginScreen
(
phoneNumber:
phoneNumber
));
}
});
}
void
_getUserProfile
()
{
showLoading
();
client
.
getUserProfile
().
then
((
value
)
async
{
hideLoading
();
final
userProfile
=
value
.
data
;
if
(
value
.
isSuccess
&&
userProfile
!=
null
)
{
await
DataPreference
.
instance
.
saveUserProfile
(
userProfile
);
if
(
await
_biometricManager
.
canCheckBiometrics
())
{
Get
.
to
(
BiometricAuthScreen
());
}
else
{
Get
.
to
(
MainTabScreen
());
}
}
else
{
DataPreference
.
instance
.
clearLoginToken
();
Get
.
off
(()
=>
LoginScreen
(
phoneNumber:
phoneNumber
));
}
});
}
}
lib/screen/game/games_screen.dart
0 → 100644
View file @
e8a305af
import
'package:flutter/material.dart'
;
class
GameScreen
extends
StatelessWidget
{
const
GameScreen
({
super
.
key
});
@override
Widget
build
(
BuildContext
context
)
{
return
const
Center
(
child:
Text
(
'Games'
));
}
}
\ No newline at end of file
lib/screen/home/home_screen.dart
0 → 100644
View file @
e8a305af
// home_screen.dart
import
'package:flutter/material.dart'
;
import
'package:get/get.dart'
;
import
'package:mypoint_flutter_app/preference/data_preference.dart'
;
class
HomeScreen
extends
StatelessWidget
{
const
HomeScreen
({
super
.
key
});
void
_logout
(
BuildContext
context
)
async
{
final
confirm
=
await
showDialog
<
bool
>(
context:
context
,
builder:
(
ctx
)
=>
AlertDialog
(
title:
const
Text
(
'Xác nhận'
),
content:
const
Text
(
'Bạn có chắc muốn đăng xuất?'
),
actions:
[
TextButton
(
onPressed:
()
=>
Navigator
.
of
(
ctx
).
pop
(
false
),
child:
const
Text
(
'Hủy'
)),
TextButton
(
onPressed:
()
=>
Navigator
.
of
(
ctx
).
pop
(
true
),
child:
const
Text
(
'Đăng xuất'
)),
],
),
);
if
(
confirm
==
true
)
{
DataPreference
.
instance
.
clearLoginToken
();
Get
.
offAllNamed
(
'/onboarding'
);
}
}
@override
Widget
build
(
BuildContext
context
)
{
return
Center
(
child:
ElevatedButton
(
onPressed:
()
=>
_logout
(
context
),
child:
const
Text
(
'Đăng xuất'
),
),
);
}
}
\ No newline at end of file
lib/screen/login/login_screen.dart
View file @
e8a305af
// login_screen.dart
import
'dart:async'
;
import
'package:flutter/material.dart'
;
import
'package:get/get.dart'
;
import
'../../base/base_screen.dart'
;
import
'../../base/basic_state.dart'
;
import
'../../permission/biometric_manager.dart'
;
import
'../../resouce/base_color.dart'
;
import
'../../widgets/alert/custom_alert_dialog.dart'
;
import
'../../widgets/alert/data_alert_model.dart'
;
import
'../../widgets/back_button.dart'
;
import
'../../widgets/support_button.dart'
;
import
'login_viewmodel.dart'
;
...
...
@@ -21,10 +24,76 @@ class LoginScreen extends BaseScreen {
class
_LoginScreenState
extends
BaseState
<
LoginScreen
>
with
BasicState
{
final
TextEditingController
_phoneController
=
TextEditingController
();
final
FocusNode
_focusNode
=
FocusNode
();
final
loginVM
=
Get
.
put
(
LoginViewModel
());
@override
void
initState
()
{
super
.
initState
();
loginVM
.
onShowChangePass
=
(
message
)
{
Get
.
dialog
(
CustomAlertDialog
(
alertData:
DataAlertModel
(
background:
"assets/images/bg_alert_header.png"
,
title:
"Cài đặt mật khẩu"
,
content:
message
,
buttons:
[
AlertButton
(
text:
"Cài đặt ngay"
,
onPressed:
()
{
},
bgColor:
BaseColor
.
primary500
,
textColor:
Colors
.
white
,
isPrimary:
true
,
),
],
),
),
);
};
loginVM
.
onShowDeviceError
=
(
message
)
{
loginVM
.
onChangePhonePressed
();
};
loginVM
.
onShowInvalidAccount
=
(
message
)
{
Get
.
dialog
(
CustomAlertDialog
(
alertData:
DataAlertModel
(
background:
"assets/images/bg_alert_header.png"
,
title:
""
,
content:
message
,
buttons:
[
AlertButton
(
text:
"Quên mật khẩu"
,
onPressed:
()
{
loginVM
.
onForgotPassPressed
(
widget
.
phoneNumber
);
},
bgColor:
BaseColor
.
primary500
,
textColor:
Colors
.
white
,
isPrimary:
true
,
),
AlertButton
(
text:
"Đã hiểu"
,
onPressed:
()
{
Get
.
back
();
},
bgColor:
Colors
.
white
,
textColor:
Colors
.
black
,
isPrimary:
true
,
),
],
),
),
);
};
loginVM
.
onShowAlertError
=
(
message
)
{
if
(
message
.
isNotEmpty
)
{
showAlertError
(
message
);
}
};
WidgetsBinding
.
instance
.
addPostFrameCallback
((
_
)
{
_focusNode
.
requestFocus
();
});
...
...
@@ -39,7 +108,7 @@ class _LoginScreenState extends BaseState<LoginScreen> with BasicState {
@override
Widget
createBody
()
{
final
loginVM
=
Get
.
put
(
LoginViewModel
(
)
);
//
final loginVM = Get.
find<
LoginViewModel
>
();
return
GestureDetector
(
onTap:
hideKeyboard
,
child:
Scaffold
(
...
...
@@ -185,7 +254,10 @@ class _LoginScreenState extends BaseState<LoginScreen> with BasicState {
}
return
Column
(
children:
[
IconButton
(
icon:
Icon
(
icon
,
size:
36
),
onPressed:
()
=>
vm
.
onBiometricLoginPressed
(
Get
.
context
!)),
IconButton
(
icon:
Icon
(
icon
,
size:
36
),
onPressed:
()
=>
vm
.
onBiometricLoginPressed
(
widget
.
phoneNumber
),
),
Text
(
"Đăng nhập bằng
$label
"
),
],
);
...
...
@@ -225,7 +297,9 @@ class _LoginScreenState extends BaseState<LoginScreen> with BasicState {
minimumSize:
const
Size
.
fromHeight
(
48
),
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
8
)),
),
onPressed:
enabled
?
vm
.
onLoginPressed
:
null
,
onPressed:
()
{
enabled
?
vm
.
onLoginPressed
(
widget
.
phoneNumber
)
:
null
;
},
child:
const
Text
(
"Đăng nhập"
,
style:
TextStyle
(
fontSize:
16
,
fontWeight:
FontWeight
.
bold
,
color:
Colors
.
white
),
...
...
lib/screen/login/login_viewmodel.dart
View file @
e8a305af
import
'dart:convert'
;
import
'package:get/get.dart'
;
import
'package:flutter/material.dart'
;
import
'package:mypoint_flutter_app/configs/constants.dart'
;
import
'package:mypoint_flutter_app/networking/restful_api_request.dart'
;
import
'package:mypoint_flutter_app/screen/onboarding/onboarding_screen.dart'
;
import
'package:mypoint_flutter_app/screen/otp/forgot_pass_otp_repository.dart'
;
import
'package:mypoint_flutter_app/screen/otp/otp_screen.dart'
;
import
'../../base/base_response_model.dart'
;
import
'../../base/restful_api_viewmodel.dart'
;
import
'../../model/auth/login_token_response_model.dart'
;
import
'../../permission/biometric_manager.dart'
;
import
'../../preference/data_preference.dart'
;
import
'../main_tab_screen/main_tab_screen.dart'
;
// login_state_enum.dart
enum
LoginState
{
idle
,
typing
,
done
,
error
}
...
...
@@ -19,6 +22,11 @@ class LoginViewModel extends RestfulApiViewModel {
var
password
=
""
.
obs
;
var
biometricType
=
BiometricTypeEnum
.
none
.
obs
;
void
Function
(
String
message
)?
onShowAlertError
;
void
Function
(
String
message
)?
onShowDeviceError
;
void
Function
(
String
message
)?
onShowChangePass
;
void
Function
(
String
message
)?
onShowInvalidAccount
;
@override
void
onInit
()
{
super
.
onInit
();
...
...
@@ -47,65 +55,88 @@ class LoginViewModel extends RestfulApiViewModel {
isPasswordVisible
.
value
=
!
isPasswordVisible
.
value
;
}
void
onLoginPressed
()
{
void
onLoginPressed
(
String
phone
)
{
if
(
password
.
value
.
isEmpty
)
return
;
// Ví dụ: Mật khẩu chuẩn là "123456"
if
(
password
.
value
==
"123456"
)
{
loginState
.
value
=
LoginState
.
done
;
debugPrint
(
"Đăng nhập thành công!"
);
// TODO: Chuyển màn hình
showLoading
();
client
.
login
(
phone
,
password
.
value
).
then
((
value
)
async
{
hideLoading
();
_handleLoginResponse
(
value
,
phone
);
});
}
void
_getUserProfile
()
{
showLoading
();
client
.
getUserProfile
().
then
((
value
)
async
{
hideLoading
();
final
userProfile
=
value
.
data
;
if
(
value
.
isSuccess
&&
userProfile
!=
null
)
{
await
DataPreference
.
instance
.
saveUserProfile
(
userProfile
);
Get
.
to
(
MainTabScreen
());
}
else
{
loginState
.
value
=
LoginState
.
error
;
debugPrint
(
"Sai mật khẩu!"
);
DataPreference
.
instance
.
clearLoginToken
();
final
mgs
=
value
.
errorMessage
??
Constants
.
commonError
;
onShowAlertError
?.
call
(
mgs
);
}
});
}
void
onChangePhonePressed
()
{
Get
.
back
();
}
void
onForgotPassPressed
(
String
phone
Number
)
{
void
onForgotPassPressed
(
String
phone
)
{
showLoading
();
client
.
otpCreateNew
(
phone
Number
).
then
((
value
)
{
client
.
otpCreateNew
(
phone
).
then
((
value
)
{
hideLoading
();
// TODO: handle error later
if
(
value
.
isSuccess
)
{
Get
.
to
(
OtpScreen
(
repository:
ForgotPassOTPRepository
(
phone
Number
,
value
.
data
?.
resendAfterSecond
??
Constants
.
otpTtl
),
repository:
ForgotPassOTPRepository
(
phone
,
value
.
data
?.
resendAfterSecond
??
Constants
.
otpTtl
),
),
);
}
});
}
Future
<
void
>
_handleLoginResponse
(
BaseResponseModel
<
LoginTokenResponseModel
>
response
,
String
phone
)
async
{
if
(
response
.
isSuccess
&&
response
.
data
!=
null
)
{
await
DataPreference
.
instance
.
saveLoginToken
(
response
.
data
!);
_getUserProfile
();
return
;
}
final
errorMsg
=
response
.
errorMessage
??
Constants
.
commonError
;
final
errorCode
=
response
.
errorCode
;
if
(
errorCode
==
ErrorCodes
.
deviceUndefined
)
{
onShowDeviceError
?.
call
(
errorMsg
);
}
else
if
(
errorCode
==
ErrorCodes
.
requiredChangePass
)
{
onShowChangePass
?.
call
(
errorMsg
);
}
else
if
(
errorCode
==
ErrorCodes
.
invalidAccount
)
{
onShowInvalidAccount
?.
call
(
errorMsg
);
}
else
{
if
(
errorCode
==
ErrorCodes
.
bioTokenInvalid
)
{
DataPreference
.
instance
.
clearBioToken
(
phone
);
}
onShowAlertError
?.
call
(
errorMsg
);
}
}
/// Xác thực đăng nhập bằng sinh trắc
Future
<
void
>
onBiometricLoginPressed
(
BuildContext
context
)
async
{
// Kiểm tra thiết bị hỗ trợ
Future
<
void
>
onBiometricLoginPressed
(
String
phone
)
async
{
final
canUse
=
await
canUseBiometrics
();
if
(!
canUse
||
biometricType
.
value
==
BiometricTypeEnum
.
none
)
{
Get
.
snackbar
(
"Thông báo"
,
"Thiết bị không hỗ trợ sinh trắc học"
,
snackPosition:
SnackPosition
.
BOTTOM
);
onShowAlertError
?.
call
(
"Thiết bị không hỗ trợ sinh trắc học"
);
return
;
}
// Tuỳ chọn: hiển thị dialog xác nhận trước khi gọi authenticate
final
success
=
await
_biometricManager
.
showCustomBiometricDialog
(
context
,
title:
"Xác thực sinh trắc học"
,
content:
(
biometricType
.
value
==
BiometricTypeEnum
.
faceId
)
?
"Bạn có muốn đăng nhập bằng Face ID không?"
:
"Bạn có muốn đăng nhập bằng vân tay không?"
,
confirmText:
"Đồng ý"
,
cancelText:
"Huỷ"
,
);
if
(
success
)
{
loginState
.
value
=
LoginState
.
done
;
debugPrint
(
"Đăng nhập bằng sinh trắc thành công!"
);
// TODO: Chuyển màn hình
}
else
{
debugPrint
(
"Xác thực thất bại hoặc người dùng huỷ."
);
final
bioToken
=
await
DataPreference
.
instance
.
getBioToken
(
phone
);
if
(
bioToken
==
null
)
{
onShowAlertError
?.
call
(
"Tài khoản này chưa kích hoạt đăng nhập bằng sinh trắc học!
\n
Vui lòng đăng nhập > cài đặt để kích hoạt tính năng"
);
return
;
}
client
.
login
(
phone
,
password
.
value
).
then
((
value
)
async
{
hideLoading
();
_handleLoginResponse
(
value
,
phone
);
});
}
}
lib/screen/main_tab_screen/main_tab_screen.dart
0 → 100644
View file @
e8a305af
import
'package:flutter/material.dart'
;
import
'../game/games_screen.dart'
;
import
'../home/home_screen.dart'
;
import
'../personal/personal_screen.dart'
;
import
'../shopping/shopping_screen.dart'
;
import
'../voucher/voucher_screen.dart'
;
class
MainTabScreen
extends
StatefulWidget
{
const
MainTabScreen
({
super
.
key
});
@override
State
<
MainTabScreen
>
createState
()
=>
_MainTabScreenState
();
}
class
_MainTabScreenState
extends
State
<
MainTabScreen
>
{
int
_currentIndex
=
0
;
final
List
<
Widget
>
_pages
=
const
[
HomeScreen
(),
VoucherScreen
(),
GameScreen
(),
ShoppingScreen
(),
PersonalScreen
(),
];
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
body:
_pages
[
_currentIndex
],
bottomNavigationBar:
Container
(
decoration:
const
BoxDecoration
(
color:
Colors
.
red
,
),
padding:
const
EdgeInsets
.
symmetric
(
vertical:
10
,
horizontal:
16
),
child:
SafeArea
(
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceAround
,
children:
[
_buildTabItem
(
icon:
Icons
.
home
,
label:
'Trang chủ'
,
index:
0
),
_buildTabItem
(
icon:
Icons
.
star
,
label:
'Ưu đãi'
,
index:
1
),
_buildTabItem
(
icon:
Icons
.
videogame_asset
,
label:
'Game'
,
index:
2
),
_buildTabItem
(
icon:
Icons
.
shopping_cart
,
label:
'Mua sắm'
,
index:
3
),
_buildTabItem
(
icon:
Icons
.
person
,
label:
'Cá nhân'
,
index:
4
),
],
),
),
),
);
}
Widget
_buildTabItem
({
required
IconData
icon
,
required
String
label
,
required
int
index
})
{
final
isSelected
=
_currentIndex
==
index
;
return
GestureDetector
(
onTap:
()
=>
setState
(()
=>
_currentIndex
=
index
),
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
children:
[
Icon
(
icon
,
color:
Colors
.
white
.
withOpacity
(
isSelected
?
1
:
0.6
)),
const
SizedBox
(
height:
4
),
Text
(
label
,
style:
TextStyle
(
color:
Colors
.
white
.
withOpacity
(
isSelected
?
1
:
0.6
),
fontSize:
12
,
)),
],
),
);
}
}
lib/screen/onboarding/onboarding_screen.dart
View file @
e8a305af
...
...
@@ -7,6 +7,7 @@ import '../../base/base_screen.dart';
import
'../../base/basic_state.dart'
;
import
'../../configs/constants.dart'
;
import
'../../resouce/base_color.dart'
;
import
'../biometric/biometric_screen.dart'
;
import
'../faqs/faqs_screen.dart'
;
import
'../login/login_screen.dart'
;
import
'../otp/otp_screen.dart'
;
...
...
@@ -37,7 +38,6 @@ class _OnboardingScreenState extends BaseState<OnboardingScreen> with BasicState
_handleResponseCheckPhoneNumber
(
response
.
data
);
_handleResponseError
(
response
);
});
//
});
}
...
...
@@ -50,8 +50,7 @@ class _OnboardingScreenState extends BaseState<OnboardingScreen> with BasicState
if
(
errorCode
==
ErrorCodes
.
deviceLock
)
{
// show alert error popupErrorCSKH
return
;
}
//show alert error popupError
}
//show alert error popupError
});
}
...
...
lib/screen/personal/personal_screen.dart
0 → 100644
View file @
e8a305af
import
'package:flutter/material.dart'
;
class
PersonalScreen
extends
StatelessWidget
{
const
PersonalScreen
({
super
.
key
});
@override
Widget
build
(
BuildContext
context
)
{
return
const
Center
(
child:
Text
(
'Cá nhân'
));
}
}
\ No newline at end of file
lib/screen/shopping/shopping_screen.dart
0 → 100644
View file @
e8a305af
import
'package:flutter/material.dart'
;
class
ShoppingScreen
extends
StatelessWidget
{
const
ShoppingScreen
({
super
.
key
});
@override
Widget
build
(
BuildContext
context
)
{
return
const
Center
(
child:
Text
(
'Mua sắm'
));
}
}
\ No newline at end of file
lib/screen/splash/splash_screen.dart
View file @
e8a305af
...
...
@@ -2,7 +2,6 @@ import 'dart:io';
import
'package:flutter/material.dart'
;
import
'package:flutter/services.dart'
;
import
'package:get/get.dart'
;
import
'package:get/get_core/src/get_main.dart'
;
import
'package:mypoint_flutter_app/configs/api_paths.dart'
;
import
'package:mypoint_flutter_app/dio_http_service/api_helper.dart'
;
import
'package:mypoint_flutter_app/networking/api_service.dart'
;
...
...
lib/screen/voucher/voucher_screen.dart
0 → 100644
View file @
e8a305af
import
'package:flutter/material.dart'
;
class
VoucherScreen
extends
StatelessWidget
{
const
VoucherScreen
({
super
.
key
});
@override
Widget
build
(
BuildContext
context
)
{
return
const
Center
(
child:
Text
(
'Ưu đãi'
));
}
}
\ No newline at end of file
lib/widgets/alert/custom_alert_dialog.dart
View file @
e8a305af
...
...
@@ -36,7 +36,7 @@ class CustomAlertDialog extends StatelessWidget {
// fit: BoxFit.cover,
// ),
),
const
SizedBox
(
height:
10
),
const
SizedBox
(
height:
2
),
// Title
if
(
alertData
.
title
!=
null
)
Text
(
...
...
@@ -97,7 +97,7 @@ class CustomAlertDialog extends StatelessWidget {
onPressed:
btn
.
onPressed
,
child:
Text
(
btn
.
text
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
14
,
fontWeight:
FontWeight
.
bold
),
style:
TextStyle
(
color:
btn
.
textColor
,
fontSize:
14
,
fontWeight:
FontWeight
.
bold
),
),
),
),
...
...
@@ -122,7 +122,7 @@ class CustomAlertDialog extends StatelessWidget {
onPressed:
btn
.
onPressed
,
child:
Text
(
btn
.
text
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
14
,
fontWeight:
FontWeight
.
bold
),
style:
TextStyle
(
color:
btn
.
textColor
,
fontSize:
14
,
fontWeight:
FontWeight
.
bold
),
),
),
),
...
...
lib/widgets/alert/src/alert.dart
deleted
100755 → 0
View file @
5d865668
import
'dart:async'
;
import
'package:flutter/material.dart'
;
import
'package:mypoint_flutter_app/resouce/define_image.dart'
;
import
'alert_style.dart'
;
import
'animation_transition.dart'
;
import
'dialog_button.dart'
;
class
Alert
{
final
String
?
id
;
final
BuildContext
context
;
final
AlertType
?
type
;
final
AlertStyle
style
;
final
EdgeInsets
?
padding
;
final
Widget
?
image
;
final
String
?
urlImage
;
final
String
?
title
;
final
String
?
desc
;
final
Widget
content
;
final
List
<
DialogButton
>?
buttons
;
final
Function
?
closeFunction
;
final
Widget
?
closeIcon
;
final
bool
onWillPopActive
;
final
bool
useRootNavigator
;
final
AlertAnimation
?
alertAnimation
;
Alert
({
required
this
.
context
,
this
.
id
,
this
.
type
,
this
.
style
=
const
AlertStyle
(),
this
.
padding
,
this
.
image
,
this
.
urlImage
,
this
.
title
,
this
.
desc
,
this
.
content
=
const
SizedBox
(),
this
.
buttons
,
this
.
closeFunction
,
this
.
closeIcon
,
this
.
onWillPopActive
=
false
,
this
.
alertAnimation
,
this
.
useRootNavigator
=
true
,
});
/// Displays defined alert window
Future
<
bool
?>
show
()
async
{
return
await
showGeneralDialog
(
context:
context
,
pageBuilder:
(
BuildContext
buildContext
,
Animation
<
double
>
animation
,
Animation
<
double
>
secondaryAnimation
)
{
return
_buildDialog
();
},
barrierDismissible:
style
.
isOverlayTapDismiss
,
barrierLabel:
MaterialLocalizations
.
of
(
context
).
modalBarrierDismissLabel
,
barrierColor:
style
.
overlayColor
,
useRootNavigator:
useRootNavigator
,
transitionDuration:
style
.
animationDuration
,
transitionBuilder:
(
BuildContext
context
,
Animation
<
double
>
animation
,
Animation
<
double
>
secondaryAnimation
,
Widget
child
,
)
=>
alertAnimation
==
null
?
_showAnimation
(
animation
,
secondaryAnimation
,
child
)
:
alertAnimation
!(
context
,
animation
,
secondaryAnimation
,
child
));
}
/// Dismisses the alert dialog.
Future
<
void
>
dismiss
()
async
{
Navigator
.
of
(
context
,
rootNavigator:
useRootNavigator
).
pop
();
}
/// Alert dialog content widget
Widget
_buildDialog
()
{
final
Widget
child
=
Align
(
alignment:
style
.
alertAlignment
,
child:
ConstrainedBox
(
constraints:
style
.
constraints
??
BoxConstraints
.
loose
(
Size
.
infinite
),
child:
SingleChildScrollView
(
child:
AlertDialog
(
key:
id
==
null
?
null
:
Key
(
id
!),
backgroundColor:
style
.
backgroundColor
??
Theme
.
of
(
context
).
dialogTheme
.
backgroundColor
,
shape:
style
.
alertBorder
??
_defaultShape
(),
insetPadding:
style
.
alertPadding
,
elevation:
style
.
alertElevation
,
titlePadding:
const
EdgeInsets
.
all
(
0.0
),
title:
Container
(
child:
Center
(
child:
Column
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
<
Widget
>[
_getCloseButton
(),
Padding
(
padding:
padding
??
EdgeInsets
.
fromLTRB
(
20
,
(
style
.
isCloseButton
?
0
:
10
),
20
,
0
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
stretch
,
children:
<
Widget
>[
Center
(
child:
_getImage
(),
),
if
(
title
!=
null
)
_getTextWidget
(
style
.
isTitleSelectable
,
title
,
style
.
titleStyle
,
style
.
titleTextAlign
,
style
.
titlePadding
??
EdgeInsets
.
only
(
top:
15
,
bottom:
desc
==
null
?
0
:
10
,
),
),
if
(
desc
!=
null
)
_getTextWidget
(
style
.
isDescSelectable
,
desc
,
style
.
descStyle
,
style
.
descTextAlign
,
style
.
descPadding
,
),
content
,
],
),
)
],
),
),
),
contentPadding:
style
.
buttonAreaPadding
,
content:
style
.
buttonsDirection
==
ButtonsDirection
.
row
?
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceEvenly
,
children:
_getButtons
(),
)
:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
crossAxisAlignment:
CrossAxisAlignment
.
center
,
mainAxisAlignment:
MainAxisAlignment
.
spaceEvenly
,
children:
_getButtons
(),
)),
),
),
);
return
onWillPopActive
?
PopScope
(
child:
child
)
//WillPopScope(onWillPop: () async => false, child: child)
:
child
;
}
/// Returns Text Widget or Selectable Text Widget with Padding
/// based on isSelectable property.
Widget
_getTextWidget
(
bool
isSelectable
,
String
?
text
,
TextStyle
textStyle
,
TextAlign
textAlign
,
EdgeInsets
edgeInsets
)
{
return
Padding
(
padding:
edgeInsets
,
child:
_getTextWidgetBySelectable
(
isSelectable
,
text
,
textStyle
,
textAlign
,
),
);
}
/// Returns Text Widget or Selectable Text Widget
/// based on isSelectable property.
Widget
_getTextWidgetBySelectable
(
bool
isSelectable
,
String
?
text
,
TextStyle
textStyle
,
TextAlign
textAlign
)
{
if
(
isSelectable
)
{
return
SelectableText
(
text
??
""
,
style:
textStyle
,
textAlign:
textAlign
,
);
}
else
{
return
Text
(
text
??
""
,
style:
textStyle
,
textAlign:
textAlign
,
);
}
}
/// Returns the close button on the top right
Widget
_getCloseButton
()
{
return
style
.
isCloseButton
?
Padding
(
padding:
const
EdgeInsets
.
fromLTRB
(
0
,
10
,
10
,
0
),
child:
GestureDetector
(
onTap:
()
{
if
(
closeFunction
==
null
)
{
Navigator
.
of
(
context
,
rootNavigator:
useRootNavigator
).
pop
();
}
else
{
closeFunction
!();
}
},
child:
Container
(
alignment:
FractionalOffset
.
topRight
,
child:
closeIcon
!=
null
?
Container
(
child:
closeIcon
)
:
Container
(
width:
20
,
height:
20
,
decoration:
BoxDecoration
(
image:
DecorationImage
(
image:
AssetImage
(
icClose
),
),
),
),
),
),
)
:
Container
();
}
/// Returns defined buttons. Default: Cancel Button
List
<
Widget
>
_getButtons
()
{
List
<
Widget
>
expandedButtons
=
[];
if
(
style
.
isButtonVisible
)
{
if
(
buttons
!=
null
)
{
for
(
var
button
in
buttons
!)
{
var
buttonWidget
=
Padding
(
padding:
const
EdgeInsets
.
only
(
left:
2
,
right:
2
),
child:
button
,
);
if
((
button
.
width
!=
null
&&
buttons
!.
length
==
1
)
||
style
.
buttonsDirection
==
ButtonsDirection
.
column
)
{
expandedButtons
.
add
(
buttonWidget
);
}
else
{
expandedButtons
.
add
(
Expanded
(
child:
buttonWidget
,
));
}
}
}
else
{
Widget
cancelButton
=
DialogButton
(
child:
Text
(
"CANCEL"
,
style:
TextStyle
(
color:
Colors
.
white
,
fontSize:
20
),
),
onPressed:
()
=>
Navigator
.
pop
(
context
),
);
if
(
style
.
buttonsDirection
==
ButtonsDirection
.
row
)
{
cancelButton
=
Expanded
(
child:
cancelButton
,
);
}
expandedButtons
.
add
(
cancelButton
);
}
}
return
expandedButtons
;
}
/// Returns alert default border style
ShapeBorder
_defaultShape
()
{
return
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
10.0
),
side:
BorderSide
(
color:
Colors
.
blueGrey
,
),
);
}
/// Returns alert image for icon
Widget
_getImage
()
{
Widget
response
;
if
(
urlImage
!=
null
&&
urlImage
!.
isNotEmpty
)
{
response
=
Image
.
network
(
urlImage
!);
return
response
;
}
switch
(
type
)
{
case
AlertType
.
success
:
response
=
Image
.
asset
(
""
);
break
;
case
AlertType
.
error
:
response
=
Image
.
asset
(
""
);
break
;
case
AlertType
.
info
:
response
=
Image
.
asset
(
""
);
break
;
case
AlertType
.
warning
:
response
=
Image
.
asset
(
""
);
break
;
case
AlertType
.
none
:
response
=
Container
();
break
;
default
:
response
=
image
??
Container
();
break
;
}
return
response
;
}
/// Shows alert with selected animation
_showAnimation
(
animation
,
secondaryAnimation
,
child
)
{
return
AnimationTransition
.
fromBottom
(
animation
,
secondaryAnimation
,
child
);
}
}
lib/widgets/alert/src/alert_style.dart
deleted
100755 → 0
View file @
5d865668
import
'package:flutter/material.dart'
;
enum
AlertType
{
error
,
success
,
info
,
warning
,
none
}
/// Buttons container
enum
ButtonsDirection
{
row
,
column
}
/// Defines Default Alert Window Padding
const
EdgeInsets
defaultAlertPadding
=
EdgeInsets
.
symmetric
(
horizontal:
40.0
,
vertical:
24.0
);
typedef
AlertAnimation
=
Widget
Function
(
BuildContext
context
,
Animation
<
double
>
animation
,
Animation
<
double
>
secondaryAnimation
,
Widget
child
,
);
/// Alert style class for reusable customization of dialogs.
class
AlertStyle
{
/// The [animationDuration] parameter is used to set the animation transition time. Default: "200 ms"
final
Duration
animationDuration
;
/// The [alertBorder] parameter sets border.
final
ShapeBorder
?
alertBorder
;
/// The [isButtonVisible] paramater is used to decide hide or display buttons
final
bool
isButtonVisible
;
/// The [isCloseButton] parameter sets visibility of the close button. Default: "true"
final
bool
isCloseButton
;
/// The [isOverlayTapDismiss] parameter sets closing the alert by clicking outside. Default: "true"
final
bool
isOverlayTapDismiss
;
/// The [backgroundColor] parameter sets the background color.
final
Color
?
backgroundColor
;
/// The [overlayColor] parameter sets the background color of the outside. Default: "Color(0xDD000000)"
final
Color
overlayColor
;
/// The [titleStyle] parameter sets alert title text style.
final
TextStyle
titleStyle
;
/// The [descStyle] parameter sets alert desc text style.
final
TextStyle
descStyle
;
/// The [titleTextAlign] parameter sets alignment of the title.
final
TextAlign
titleTextAlign
;
/// The [descTextAlign] parameter sets alignment of the desc.
final
TextAlign
descTextAlign
;
/// The [buttonAreaPadding] parameter sets button area padding.
final
EdgeInsets
buttonAreaPadding
;
/// The [constraints] parameter sets Alert size.
final
BoxConstraints
?
constraints
;
/// The [buttonsDirection] parameter sets button container as Row or Col.
final
ButtonsDirection
buttonsDirection
;
/// The [alertElevation] parameter sets elevation of alert dialog container.
final
double
?
alertElevation
;
/// The [alertPadding] parameter sets alert area padding.
final
EdgeInsets
alertPadding
;
/// The [alertAlignment] parameter sets alert dialog alignment.
final
AlignmentGeometry
alertAlignment
;
/// The [isTitleSelectable] parameter sets title text is selectable or not.
final
bool
isTitleSelectable
;
/// The [isDescSelectable] parameter sets desc text is selectable or not.
final
bool
isDescSelectable
;
/// The [titlePadding] parameter sets title area padding.
final
EdgeInsets
?
titlePadding
;
/// The [descPadding] parameter sets desc area padding.
final
EdgeInsets
descPadding
;
/// Alert style constructor function
/// All properties are optional.
const
AlertStyle
({
this
.
animationDuration
=
const
Duration
(
milliseconds:
200
),
this
.
alertBorder
,
this
.
isButtonVisible
=
true
,
this
.
isCloseButton
=
true
,
this
.
isOverlayTapDismiss
=
true
,
this
.
backgroundColor
,
this
.
overlayColor
=
Colors
.
black87
,
this
.
titleStyle
=
const
TextStyle
(
color:
Colors
.
black
,
fontWeight:
FontWeight
.
w500
,
fontStyle:
FontStyle
.
normal
),
this
.
titleTextAlign
=
TextAlign
.
center
,
this
.
descStyle
=
const
TextStyle
(
color:
Colors
.
black
,
fontWeight:
FontWeight
.
w400
,
fontStyle:
FontStyle
.
normal
),
this
.
descTextAlign
=
TextAlign
.
center
,
this
.
buttonAreaPadding
=
const
EdgeInsets
.
all
(
20.0
),
this
.
constraints
,
this
.
buttonsDirection
=
ButtonsDirection
.
row
,
this
.
alertElevation
,
this
.
alertPadding
=
defaultAlertPadding
,
this
.
alertAlignment
=
Alignment
.
center
,
this
.
isTitleSelectable
=
false
,
this
.
isDescSelectable
=
false
,
this
.
titlePadding
,
this
.
descPadding
=
const
EdgeInsets
.
all
(
0.0
),
});
}
lib/widgets/alert/src/animation_transition.dart
deleted
100755 → 0
View file @
5d865668
import
'package:flutter/material.dart'
;
/// Predefined functions for transition animations
///
/// Exp: AnimationTransition.fromRight(animation, secondaryAnimation, child);
class
AnimationTransition
{
/// Slide animation, from right to left (SlideTransition)
static
fromRight
(
Animation
<
double
>
animation
,
Animation
<
double
>
secondaryAnimation
,
Widget
child
)
{
return
SlideTransition
(
position:
Tween
<
Offset
>(
begin:
const
Offset
(
1.0
,
0.0
),
end:
Offset
.
zero
,
).
animate
(
animation
),
child:
child
,
);
}
/// Slide animation, from left to right (SlideTransition)
static
fromLeft
(
Animation
<
double
>
animation
,
Animation
<
double
>
secondaryAnimation
,
Widget
child
)
{
return
SlideTransition
(
position:
Tween
<
Offset
>(
begin:
const
Offset
(-
1.0
,
0.0
),
end:
Offset
.
zero
,
).
animate
(
animation
),
child:
child
,
);
}
/// Slide animation, from top to bottom (SlideTransition)
static
fromTop
(
Animation
<
double
>
animation
,
Animation
<
double
>
secondaryAnimation
,
Widget
child
)
{
return
SlideTransition
(
position:
Tween
<
Offset
>(
begin:
const
Offset
(
0.0
,
-
1.0
),
end:
Offset
.
zero
,
).
animate
(
animation
),
child:
child
,
);
}
/// Slide animation, from top to bottom (SlideTransition)
static
fromBottom
(
Animation
<
double
>
animation
,
Animation
<
double
>
secondaryAnimation
,
Widget
child
)
{
return
SlideTransition
(
position:
Tween
<
Offset
>(
begin:
const
Offset
(
0.0
,
1.0
),
end:
Offset
.
zero
,
).
animate
(
animation
),
child:
child
,
);
}
/// Scale animation, from in to out (ScaleTransition)
static
grow
(
Animation
<
double
>
animation
,
Animation
<
double
>
secondaryAnimation
,
Widget
child
)
{
return
ScaleTransition
(
scale:
Tween
<
double
>(
begin:
0.0
,
end:
1.0
,
).
animate
(
CurvedAnimation
(
parent:
animation
,
curve:
Interval
(
0.00
,
0.50
,
curve:
Curves
.
linear
,
),
),
),
child:
child
,
);
}
/// Scale animation, from out to in (ScaleTransition)
static
shrink
(
Animation
<
double
>
animation
,
Animation
<
double
>
secondaryAnimation
,
Widget
child
)
{
return
ScaleTransition
(
scale:
Tween
<
double
>(
begin:
1.2
,
end:
1.0
,
).
animate
(
CurvedAnimation
(
parent:
animation
,
curve:
Interval
(
0.5
,
1.0
),
// curve: Interval(0,
// 0.50,
// 1.00,
// curve: Curves.linear,
// ),
),
),
child:
child
,
);
}
}
lib/widgets/alert/src/dialog_button.dart
deleted
100755 → 0
View file @
5d865668
/*
* rflutter_alert
* Created by Ratel
* https://ratel.com.tr
*
* Copyright (c) 2018 Ratel, LLC. All rights reserved.
* See LICENSE for distribution and usage details.
*/
import
'package:flutter/material.dart'
;
/// Used for defining alert buttons.
///
/// [child] and [onPressed] parameters are required.
class
DialogButton
extends
StatelessWidget
{
final
Widget
?
child
;
final
double
?
width
;
final
double
height
;
final
Color
?
color
;
final
Color
?
highlightColor
;
final
Color
?
splashColor
;
final
Gradient
?
gradient
;
final
BorderRadius
radius
;
final
VoidCallback
?
onPressed
;
final
BoxBorder
border
;
final
EdgeInsets
padding
;
final
EdgeInsets
margin
;
/// DialogButton constructor
const
DialogButton
({
super
.
key
,
required
this
.
child
,
this
.
width
,
this
.
height
=
50.0
,
this
.
color
,
this
.
highlightColor
,
this
.
splashColor
,
this
.
gradient
,
this
.
radius
=
const
BorderRadius
.
all
(
Radius
.
circular
(
20
)),
this
.
border
=
const
Border
.
fromBorderSide
(
BorderSide
(
color:
Color
(
0x00000000
),
width:
0
,
style:
BorderStyle
.
solid
,
),
),
this
.
padding
=
const
EdgeInsets
.
only
(
left:
6
,
right:
6
),
this
.
margin
=
const
EdgeInsets
.
all
(
6
),
required
this
.
onPressed
,
});
/// Creates alert buttons based on constructor params
@override
Widget
build
(
BuildContext
context
)
{
return
Container
(
margin:
margin
,
width:
width
,
height:
height
,
decoration:
BoxDecoration
(
color:
color
??
Theme
.
of
(
context
).
colorScheme
.
secondary
,
gradient:
gradient
,
borderRadius:
radius
,
border:
border
,
),
child:
Material
(
color:
Colors
.
transparent
,
child:
InkWell
(
borderRadius:
radius
,
highlightColor:
highlightColor
??
Theme
.
of
(
context
).
highlightColor
,
splashColor:
splashColor
??
Theme
.
of
(
context
).
splashColor
,
onTap:
onPressed
,
child:
Padding
(
padding:
padding
,
child:
Center
(
child:
child
,
),
),
),
),
);
}
}
Prev
1
2
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
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 comment