I have a login form with two textfields 'UserName', 'Password' & a button 'Login'. On tap of login button I am calling an API. I want to show a CircularProgressIndicator
during this api call. Progress dialog should show in the centre & top of login form. I have tried FutureBuilder
but it hides the login form shows CircularProgressIndicator
only. I want all content of screen to show behind the CircularProgressIndicator
.
Full Code:
import 'package:flutter/material.dart';
import 'package:the_don_flutter/userModel.dart';
import 'package:validate/validate.dart';
import 'package:http/http.dart' as http;
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'signup.dart';
class Login extends StatefulWidget{
@override
State<Login> createState() {
// TODO: implement createState
return LoginFormState();
}
}
class LoginFormState extends State<Login>{
final GlobalKey<FormState> formKey = new GlobalKey<FormState>();
String _passwordValidation(String value){
if(value.isEmpty){
return "Field Can't be empty.";
}else if(value.length < 6)
return "Password must be of six characters long.";
return null;
}
String _checkValidEmail(String value){
try{
Validate.isEmail(value);
}catch(e){
return "Email is not valid.";
}
return null;
}
Future<User> _loginUser() async{
var response = await http.post("https://example/public/api/login", headers: {}, body: {'username':'poras@techaheadcorp.com', 'password':'123456'})
.catchError((error) => print("Error $error"));
print("response of login ${response.body}");
return User.fromJson(json.decode(response.body));
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: Container(
padding: EdgeInsets.only(left: 20.0, top: 100.0, right: 20.0),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/bg_green.jpg"),
fit: BoxFit.fill)),
child: Column(
children: <Widget>[
Form(
key: formKey,
child: Column(children: <Widget>[
Padding(padding: EdgeInsets.only(bottom: 20.0),
child: TextFormField(
validator: _checkValidEmail,
decoration: InputDecoration(
hintText: "abc@example.com",
labelText: "User Name",
hintStyle: TextStyle(color: Colors.white)),
style: TextStyle(color: Colors.white),
autofocus: true,),),
TextFormField(
obscureText: true,
validator: _passwordValidation,
decoration: InputDecoration(
hintText: "password",
labelText: "Password",
hintStyle: TextStyle(color: Colors.white)),
style: TextStyle(color: Colors.white),
autofocus: true,)
],),),
Padding(padding: EdgeInsets.only(top: 20.0),
child: Row(mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text("Forgot Password?", textAlign: TextAlign.start, style: TextStyle(color: Colors.white,),),
],),),
Padding(padding: EdgeInsets.only(top: 20.0),
child: GestureDetector(
onTap: _submitForm,
child: Row(mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text("LOGIN", textAlign: TextAlign.start, style: TextStyle(color: Colors.white, fontSize: 40.0),),
Icon(Icons.chevron_right, size: 40.0, color: Colors.white,),
],),), ),
Expanded(
child: Padding(padding: EdgeInsets.only(bottom: 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Text("Don't have an account?", textAlign: TextAlign.start, style: TextStyle(color: Colors.white,),),
Container(
margin: EdgeInsets.only(left: 8.0),
child: GestureDetector(
onTap: (){Navigator.push(context, MaterialPageRoute(builder: (context) => Signup()));},
child: Text("REGISTER NOW!", textAlign: TextAlign.start, style: TextStyle(color: Colors.black,),),
)),
],
),))
],
),
),
);
}
_submitForm() {
if(formKey.currentState.validate()){
print("Go to Home page");
_loginUser();
}
}
}
To show a progressdialog
on button click while api
is fetching data on login screen.
Try this
Declare this method to show a progress dialog
showLoaderDialog(BuildContext context){
AlertDialog alert=AlertDialog(
content: new Row(
children: [
CircularProgressIndicator(),
Container(margin: EdgeInsets.only(left: 7),child:Text("Loading..." )),
],),
);
showDialog(barrierDismissible: false,
context:context,
builder:(BuildContext context){
return alert;
},
);
}
USAGE
on button click when api is called, call this method like this
onPressed: () {
showLoaderDialog(context);
//api here },
and when response is fetched dismiss that dialog like this
Navigator.pop(context);
This demo (with source code) should be very close to what your trying to do
It includes triggering the form validators before and after the async call.
You can try below code snippet for it
class ProgressHUD extends StatelessWidget {
final Widget child;
final bool inAsyncCall;
final double opacity;
final Color color;
final Animation<Color> valueColor;
ProgressHUD({
Key key,
@required this.child,
@required this.inAsyncCall,
this.opacity = 0.3,
this.color = Colors.grey,
this.valueColor,
}) : super(key: key);
@override
Widget build(BuildContext context) {
List<Widget> widgetList = new List<Widget>();
widgetList.add(child);
if (inAsyncCall) {
final modal = new Stack(
children: [
new Opacity(
opacity: opacity,
child: ModalBarrier(dismissible: false, color: color),
),
new Center(
child: new CircularProgressIndicator(
valueColor: valueColor,
),
),
],
);
widgetList.add(modal);
}
return Stack(
children: widgetList,
);
}
}
Use it
body: ProgressHUD(
child: screen,
inAsyncCall: _isLoading,
opacity: 0.0,
),
just change the state of _isloading true if you want to display progress.
where ever you want call this function
progressDialogue(BuildContext context) {
//set up the AlertDialog
AlertDialog alert=AlertDialog(
backgroundColor: Colors.transparent,
elevation: 0,
content: Container(
child: Center(
child: CircularProgressIndicator(),
),
),
);
showDialog(
//prevent outside touch
barrierDismissible: false,
context:context,
builder: (BuildContext context) {
//prevent Back button press
return WillPopScope(
onWillPop: (){},
child: alert);
},
);
}
**For dismiss call this **
Navigator.pop(context);
In Flutter, use a ProgressIndicator
widget. Show the progress programmatically by controlling when it's rendered through a boolean flag. Tell Flutter to update its state before your long-running task starts, and hide it after it ends.
In the example below, the build function is separated into three different functions. If showLoadingDialog()
is true
(when widgets.length == 0
), then render the ProgressIndicator
. Otherwise, render the ListView
with the data returned from a network call.
Full example
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
List widgets = [];
@override
void initState() {
super.initState();
loadData();
}
showLoadingDialog() {
return widgets.length == 0;
}
getBody() {
if (showLoadingDialog()) {
return getProgressDialog();
} else {
return getListView();
}
}
getProgressDialog() {
return Center(child: CircularProgressIndicator());
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: getBody());
}
ListView getListView() => ListView.builder(
itemCount: widgets.length,
itemBuilder: (BuildContext context, int position) {
return getRow(position);
});
Widget getRow(int i) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Text("Row ${widgets[i]["title"]}"),
);
}
loadData() async {
String dataURL = "https://jsonplaceholder.typicode.com/posts";
http.Response response = await http.get(dataURL);
setState(() {
widgets = jsonDecode(response.body);
});
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.