Related
So I made a CRUD app with Firebase Firestore in Flutter. I have this error where it says
Exception caught by gesture
═══════════════════════
a document path must be a non-empty string
'package:cloud_firestore/src/collection_reference.dart':
package:cloud_firestore/src/collection_reference.dart:1
Failed assertion: line 116 pos 14: 'path.isNotEmpty'
I build this app with various tutorial videos in Youtube. I can make the Create, Read, and Delete features but I face this problem with Update features. Here's the code ...or check this Github https://github.com/rayhanyovi/Dapurku
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:dapurkuu/warna.dart';
import 'package:dapurkuu/widget.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
//=====================================
const Color Red = Color(0xFFDA4040);
const Color Blue = Color(0xFF5F52EE);
const Color Black = Color(0xFF3A3A3A);
const Color Grey = Color(0xFF717171);
const Color BGColor = Color(0xFFEEEFF5);
final controllerName = TextEditingController();
final controllerAge = TextEditingController();
final controllerBirthday = TextEditingController();
final updateName = TextEditingController();
final updateAge = TextEditingController();
final updateBirthday = TextEditingController();
//=====================================
class User {
String id;
final String name;
final int age;
final DateTime birthday;
User({
this.id = '',
required this.name,
required this.age,
required this.birthday,
});
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
'age': age,
'birthday': birthday,
};
static User fromJson(Map<String, dynamic> json) => User(
id: json['id'],
name: json['name'],
age: json['age'],
birthday: (json['birthday'] as Timestamp).toDate(),
);
}
//=====================================
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(statusBarColor: Colors.transparent));
return MaterialApp(
debugShowCheckedModeBanner: false,
title: "Test",
home: Home(),
);
}
}
//=====================================
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final CollectionReference _users =
FirebaseFirestore.instance.collection('users');
#override
Widget build(BuildContext context) {
Future createUser(User user) async {
final docUser = FirebaseFirestore.instance.collection('users').doc();
user.id = docUser.id;
final json = user.toJson();
await docUser.set(json);
Future updateUser(User user) async {
final docUser = FirebaseFirestore.instance.collection('users').doc();
user.id = docUser.id;
final json = user.toJson();
await docUser.update(json);
}
}
Widget buildUser(User user) {
return Container(
margin: EdgeInsets.only(
right: 20,
left: 20,
bottom: 15,
),
child: Material(
child: ListTile(
onTap: () {
print("card");
},
contentPadding:
EdgeInsets.only(top: 10, bottom: 10, left: 20, right: 15),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
tileColor: Colors.white,
title: Padding(
padding: const EdgeInsets.only(bottom: 5),
child: Text(
user.name,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Jumlah: ${user.age}',
),
Text(
'Kadaluarsa: ${DateFormat('dd MMMM yyyy').format(user.birthday)}')
],
),
trailing: Wrap(spacing: 0, children: [
IconButton(
icon: Icon(Icons.edit),
iconSize: 24,
color: Colors.black,
onPressed: () async {
await Edit(context, createUser);
},
),
IconButton(
icon: Icon(Icons.delete),
iconSize: 24,
color: Colors.red,
onPressed: () {
final docUser = FirebaseFirestore.instance
.collection('users')
.doc(user.id);
docUser.delete();
},
),
]),
),
),
);
}
return Scaffold(
backgroundColor: BGColor,
appBar: _AppBar(),
body: Column(
children: [
/* JUDUL */
Column(
children: [
Container(
margin: EdgeInsets.only(left: 20),
alignment: Alignment.centerLeft,
child: SearchBox()),
Container(
margin: EdgeInsets.only(left: 20),
alignment: Alignment.centerLeft,
child: Text(
'Bahan Dapurmu',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Black,
),
),
)
],
),
/* STREAM BUILDER */
StreamBuilder(
stream: readUsers(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final users = snapshot.data!;
return Expanded(
child: ListView(
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: users.map(buildUser).toList(),
),
);
} else if (snapshot.hasError) {
return Container(
child: Text("data kosong"),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
await Create(context, createUser);
},
child: Icon(Icons.add),
),
);
}
Edit(BuildContext context, Future<dynamic> updateUser(User user)) async {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Tambahkan bahan"),
content: Column(
children: [
TextField(
decoration: const InputDecoration(
labelText: 'Nama',
),
controller: updateName,
),
TextField(
decoration: const InputDecoration(
labelText: 'Umur',
),
controller: updateAge,
keyboardType: TextInputType.number,
),
TextField(
controller: updateBirthday,
//editing controller of this TextField
decoration: InputDecoration(
labelText: "Enter Date" //label text of field
),
readOnly: true,
//set it true, so that user will not able to edit text
onTap: () async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(1950),
//DateTime.now() - not to allow to choose before today.
lastDate: DateTime(2100));
if (pickedDate != null) {
print(
pickedDate);
//pickedDate output format => 2021-03-10 00:00:00.000
//formatted date output using intl package => 2021-03-16
setState(() {
updateBirthday.text = pickedDate
.toString(); //set output date to TextField value.
});
} else {}
},
),
],
),
actions: <Widget>[
MaterialButton(
elevation: 5.0,
child: Text("Ubah Data"),
onPressed: () {
final user = User(
name: updateName.text,
age: int.parse(updateAge.text),
birthday: DateTime.parse(updateBirthday.text),
);
final docUser = FirebaseFirestore.instance
.collection('users')
.doc(user.id);
updateUser(user);
}),
],
);
});
}
Create(BuildContext context, Future<dynamic> createUser(User user)) async {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Tambahkan bahan"),
content: Column(
children: [
TextField(
decoration: const InputDecoration(
labelText: 'Nama',
),
controller: controllerName,
),
TextField(
decoration: const InputDecoration(
labelText: 'Umur',
),
controller: controllerAge,
keyboardType: TextInputType.number,
),
TextField(
controller: controllerBirthday,
//editing controller of this TextField
decoration: InputDecoration(
labelText: "Enter Date" //label text of field
),
readOnly: true,
//set it true, so that user will not able to edit text
onTap: () async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(1950),
//DateTime.now() - not to allow to choose before today.
lastDate: DateTime(2100));
if (pickedDate != null) {
print(
pickedDate);
//pickedDate output format => 2021-03-10 00:00:00.000
//formatted date output using intl package => 2021-03-16
setState(() {
controllerBirthday.text = pickedDate
.toString(); //set output date to TextField value.
});
} else {}
},
),
],
),
actions: <Widget>[
MaterialButton(
elevation: 5.0,
child: Text("Tambahkan"),
onPressed: () {
final user = User(
name: controllerName.text,
age: int.parse(controllerAge.text),
birthday: DateTime.parse(controllerBirthday.text),
);
createUser(user);
}),
],
);
});
}
Stream<List<User>> readUsers() => FirebaseFirestore.instance
.collection('users')
.snapshots()
.map((snapshot) =>
snapshot.docs.map((doc) => User.fromJson(doc.data())).toList());
}
//=====================================
AppBar _AppBar() {
return AppBar(
elevation: 0,
backgroundColor: BGColor,
title: Center(
child: Text(
"Dapurku",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Black,
),
),
),
);
}
//=====================================
class UpdateButton extends StatelessWidget {
const UpdateButton({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: IconButton(
icon: Icon(Icons.edit),
iconSize: 24,
color: Colors.black,
onPressed: () {},
),
);
}
}
//=====================================
class SearchBox extends StatelessWidget {
const SearchBox({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.only(left: 10),
child: const TextField(
decoration: InputDecoration(
contentPadding: EdgeInsets.all(0),
prefixIcon: Icon(
Icons.search,
color: Black,
size: 20,
),
prefixIconConstraints: BoxConstraints(
maxHeight: 20,
minWidth: 25,
),
border: InputBorder.none,
hintText: "Cari Bahan...",
hintStyle: TextStyle(
color: Grey,
)),
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
);
}
}
Can you tell me what I did wrong or help me solve the problem? I've been looking for it anywhere and solving this problem for days. It is for my school assignment :/
hope all are doing well
i use a Switch Button in popup Form when pressing the floating action Button but its not working or the UI not updated when i pressed the Switch Button
I used it in a StatefulWidget but its not toggle until i press hot reload,if there any Suggestion please
here where i use the Switch :
void _openAlbumsDialog() {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Add an Album'),
content: Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _form,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
initialValue: _albumInitValues['name'],
decoration: InputDecoration(
helperText: 'Album Name',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
),
),
textInputAction: TextInputAction.next,
onSaved: ((newValue) {
_editedALbum = Album(
albumId: DateTime.now().toString(),
albumName: newValue!,
albumLinks: _editedALbum.albumLinks,
);
}),
validator: (value) {
if (value!.isEmpty) {
return 'Please insert a Value';
} else {
return null;
}
},
),
const SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('Private'),
Consumer<Album>(
builder: (context, value, child) => Switch.adaptive(
mouseCursor: MouseCursor.uncontrolled,
value: _isPrivate,
onChanged: (value) {
final albumProvider =
Provider.of<Album>(context, listen: false);
albumProvider.togglePrivateAlbum();
setState(() {
_isPrivate = value;
});
},
),
),
actions: [
TextButton(
onPressed: () {
FocusScope.of(context).unfocus();
_isLoading
? const Center(
child: CircularProgressIndicator(),
)
: _saveAlbumFOrm();
},
child: const Text('Save'))
],
));
}
#override
Widget build(BuildContext context) {
return SpeedDial(
children: [
SpeedDialChild(
child: const Icon(Icons.folder),
backgroundColor: Colors.green,
foregroundColor: Colors.white,
label: 'Add a new Album',
labelStyle: const TextStyle(fontSize: 12.0),
onTap: _openAlbumsDialog,
),
],
);
}
}
void togglePrivate in Album Provider:
void togglePrivateAlbum() {
isPrivate = !isPrivate;
}
It happens so because you have to recreate your own StatefulBuilder(), so wrap your showDialog() with a StatefulBuilder() wich will then have the targeted state
StatefulBuilder(
builder:(context, StateSetter innerSetter){
....
//use the setter now
innerSetter(() {
_isPrivate = value;
});
....
})
I'm trying to consume an API for the login screen but keep getting this error.
I want to be able to login via an API to my flutter app.
the first part is what is being posted to the API and the second part is the server response as seen on the code below
I/flutter ( 892): {email: rashid.david12#gmail.com, password: 123456789}
I/flutter ( 892): {errors: [The email field is required., The password field is required.]}
This is my code, what should I do, or any workaround for the error above?
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class Signin extends StatefulWidget {
#override
_SigninState createState() => _SigninState();
}
class _SigninState extends State<Signin> {
// For CircularProgressIndicator.
bool visible = false ;
// Getting value from TextField widget.
final emailController = TextEditingController();
final passwordController = TextEditingController();
Future userLogin() async{
// Showing CircularProgressIndicator.
setState(() {
visible = true ;
});
// Getting value from Controller
String email = emailController.text;
String password = passwordController.text;
// SERVER LOGIN API URL
var url = 'http://192.236.160.238/api/login';
// Store all data with Param Name.
var data = {'email': email, 'password' : password};
// Starting Web API Call.
var response = await http.post(url, body: json.encode(data[email]));
print(data);
// Getting Server response into variable.
var message = jsonDecode(response.body);
print(message);
// If the Response Message is Matched.
if(message == 'success')
{
// Hiding the CircularProgressIndicator.
setState(() {
visible = false;
});
// Navigate to Home & Sending Email to Next Screen.
Navigator.pushNamed(context, '/home');
}else{
// If Email or Password did not Matched.
// Hiding the CircularProgressIndicator.
setState(() {
visible = false;
});
// Showing Alert Dialog with Response JSON Message.
// showDialog(
// context: context,
// builder: (BuildContext context) {
// return AlertDialog(
// title: new Text(message),
// actions: <Widget>[
// FlatButton(
// child: new Text("OK"),
// onPressed: () {
// Navigator.of(context).pop();
// },
// ),
// ],
// );
// },
// );
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: Icon(Icons.menu),
title: const Text('Bdm Self Service App'),
centerTitle: true,
actions: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Icon(Icons.search),
),
],
backgroundColor: Colors.blue,
elevation: 0.0,
),
body: Padding(
padding: EdgeInsets.fromLTRB(30.0, 40.0, 30.0, 0.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Center(
child: CircleAvatar(
backgroundImage: AssetImage('assets/rcmsbg.png'),
radius: 60.0,
),
),
SizedBox(height: 10.0),
TextFormField(
controller: emailController,
decoration: InputDecoration(labelText: 'Enter Username or Email'),
),
SizedBox(height: 10.0),
TextFormField(
controller: passwordController,
decoration: InputDecoration(labelText: 'Enter Password'),
autofocus: false,
obscureText: true,
),
SizedBox(height: 20.0),
FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
side: BorderSide(color: Colors.green)),
onPressed: userLogin,
child: Text('login'),
color: Colors.green,
minWidth: 300.0,
),
SizedBox(height: 20.0),
Text('Forgot Password?'),
FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
side: BorderSide(color: Colors.green)),
onPressed: () {
Navigator.pushNamed(context, '/resetpassword');
},
child: Text('Reset Password'),
color: Colors.green,
minWidth: 200.0,
),
SizedBox(height: 20.0),
Text('Do not have an account yet?'),
FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
side: BorderSide(color: Colors.green)),
onPressed: () {
Navigator.pushNamed(context, '/signup');
},
child: Text('Sign Up'),
color: Colors.green,
minWidth: 200.0,
),
],
),
// ],
),
// ),
);
}
}
If I am reading your code correctly, on the login function you are only
sending email and
// Store all data with Param Name.
var data = {'email': email, 'password' : password};
// Starting Web API Call.
var response = await http.post(url, body: json.encode(data)); // REMOVED [email]
print(data);
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
class SignUpScreen extends StatefulWidget {
const SignUpScreen({Key? key}) : super(key: key);
#override
_SignUpScreenState createState() => _SignUpScreenState();
}
class _SignUpScreenState extends State<SignUpScreen> {
TextEditingController emailController = TextEditingController();
TextEditingController passwordController = TextEditingController();
void login(String email , password) async {
try{
Response response = await post(
Uri.parse('https://reqres.in/api/login'),
body: {
'email' : 'eve.holt#reqres.in',
'password' : 'cityslicka'
}
);
if(response.statusCode == 200){
var data = jsonDecode(response.body.toString());
print(data['token']);
print('Login successfully');
}else {
print('failed');
}
}catch(e){
print(e.toString());
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sign Up Api'),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
controller: emailController,
decoration: InputDecoration(
hintText: 'Email'
),
),
SizedBox(height: 20,),
TextFormField(
controller: passwordController,
decoration: InputDecoration(
hintText: 'Password'
),
),
SizedBox(height: 40,),
GestureDetector(
onTap: (){
login(emailController.text.toString(), passwordController.text.toString());
},
child: Container(
height: 50,
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(10)
),
child: Center(child: Text('Login'),),
),
)
],
),
),
);
}
}
I am not using any button in alert dialog, so in action how can we prevent the overflow the alert dialog, if am using gesture detector or inkwell to get ontap or onpress function or is there any other method to do it
_showDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return UnicornAlertDialog(
title: Column(
children: <Widget>[
Container(
child: Image.asset('images/done.png'),
),
const SizedBox(height: 15.0),
Container(
child: Text(
'Verify',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
),
),
)
],
),
content: Text('You have successfully verified your mobile number',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white, fontSize: 15.0)),
gradient: LinearGradient(
colors: <Color>[
Color(0xDD4a00e0),
Color(0xFF8e2de2),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
actions: <Widget>[
Container(
child: new GestureDetector(
onTap:(){
Navigator.push(context,
MaterialPageRoute(builder: (context) => ThirdRoute()));
} ,
),
),
]
);
});
}
You are getting overflow error due to GestureDetector used inside actions property of the dialog. If you just want user to tap anywhere on the alertDialog, you can wrap the AlertDialog with GestureDetector. With this, when user taps anywhere on the dialog, it will navigate them to thirdRoute. Working code below:
_showDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return GestureDetector(
child: AlertDialog(
title:
Column(
children: <Widget>[
Container(
child: Image.asset('images/done.png'),
),
const SizedBox(height: 15.0),
Container(
child: Text(
'Verify',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.black,
fontSize: 20.0,
),
),
)
],
),
content:
Text('You have successfully verified your mobile number',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.black, fontSize: 15.0)),
// gradient: LinearGradient(
// colors: <Color>[
// Color(0xDD4a00e0),
// Color(0xFF8e2de2),
// ],
// begin: Alignment.topCenter,
// end: Alignment.bottomCenter,
// ),
actions: <Widget>[]
),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => NextScreen()));
}
);
});
}
Hope this answers your question.
_showDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return GestureDetector(
child: UnicornAlertDialog(
title: Column(
children: <Widget>[
Container(
child: Image.asset('images/done.png'),
),
const SizedBox(height: 15.0),
Container(
child: Text(
'Verify',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
),
),
)
],
),
content: Text(
'You have successfully verified your mobile number',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white, fontSize: 15.0)),
gradient: LinearGradient(
colors: <Color>[
Color(0xDD4a00e0),
Color(0xFF8e2de2),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
actions: <Widget>[ ]),
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => ThirdRoute()));
},
);
});
}
Unicorn alert dialog is used for background-color decoration, and since you cannot have gradient color in normal alert dialog, I used this.
code snippet
_showDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return UnicornAlertDialog(
title: GestureDetector(
onTap: () { print("on tap title");},
child: Column(
children: <Widget>[
Container(
child: Image.asset('assets/images/background.jpg'),
),
const SizedBox(height: 15.0),
Container(
child: Text(
'Verify',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
),
),
)
],
),
),
content: GestureDetector(
onTap: () { print("on tap content");},
child: Text('You have successfully verified your mobile number',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white, fontSize: 15.0)),
),
gradient: LinearGradient(
colors: <Color>[
Color(0xDD4a00e0),
Color(0xFF8e2de2),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
actions: <Widget>[
]
);
});
}
full code
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
class UnicornAlertDialog extends StatelessWidget {
const UnicornAlertDialog({
Key key,
#required this.gradient,
this.title,
this.titlePadding,
this.titleTextStyle,
this.content,
this.contentPadding = const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0),
this.contentTextStyle,
this.actions,
this.backgroundColor,
this.elevation,
this.semanticLabel,
this.shape,
}) : assert(contentPadding != null),
super(key: key);
final Gradient gradient;
final Widget title;
final EdgeInsetsGeometry titlePadding;
final TextStyle titleTextStyle;
final Widget content;
final EdgeInsetsGeometry contentPadding;
final TextStyle contentTextStyle;
final List<Widget> actions;
final Color backgroundColor;
final double elevation;
final String semanticLabel;
final ShapeBorder shape;
#override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
final ThemeData theme = Theme.of(context);
final DialogTheme dialogTheme = DialogTheme.of(context);
final List<Widget> children = <Widget>[];
String label = semanticLabel;
if (title != null) {
children.add(Padding(
padding: titlePadding ?? EdgeInsets.fromLTRB(24.0, 24.0, 24.0, content == null ? 20.0 : 0.0),
child: DefaultTextStyle(
style: titleTextStyle ?? dialogTheme.titleTextStyle ?? theme.textTheme.title,
child: Semantics(
child: title,
namesRoute: true,
container: true,
),
),
));
} else {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
label = semanticLabel;
break;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
label = semanticLabel ?? MaterialLocalizations.of(context)?.alertDialogLabel;
}
}
if (content != null) {
children.add(Flexible(
child: Padding(
padding: contentPadding,
child: DefaultTextStyle(
style: contentTextStyle ?? dialogTheme.contentTextStyle ?? theme.textTheme.subhead,
child: content,
),
),
));
}
if (actions != null) {
children.add(ButtonTheme.bar(
child: ButtonBar(
children: actions,
),
));
}
Widget dialogChild = IntrinsicWidth(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: children,
),
);
if (label != null)
dialogChild = Semantics(
namesRoute: true,
label: label,
child: dialogChild,
);
return Dialog(
backgroundColor: backgroundColor,
gradient: gradient,
elevation: elevation,
shape: shape,
child: dialogChild,
);
}
}
class Dialog extends StatelessWidget {
const Dialog({
Key key,
this.gradient,
this.backgroundColor,
this.elevation,
this.insetAnimationDuration = const Duration(milliseconds: 100),
this.insetAnimationCurve = Curves.decelerate,
this.shape,
this.child,
}) : super(key: key);
final Color backgroundColor;
final double elevation;
final Duration insetAnimationDuration;
final Curve insetAnimationCurve;
final ShapeBorder shape;
final Widget child;
final Gradient gradient;
static const RoundedRectangleBorder _defaultDialogShape =
RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0)));
static const double _defaultElevation = 24.0;
#override
Widget build(BuildContext context) {
final DialogTheme dialogTheme = DialogTheme.of(context);
return AnimatedPadding(
padding: MediaQuery.of(context).viewInsets + const EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0),
duration: insetAnimationDuration,
curve: insetAnimationCurve,
child: MediaQuery.removeViewInsets(
removeLeft: true,
removeTop: true,
removeRight: true,
removeBottom: true,
context: context,
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(minWidth: 280.0),
child: Material(
color: backgroundColor ?? dialogTheme.backgroundColor ?? Theme.of(context).dialogBackgroundColor,
elevation: elevation ?? dialogTheme.elevation ?? _defaultElevation,
shape: shape ?? dialogTheme.shape ?? _defaultDialogShape,
type: MaterialType.card,
child: ClipRRect(
borderRadius: _defaultDialogShape.borderRadius,
child: Container(
decoration: BoxDecoration(
gradient: gradient
),
child: child,
),
),
),
),
),
),
);
}
}
_showDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return UnicornAlertDialog(
title: GestureDetector(
onTap: () { print("on tap title");},
child: Column(
children: <Widget>[
Container(
child: Image.asset('assets/images/background.jpg'),
),
const SizedBox(height: 15.0),
Container(
child: Text(
'Verify',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
),
),
)
],
),
),
content: GestureDetector(
onTap: () { print("on tap content");},
child: Text('You have successfully verified your mobile number',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white, fontSize: 15.0)),
),
gradient: LinearGradient(
colors: <Color>[
Color(0xDD4a00e0),
Color(0xFF8e2de2),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
actions: <Widget>[
]
);
});
}
Future<void> _ackAlert(BuildContext context) {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Not in stock'),
content: const Text('This item is no longer available'),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
enum ConfirmAction { CANCEL, ACCEPT }
Future<ConfirmAction> _asyncConfirmDialog(BuildContext context) async {
return showDialog<ConfirmAction>(
context: context,
barrierDismissible: false, // user must tap button for close dialog!
builder: (BuildContext context) {
return AlertDialog(
title: Text('Reset settings?'),
content: const Text(
'This will reset your device to its default factory settings.'),
actions: <Widget>[
FlatButton(
child: const Text('CANCEL'),
onPressed: () {
Navigator.of(context).pop(ConfirmAction.CANCEL);
},
),
FlatButton(
child: const Text('ACCEPT'),
onPressed: () {
Navigator.of(context).pop(ConfirmAction.ACCEPT);
},
)
],
);
},
);
}
Future<String> _asyncInputDialog(BuildContext context) async {
String teamName = '';
return showDialog<String>(
context: context,
barrierDismissible: false, // dialog is dismissible with a tap on the barrier
builder: (BuildContext context) {
return AlertDialog(
title: Text('Enter current team'),
content: new Row(
children: <Widget>[
new Expanded(
child: new TextField(
autofocus: true,
decoration: new InputDecoration(
labelText: 'Team Name', hintText: 'eg. Juventus F.C.'),
onChanged: (value) {
teamName = value;
},
))
],
),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: () {
Navigator.of(context).pop(teamName);
},
),
],
);
},
);
}
enum Departments { Production, Research, Purchasing, Marketing, Accounting }
Future<Departments> _asyncSimpleDialog(BuildContext context) async {
return await showDialog<Departments>(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return SimpleDialog(
title: const Text('Select Departments '),
children: <Widget>[
SimpleDialogOption(
onPressed: () {
Navigator.pop(context, Departments.Production);
},
child: const Text('Production'),
),
SimpleDialogOption(
onPressed: () {
Navigator.pop(context, Departments.Research);
},
child: const Text('Research'),
),
SimpleDialogOption(
onPressed: () {
Navigator.pop(context, Departments.Purchasing);
},
child: const Text('Purchasing'),
),
SimpleDialogOption(
onPressed: () {
Navigator.pop(context, Departments.Marketing);
},
child: const Text('Marketing'),
),
SimpleDialogOption(
onPressed: () {
Navigator.pop(context, Departments.Accounting);
},
child: const Text('Accounting'),
)
],
);
});
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: AppBar(
title: Text("Dialog"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new RaisedButton(
onPressed: () {
_showDialog(context);
},
child: const Text("Unicon Dialog"),
),
new RaisedButton(
onPressed: () {
_ackAlert(context);
},
child: const Text("Ack Dialog"),
),
new RaisedButton(
onPressed: () async {
final ConfirmAction action = await _asyncConfirmDialog(context);
print("Confirm Action $action" );
},
child: const Text("Confirm Dialog"),
),
new RaisedButton(
onPressed: () async {
final Departments deptName = await _asyncSimpleDialog(context);
print("Selected Departement is $deptName");
},
child: const Text("Simple dialog"),
),
new RaisedButton(
onPressed: () async {
final String currentTeam = await _asyncInputDialog(context);
print("Current team name is $currentTeam");
},
child: const Text("Input Dialog"),
),
],
),
),
);
}
}
void main() {
runApp(new MaterialApp(home: new MyApp()));
}
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.
https://pub.dartlang.org/packages/modal_progress_hud
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.
create something like bool _isLoading = false;
When the sign-in button is pressed then
onPressed: (){
setState(() {
_isLoading = true;
});
}
Meanwhile, use Stack to insert the CircularProgressIndicator
Stack(children: <Widget>[
_isLoading ? CircularProgressIndicator() : Container(), //only show CircularProgressIndicator when loading
//your login body here
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: () async => false, child: alert);
},
);
}
**For dismiss call this **
Navigator.pop(context);