如何在 flutter 中添加全屏加载器
How to add full screen loader in flutter
在这里,我正在尝试添加一个全屏加载器,但它不起作用,我已经完成了完整的代码,但是当我尝试添加一个全屏加载器时,它不起作用。所以在这里我只想在单击登录按钮时添加全屏加载程序。在这段代码中,我已经定义了 _isLoading 变量,当它的真正加载器将被显示时。
这是我试过的代码。
class LoginScreen extends StatefulWidget {
LoginScreen({Key key}) : super(key: key);
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
@override
Widget build(BuildContext context) {
final formKey = new GlobalKey<FormState>();
final _emailFocusNode = new FocusNode();
final _passwordFocusNode = new FocusNode();
String _username;
String _password;
bool rememberMe = false;
bool _isLoading = false;
@override
void initState() {
super.initState();
}
void _showErrorDialog(String message) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) => ShowErrorDialog(
title: Text('An Error Occurred!'),
content: Text(message),
));
}
Widget _buildUserNameField() {
return EnsureVisibleWhenFocused(
focusNode: _emailFocusNode,
child: TudoEmailWidget(
focusNode: _emailFocusNode,
prefixIcon: Icon(Icons.email),
labelText: AppConstantsValue.appConst['login']['email']
['translation'],
validator: (val) => Validators.validateEmail(val.trim()),
onSaved: (val) => _username = val.trim(),
// onChanged:(val) => _username = val.trim(),
),
);
}
Widget _buildPasswordField() {
return EnsureVisibleWhenFocused(
focusNode: _passwordFocusNode,
child: TudoPasswordWidget(
focusNode: _passwordFocusNode,
prefixIcon: Icon(Icons.vpn_key),
hintText: AppConstantsValue.appConst['login']['password']
['translation'],
labelText: AppConstantsValue.appConst['login']['password']
['translation'],
validator: Validators().validatePassword,
onSaved: (val) => _password = val.trim(),
),
);
}
Widget _buildLoginButton(BuildContext context, LoginViewModel loginVm) {
return GestureDetector(
child: TudoLoginButtonWidget.buildRoundedRectButton(
"Log In", signInGradients, false),
onTap: () async {
if (!formKey.currentState.validate()) {
// Invalid!
return;
}
formKey.currentState.save();
print("User");
setState(() {
_isLoading = true;
});
try {
LoginRepository _loginRepository = LoginRepository();
Map<String, dynamic> loginResponse =
await _loginRepository.loginUser(_username, _password);
if (loginResponse['error'] != null) {
var errorMessage = 'Invalid email or password';
_showErrorDialog(errorMessage);
} else {
LoginUser userModel = LoginUser(
token: loginResponse['data']['loginUser']['token'],
user: User.fromJson(
loginResponse['data']['loginUser']['user'],
),
);
SharedPreferences preferences =
await SharedPreferences.getInstance();
preferences.setString('user', loginUserToJson(userModel));
loginVm.loginMe(context, userModel);
}
setState(() {
_isLoading = false;
});
} catch (error) {
print('error');
print(error);
setState(() {
_isLoading = false;
});
var errorMessage = 'Authentication failed';
_showErrorDialog(errorMessage);
}
},
);
}
Widget content(context, loginVm) {
ProgressDialog pr =
new ProgressDialog(context, type: ProgressDialogType.Normal);
pr.style(message: 'Showing some progress...');
return new SafeArea(
top: false,
bottom: false,
child: Form(
key: formKey,
child: Scrollbar(
child: SingleChildScrollView(
dragStartBehavior: DragStartBehavior.down,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: new Container(
margin: EdgeInsets.fromLTRB(30, 100, 30, 0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
TudoLogoWidget(),
_buildUserNameField(),
SizedBox(
height: 20,
),
_buildPasswordField(),
SizedBox(
height: 20.0,
),
_buildLoginButton(context, loginVm),
SizedBox(
height: 20,
),
],
),
),
),
),
),
);
}
return new WillPopScope(
onWillPop: () =>
SystemChannels.platform.invokeMethod('SystemNavigator.pop'),
child: Scaffold(
body: Container(
height: double.infinity,
width: double.infinity,
child: Stack(
children: <Widget>[
Background(),
SingleChildScrollView(
child: StoreConnector<AppState, LoginViewModel>(
converter: (Store<AppState> store) =>
LoginViewModel.fromStore(store),
builder: (BuildContext context, LoginViewModel loginVm) =>
content(context, loginVm),
)),
],
),
),
),
);
}
}
class LoginViewModel {
final Function(BuildContext context, LoginUser loginUser) loginMe;
LoginViewModel({
this.loginMe,
});
static LoginViewModel fromStore(Store<AppState> store) {
return LoginViewModel(
loginMe: (context, loginUser) {
store.dispatch(
login(context, loginUser),
);
},
);
}
}
我注意到你的构建方法中有很多嵌套的东西。你可以把它们拉出来。
例如,我认为您的 initState() 方法不会在任何时候被调用。
您需要做的是:
只需从现在所在的位置删除该行:
Widget build(BuildContext context) {
然后像这样添加:
@override
Widget build(BuildContext context) {
// YOU HAVE THIS
final normalLoginWidgets = WillPopScope(
onWillPop: () =>
SystemChannels.platform.invokeMethod('SystemNavigator.pop'),
child: Scaffold(
body: Container(
height: double.infinity,
width: double.infinity,
child: Stack(
children: <Widget>[
Background(),
SingleChildScrollView(
child: StoreConnector<AppState, LoginViewModel>(
converter: (Store<AppState> store) =>
LoginViewModel.fromStore(store),
builder: (BuildContext context, LoginViewModel loginVm) =>
content(context, loginVm),
)),
],
),
),
),
);
// THIS IS NEW
if (_isLoading) {
return Stack(children: <Widget>[
normalLoginWidgets,
loadingWidget()
]);
} else {
return normalLoginWidgets;
}
}
我为 showing/close 加载对话框创建了一个 class。
每当您需要显示全屏加载对话框时,您只需调用该方法即可。
我认为这是非常有效的方法,因为您不需要每次都为对话框编写代码
class ProcessDialog {
static ProcessDialog _instance = new ProcessDialog.internal();
static bool _isLoading = false;
ProcessDialog.internal();
factory ProcessDialog() => _instance;
static BuildContext _context;
static void closeLoadingDialog() {
if (_isLoading) {
Navigator.of(_context).pop();
_isLoading = false;
}
}
static void showLoadingDialog(BuildContext context) async {
_context = context;
_isLoading = true;
await showDialog(
context: _context,
barrierDismissible: false,
builder: (BuildContext context) {
return SimpleDialog(
elevation: 0.0,
backgroundColor: Colors.transparent,
children: <Widget>[
Center(
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(ColorUtils.primaryColor),
),
)
],
);
});
}
}
void showWait(context) {
showDialog(
context: context,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 4.0, sigmaY: 4.0),
child: Scaffold(
backgroundColor: Colors.transparent,
body: Container(
color: Colors.transparent,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
child: CircularProgressIndicator()
),
Container(
margin: EdgeInsets.symmetric(vertical: 20),
child: Text(
"LOADING...",
style: TextStyle(color: Colors.white),
),
)
],
),
),
)));
}
然后调用 showWait(context);在你的按钮事件中。
您可以使用 EasyLoader package
轻松添加全屏加载器
如何使用它的示例:
Widget build(BuildContext context) {
return Scaffold(
//// Wrap your body in a stack
body: Stack(
children: <Widget>[
Center(
child: Text("Lorem Ipsum"),
),
//// Put the loader widget at the end of the stack. You can set it to appear based on a boolean. E.g. a loading flag.
EasyLoader(image: AssetImage('assets/loading.png'),)
],
),
);
}
在这里,我正在尝试添加一个全屏加载器,但它不起作用,我已经完成了完整的代码,但是当我尝试添加一个全屏加载器时,它不起作用。所以在这里我只想在单击登录按钮时添加全屏加载程序。在这段代码中,我已经定义了 _isLoading 变量,当它的真正加载器将被显示时。
这是我试过的代码。
class LoginScreen extends StatefulWidget {
LoginScreen({Key key}) : super(key: key);
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
@override
Widget build(BuildContext context) {
final formKey = new GlobalKey<FormState>();
final _emailFocusNode = new FocusNode();
final _passwordFocusNode = new FocusNode();
String _username;
String _password;
bool rememberMe = false;
bool _isLoading = false;
@override
void initState() {
super.initState();
}
void _showErrorDialog(String message) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) => ShowErrorDialog(
title: Text('An Error Occurred!'),
content: Text(message),
));
}
Widget _buildUserNameField() {
return EnsureVisibleWhenFocused(
focusNode: _emailFocusNode,
child: TudoEmailWidget(
focusNode: _emailFocusNode,
prefixIcon: Icon(Icons.email),
labelText: AppConstantsValue.appConst['login']['email']
['translation'],
validator: (val) => Validators.validateEmail(val.trim()),
onSaved: (val) => _username = val.trim(),
// onChanged:(val) => _username = val.trim(),
),
);
}
Widget _buildPasswordField() {
return EnsureVisibleWhenFocused(
focusNode: _passwordFocusNode,
child: TudoPasswordWidget(
focusNode: _passwordFocusNode,
prefixIcon: Icon(Icons.vpn_key),
hintText: AppConstantsValue.appConst['login']['password']
['translation'],
labelText: AppConstantsValue.appConst['login']['password']
['translation'],
validator: Validators().validatePassword,
onSaved: (val) => _password = val.trim(),
),
);
}
Widget _buildLoginButton(BuildContext context, LoginViewModel loginVm) {
return GestureDetector(
child: TudoLoginButtonWidget.buildRoundedRectButton(
"Log In", signInGradients, false),
onTap: () async {
if (!formKey.currentState.validate()) {
// Invalid!
return;
}
formKey.currentState.save();
print("User");
setState(() {
_isLoading = true;
});
try {
LoginRepository _loginRepository = LoginRepository();
Map<String, dynamic> loginResponse =
await _loginRepository.loginUser(_username, _password);
if (loginResponse['error'] != null) {
var errorMessage = 'Invalid email or password';
_showErrorDialog(errorMessage);
} else {
LoginUser userModel = LoginUser(
token: loginResponse['data']['loginUser']['token'],
user: User.fromJson(
loginResponse['data']['loginUser']['user'],
),
);
SharedPreferences preferences =
await SharedPreferences.getInstance();
preferences.setString('user', loginUserToJson(userModel));
loginVm.loginMe(context, userModel);
}
setState(() {
_isLoading = false;
});
} catch (error) {
print('error');
print(error);
setState(() {
_isLoading = false;
});
var errorMessage = 'Authentication failed';
_showErrorDialog(errorMessage);
}
},
);
}
Widget content(context, loginVm) {
ProgressDialog pr =
new ProgressDialog(context, type: ProgressDialogType.Normal);
pr.style(message: 'Showing some progress...');
return new SafeArea(
top: false,
bottom: false,
child: Form(
key: formKey,
child: Scrollbar(
child: SingleChildScrollView(
dragStartBehavior: DragStartBehavior.down,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: new Container(
margin: EdgeInsets.fromLTRB(30, 100, 30, 0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
TudoLogoWidget(),
_buildUserNameField(),
SizedBox(
height: 20,
),
_buildPasswordField(),
SizedBox(
height: 20.0,
),
_buildLoginButton(context, loginVm),
SizedBox(
height: 20,
),
],
),
),
),
),
),
);
}
return new WillPopScope(
onWillPop: () =>
SystemChannels.platform.invokeMethod('SystemNavigator.pop'),
child: Scaffold(
body: Container(
height: double.infinity,
width: double.infinity,
child: Stack(
children: <Widget>[
Background(),
SingleChildScrollView(
child: StoreConnector<AppState, LoginViewModel>(
converter: (Store<AppState> store) =>
LoginViewModel.fromStore(store),
builder: (BuildContext context, LoginViewModel loginVm) =>
content(context, loginVm),
)),
],
),
),
),
);
}
}
class LoginViewModel {
final Function(BuildContext context, LoginUser loginUser) loginMe;
LoginViewModel({
this.loginMe,
});
static LoginViewModel fromStore(Store<AppState> store) {
return LoginViewModel(
loginMe: (context, loginUser) {
store.dispatch(
login(context, loginUser),
);
},
);
}
}
我注意到你的构建方法中有很多嵌套的东西。你可以把它们拉出来。
例如,我认为您的 initState() 方法不会在任何时候被调用。
您需要做的是: 只需从现在所在的位置删除该行:
Widget build(BuildContext context) {
然后像这样添加:
@override
Widget build(BuildContext context) {
// YOU HAVE THIS
final normalLoginWidgets = WillPopScope(
onWillPop: () =>
SystemChannels.platform.invokeMethod('SystemNavigator.pop'),
child: Scaffold(
body: Container(
height: double.infinity,
width: double.infinity,
child: Stack(
children: <Widget>[
Background(),
SingleChildScrollView(
child: StoreConnector<AppState, LoginViewModel>(
converter: (Store<AppState> store) =>
LoginViewModel.fromStore(store),
builder: (BuildContext context, LoginViewModel loginVm) =>
content(context, loginVm),
)),
],
),
),
),
);
// THIS IS NEW
if (_isLoading) {
return Stack(children: <Widget>[
normalLoginWidgets,
loadingWidget()
]);
} else {
return normalLoginWidgets;
}
}
我为 showing/close 加载对话框创建了一个 class。
每当您需要显示全屏加载对话框时,您只需调用该方法即可。
我认为这是非常有效的方法,因为您不需要每次都为对话框编写代码
class ProcessDialog {
static ProcessDialog _instance = new ProcessDialog.internal();
static bool _isLoading = false;
ProcessDialog.internal();
factory ProcessDialog() => _instance;
static BuildContext _context;
static void closeLoadingDialog() {
if (_isLoading) {
Navigator.of(_context).pop();
_isLoading = false;
}
}
static void showLoadingDialog(BuildContext context) async {
_context = context;
_isLoading = true;
await showDialog(
context: _context,
barrierDismissible: false,
builder: (BuildContext context) {
return SimpleDialog(
elevation: 0.0,
backgroundColor: Colors.transparent,
children: <Widget>[
Center(
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(ColorUtils.primaryColor),
),
)
],
);
});
}
}
void showWait(context) {
showDialog(
context: context,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 4.0, sigmaY: 4.0),
child: Scaffold(
backgroundColor: Colors.transparent,
body: Container(
color: Colors.transparent,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
child: CircularProgressIndicator()
),
Container(
margin: EdgeInsets.symmetric(vertical: 20),
child: Text(
"LOADING...",
style: TextStyle(color: Colors.white),
),
)
],
),
),
)));
}
然后调用 showWait(context);在你的按钮事件中。
您可以使用 EasyLoader package
轻松添加全屏加载器如何使用它的示例:
Widget build(BuildContext context) {
return Scaffold(
//// Wrap your body in a stack
body: Stack(
children: <Widget>[
Center(
child: Text("Lorem Ipsum"),
),
//// Put the loader widget at the end of the stack. You can set it to appear based on a boolean. E.g. a loading flag.
EasyLoader(image: AssetImage('assets/loading.png'),)
],
),
);
}