I'm working on my Flutter app and I try to set up a redirection process after creating an entity (user). The state management is handled by Riverpod. I'm using Firebase for the auth and Postgres for the database.
The insert method in the repository returns a User. With a StateNotifier I just want to check if the method returns a user. If the user is returned I set a success state object (CreateAccountStateSuccess), if it's not I set an error state object with a message. Problem: my saveUser method always return null in my StateNotifier, even though my user is persisted in Firebase and my database. I think it's a Riverpod issue. Any idea?
My repository:
Future<AppUser?> saveUser(String email, String nickname, String role,
String firstname, String lastname) async {
try {
connection.open().then((value) async {
Future<List<Map<String, Map<String, dynamic>>>> result = connection.mappedResultsQuery(
'insert into public.user(email,nickname,role,firstname,lastname) '
'values(#emailValue,#nicknameValue,#roleValue,#firstnameValue,#lastnameValue) '
'returning *',
substitutionValues: {
'emailValue': email,
'nicknameValue': nickname,
'roleValue': role,
'firstnameValue': firstname,
'lastnameValue': lastname,
},
allowReuse: true,
timeoutInSeconds: 30,
);
result.then((value) {
final userFromDataBase = value[0]['user']!;
return AppUser(
email: userFromDataBase['email'],
nickname: userFromDataBase['nickname'],
role: userFromDataBase['role'],
firstname: userFromDataBase['firstname'],
lastname: userFromDataBase['lastname']
);
});
});
} catch (e) {
print(ErrorHandler(message: e.toString()));
return null;
}
return null;
}
My Firebase method to create user for Firebase and using my repository method:
Future<AppUser?> registerWithEmailAndPassword(String email, String password, String nickname, String role, String firstname, String lastname) async {
FirebaseApp app = await Firebase.initializeApp(
name: 'Secondary', options: Firebase.app().options);
try {
UserCredential result =
await FirebaseAuth.instanceFor(app: app).createUserWithEmailAndPassword(email: email, password: password);
User? user = result.user;
if (user == null) {
throw Exception("No user found");
} else {
try {
return await UserRepository(user.email!).saveUser(email, nickname, role, firstname, lastname);
} on PostgreSQLException catch (e) {
print('CATCH POSTGRES EXCEPTION');
print(ErrorHandler(message: e.code.toString()));
}
}
} on FirebaseException catch (e) {
print('CATCH FIREBASE EXCEPTION');
print(ErrorHandler(message: e.code.toString()));
}
return null;
}
My controller:
class CreateAccountController extends StateNotifier<CreateAccountState> {
CreateAccountController(this.ref) : super(const CreateAccountStateInitial());
final Ref ref;
void register(String email, String password, String nickname, String role, String firstname, String lastname) async {
state = const CreateAccountStateLoading();
try {
await ref.read(authRepositoryProvider).registerWithEmailAndPassword(
email,
password,
nickname,
role,
firstname,
lastname
).then((user){
user != null ? state = const CreateAccountStateSuccess() : state = const CreateAccountStateError('Something went wrong with the user creation in database');
});
} catch (e) {
state = CreateAccountStateError(e.toString());
}
}
}
final createAccountControllerProvider =
StateNotifierProvider<CreateAccountController, CreateAccountState>((ref) {
return CreateAccountController(ref);
});
My state objects :
class CreateAccountState extends Equatable {
const CreateAccountState();
#override
List<Object> get props => [];
}
class CreateAccountStateInitial extends CreateAccountState {
const CreateAccountStateInitial();
#override
List<Object> get props => [];
}
class CreateAccountStateLoading extends CreateAccountState {
const CreateAccountStateLoading();
#override
List<Object> get props => [];
}
class CreateAccountStateSuccess extends CreateAccountState {
const CreateAccountStateSuccess();
#override
List<Object> get props => [];
}
class CreateAccountStateError extends CreateAccountState {
final String error;
const CreateAccountStateError(this.error);
#override
List<Object> get props => [error];
}
My screen:
class CreateAccountScreen extends StatefulHookConsumerWidget {
const CreateAccountScreen({Key? key}) : super(key: key);
#override
ConsumerState<CreateAccountScreen> createState() => _CreateAccountScreenState();
}
class _CreateAccountScreenState extends ConsumerState<CreateAccountScreen> {
TextEditingController emailController = TextEditingController();
TextEditingController passwordController = TextEditingController();
TextEditingController nicknameController = TextEditingController();
TextEditingController roleController = TextEditingController();
TextEditingController firstnameController = TextEditingController();
TextEditingController lastnameController = TextEditingController();
#override
Widget build(BuildContext context) {
ref.listen<CreateAccountState>(createAccountControllerProvider, ((previous, state) {
if (state is CreateAccountStateError) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(state.error.toString()),
backgroundColor: Colors.red,
));
}
print(state.toString());
if (state is CreateAccountStateSuccess) {
context.goNamed('/', params:
{
'screenName': 'users'
});
}
}));
return Scaffold(
appBar: AppBar(
title: const Text('Create an account'),
elevation: 8.0,
backgroundColor: Colors.deepOrangeAccent,
actions: [
TextButton.icon(
icon: const Icon(
Icons.logout_rounded,
color: Colors.white,
),
label: const Text('', style: TextStyle(color: Colors.white)),
onPressed: () async {
ref.read(loginControllerProvider.notifier).signOut();
},
),
]
),
body: Padding(
padding: const EdgeInsets.all(10),
child: ListView(
children: <Widget>[
Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(10),
child: const Text(
'Ludocal 2',
style: TextStyle(
color: Colors.deepOrange,
fontWeight: FontWeight.w500,
fontSize: 30),
)),
Container(
padding: const EdgeInsets.all(10),
child: TextFormField(
validator: (value) =>
value == null || value.isEmpty ? "Enter an email" : null,
controller: emailController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Email Address',
),
),
),
Container(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
child: TextFormField(
obscureText: true,
validator: (value) =>
value == null || value.isEmpty ? "Enter a password" : null,
controller: passwordController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
),
),
),
Container(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
child: TextFormField(
validator: (value) =>
value == null || value.isEmpty ? "Enter a nickname" : null,
controller: nicknameController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Nickname',
),
),
),
Container(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
child: TextFormField(
validator: (value) =>
value == null || value.isEmpty ? "Enter a role" : null,
controller: roleController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Role',
),
),
),
Container(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
child: TextFormField(
validator: (value) =>
value == null || value.isEmpty ? "Enter a firstname" : null,
controller: firstnameController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Firstname',
),
),
),
Container(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
child: TextFormField(
validator: (value) =>
value == null || value.isEmpty ? "Enter a lastname" : null,
controller: lastnameController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Lastname',
),
),
),
Container(
height: 50,
padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
margin: const EdgeInsets.only(top:20),
child: ElevatedButton(
child: const Text('Create', style: TextStyle(color: Colors.white)),
onPressed: () {
ref
.read(createAccountControllerProvider.notifier)
.register(emailController.text, passwordController.text, nicknameController.text,
roleController.text, firstnameController.text, lastnameController.text);
},
)),
],
)),
);
}
}
The problem is in the saveUser function. Instead of using .then use await. It would be like the following:
Future<AppUser?> saveUser(String email, String nickname, String role,
String firstname, String lastname) async {
try {
await connection.open();
final result = await connection.mappedResultsQuery(
'insert into public.user(email,nickname,role,firstname,lastname) '
'values(#emailValue,#nicknameValue,#roleValue,#firstnameValue,#lastnameValue) '
'returning *',
substitutionValues: {
'emailValue': email,
'nicknameValue': nickname,
'roleValue': role,
'firstnameValue': firstname,
'lastnameValue': lastname,
},
allowReuse: true,
timeoutInSeconds: 30,
);
final userFromDataBase = result[0]['user']!;
return AppUser(
email: userFromDataBase['email'],
nickname: userFromDataBase['nickname'],
role: userFromDataBase['role'],
firstname: userFromDataBase['firstname'],
lastname: userFromDataBase['lastname'],
);
} catch (e) {
print(ErrorHandler(message: e.toString()));
return null;
}
}
Related
I was following this tutorial on Youtube on how to create a database in Flutter app, I literally followed all the instructions on the tutorial but I keep getting this exception whenever I try to click the save button to save a new note to the database which indicates that NOT NULL constraint failed.
here
here is what my files look like:
note_model.dart:
class Note {
final int? id;
final String title;
final String content;
const Note({required this.title, required this.content, this.id});
factory Note.fromJson(Map<String, dynamic> json) => Note(
id: json['id'],
title: json['title'],
content: json['content'],
);
Map<String, dynamic> toJson() => {
'id': id,
'title': title,
'content': content,
};
#override
String toString() {
return "id: $id \n title: $title \n content: $content \n";
}
}
database_help:
import 'package:dummy_database/models/note_model.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DatabaseHelper {
static const int _version = 1;
static const String _dbName = "Notes.db";
static Future<Database> _getDB() async {
return openDatabase(join(await getDatabasesPath(), _dbName),
onCreate: (db, version) async => await db.execute(
"CREATE TABLE Note(id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL);"),
version: _version);
}
static Future<int> addNote(Note note) async {
final db = await _getDB();
return await db.insert("Note", note.toJson(),
conflictAlgorithm: ConflictAlgorithm.replace);
}
static Future<int> updateNote(Note note) async {
final db = await _getDB();
return await db.update("Note", note.toJson(),
where: 'id = ?',
whereArgs: [note.id],
conflictAlgorithm: ConflictAlgorithm.replace);
}
static Future<int> deleteNote(Note note) async {
final db = await _getDB();
return await db.delete(
"Note",
where: 'id = ?',
whereArgs: [note.id],
);
}
static Future<List<Note>?> getAllNotes() async {
final db = await _getDB();
final List<Map<String, dynamic>> maps = await db.query("Note");
if (maps.isEmpty) {
return null;
}
return List.generate(maps.length, (index) => Note.fromJson(maps[index]));
}
}
***here is the file that causing the exception: ***
import 'dart:developer';
import 'package:dummy_database/models/note_model.dart';
import 'package:dummy_database/services/database_helper.dart';
import 'package:flutter/material.dart';
class NoteScreen extends StatelessWidget {
final Note? note;
NoteScreen({Key? key, this.note}) : super(key: key);
final titleController = TextEditingController();
final contentController = TextEditingController();
#override
Widget build(BuildContext context) {
if (note != null) {
titleController.text = note!.title;
contentController.text = note!.content;
}
return Scaffold(
appBar: AppBar(
title: Text(note == null ? 'Add a note' : 'Edit note'),
centerTitle: true,
),
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 30),
child: Column(
children: [
const Padding(
padding: EdgeInsets.only(bottom: 40),
child: Center(
child: Text(
'What are you thinking about?',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
),
Padding(
padding: const EdgeInsets.only(bottom: 40.0),
child: TextFormField(
controller: titleController,
maxLines: 1,
decoration: const InputDecoration(
hintText: 'Title',
labelText: 'Note title',
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
width: 0.75,
),
borderRadius: BorderRadius.all(
Radius.circular(10.0),
))),
),
),
TextFormField(
controller: contentController,
decoration: const InputDecoration(
hintText: 'Type here the note',
labelText: 'Note content',
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
width: 0.75,
),
borderRadius: BorderRadius.all(
Radius.circular(10.0),
))),
keyboardType: TextInputType.multiline,
onChanged: (str) {},
maxLines: 5,
),
const Spacer(),
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: SizedBox(
height: 45,
width: MediaQuery.of(context).size.width,
child: ElevatedButton(
onPressed: () async {
final title = titleController.value.text;
final content = contentController.value.text;
log("title value is $title, \n content value is $content");
if (title.isEmpty || content.isEmpty) {
return;
}
final Note model =
Note(title: title, content: content, id: note?.id);
log(model.toString());
if (note == null) {
await DatabaseHelper.addNote(model);
} else {
await DatabaseHelper.updateNote(model);
}
Navigator.pop(context);
},
style: ButtonStyle(
shape: MaterialStateProperty.all(
const RoundedRectangleBorder(
side: BorderSide(
color: Colors.white,
width: 0.75,
),
borderRadius: BorderRadius.all(
Radius.circular(10.0),
)))),
child: Text(
note == null ? 'Save' : 'Edit',
style: const TextStyle(fontSize: 20),
)),
),
)
],
),
),
);
}
}
I have a basic email contact form that I got from online and I'm trying to send a sample email but some reason I can not enter any text in my input filed. Any suggestion or idea what it might be?
I can enter a text when ever I delete my _formKey but I can not send the email successfully anymore.
Any suggestion or help will be really appreciated.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:email_validator/email_validator.dart';
import 'package:http/http.dart' as http;
class EmailForm extends StatelessWidget {
EmailForm({Key? key}) : super(key: key);
final _formKey = GlobalKey<FormState>();
final nameController = TextEditingController();
final emailController = TextEditingController();
final messageController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xfff5f5fd),
body: Center(
child: Container(
height: 450,
width: 400,
margin: const EdgeInsets.symmetric(
horizontal: 40,
vertical: 20,
),
padding: const EdgeInsets.symmetric(
horizontal: 40,
vertical: 20,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
offset: const Offset(0, 5),
blurRadius: 10,
spreadRadius: 1,
color: Colors.grey[300]!)
]),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Text('Contact Us',
style:
TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
TextFormField(
controller: nameController,
decoration: const InputDecoration(hintText: 'Name'),
validator: (value) {
if (value == null || value.isEmpty) {
return '*Required';
}
return null;
},
),
TextFormField(
controller: emailController,
decoration: const InputDecoration(hintText: 'Email'),
validator: (email) {
if (email == null || email.isEmpty) {
return 'Required*';
} else if (!EmailValidator.validate(email)) {
return 'Please enter a valid Email';
}
return null;
},
),
TextFormField(
controller: messageController,
decoration: const InputDecoration(hintText: 'Message'),
maxLines: 5,
validator: (value) {
if (value == null || value.isEmpty) {
return '*Required';
}
return null;
},
),
SizedBox(
height: 45,
width: 110,
child: TextButton(
style: TextButton.styleFrom(
primary: Colors.white,
backgroundColor: const Color(0xff151534),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(40))),
onPressed: () async {
if (_formKey.currentState!.validate()) {
final response = await sendEmail(
nameController.value.text,
emailController.value.text,
messageController.value.text);
ScaffoldMessenger.of(context).showSnackBar(
response == 200
? const SnackBar(
content: Text('Message Sent!'),
backgroundColor: Colors.green)
: const SnackBar(
content: Text('Failed to send message!'),
backgroundColor: Colors.red),
);
nameController.clear();
emailController.clear();
messageController.clear();
}
},
child: const Text('Send', style: TextStyle(fontSize: 16)),
),
),
],
),
),
),
),
);
}
}
Future sendEmail(String name, String email, String message) async {
final url = Uri.parse('https://api.emailjs.com/api/v1.0/email/send');
final response = await http.post(url,
headers: {'Content-Type': 'application/json'},
body: json.encode({
'service_id': '',
'template_id': '',
'user_id': '',
'template_params': {
'from_name': name,
'from_email': email,
'message': message
}
}));
return response.statusCode;
}
When you're using a Form Widget, you don't necessarily need to use controllers you can used the onchange function in your TextFormField Widget.
Instead of defining TextEditingControllers like this:
final nameController = TextEditingController();
final emailController = TextEditingController();
Do this instead:
var name = '';
var email = '';
Instead of
TextFormField(
controller: nameController,
decoration: const InputDecoration(hintText: 'Name'),
validator: (value) {
if (value == null || value.isEmpty) {
return '*Required';
}
return null;
},
),
Do this:
TextFormField(
decoration: const InputDecoration(hintText: 'Name'),
validator: (value) {
if (value == '') {
return '*Required';
}
return null;
},
onChanged: (value) {
name = value; //If it gives some null check error just add an exclamation like this - name = value!;
}
),
Now you just have to pass these variables in your request just like you use any normal variables. Instead of using nameController.text, just name (variable created in the beginning) is enough. This should make the form work perfectly.
If you are not getting 200 status then the problem could also be the difference between the data you're sending and the data that is expected to receive on the server.
To figure out the root cause, I would recommend you to add Print Statements everywhere to see which functions are running properly and which are not.
Printing the error statements coming from the server might help in understanding the root problem. So try this before returning statusCode
print(response.body); //Add this line for the output from the server
return response.statusCode;
And let me know what reply you get in the console.
Future sendEmail(String name, String email, String message) async {
final url = Uri.parse('https://api.emailjs.com/api/v1.0/email/send%27);
const serviceId = 'Enter your service Id';
const templateId = 'Enter your template Id';
const userId = 'Enter your User Id';
final response = await http.post(url,
headers: {'Content-Type': 'application/json'},//This line makes sure it works for all platforms.
body: json.encode({
'service_id': serviceId,
'template_id': templateId,
'user_id': userId,
'template_params': {
'from_name': name,
'from_email': email,
'message': message
}
})
);
print (response.body);
return response.statusCode;
}
You will get Service Id, template Id and User Id from the account that you have created on MailJS.
My statenotifier does not rebuild when the map changes. I think the issue is that the map is inside another class. I use riverpod and i've seen it work with lists so I tested with a List inside a class and filter rebuilds to that class. But no success
StateNotifier using copyWith to replace a map does not trigger rebuild
class Admin {
const Admin({
this.admin = const {},
});
final Map<int, List<String>> admin;
Admin copyWith({
Map<int, List<String>>? admin,
}) {
return Admin(
admin: admin ?? this.admin,
);
}
}
class Member {
const Member({
this.wordOfTheDay = "",
this.admin = const Admin(),
});
final Admin admin;
final String wordOfTheDay;
Member copyWith({
Admin? admin,
String? wordOfTheDay,
}) {
return Member(
admin: admin ?? this.admin,
wordOfTheDay: wordOfTheDay ?? this.wordOfTheDay,
);
}
}
class MemberNotifier extends StateNotifier<AsyncValue<Member>> {
MemberNotifier() : super(const AsyncValue.loading());
final Map<int, List<String>> admins = {};
void loadMembers(String id, String item) {
admins[id]!.add(item);
state = state.whenData((value) => value.copyWith(admin: state.value!.admin.copyWith(admin: admins)));
}
}
final watch = ref.watch(memberNotifierProvider.select((member) => member.value!.admin));
We can pass the Map values and set the values in Map<String,dynamic> like this:-
Provider
final counterProvider =
StateNotifierProvider<Counter, Map<String, dynamic>>((_) => Counter());
class Counter extends StateNotifier<Map<String, dynamic>> {
// Counter(Map<String, dynamic> data): super(data);
Counter() : super({});
void displayFile(String name, String email, String address) {
Map<String, dynamic> cartMap = {"name": name, "email": email, "address": address};
state = {...state, ...cartMap};
}
}
After that we can fetch the values of Map using this way in Riverpod with HooksConsumerWidget:-
class MyHomePage extends HookConsumerWidget {
MyHomePage({Key? key}) : super(key: key);
TextEditingController nameController = TextEditingController();
TextEditingController emailController = TextEditingController();
TextEditingController addressController = TextEditingController();
#override
Widget build(BuildContext context, WidgetRef ref) {
final dataValue = ref.watch(counterProvider);
String name = dataValue['name'].toString();
String email = dataValue['email'].toString();
String address = dataValue['address'].toString();
print(dataValue);
return Scaffold(
appBar: AppBar(
title: const Text(' demo'),
),
body: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
children: [
TextFormField(
controller: nameController,
keyboardType: TextInputType.text,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter name',
),
),
const SizedBox(
height: 10,
),
TextFormField(
controller: emailController,
keyboardType: TextInputType.text,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter email',
),
),
const SizedBox(
height: 10,
),
TextFormField(
controller: addressController,
keyboardType: TextInputType.text,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter address',
),
),
const SizedBox(
height: 10,
),
ElevatedButton(
onPressed: () {
ref.read(counterProvider.notifier).displayFile(
nameController.text,
emailController.text,
addressController.text);
},
child: const Text('Show Details')),
const SizedBox(
height: 20,
),
Text(name),
const SizedBox(
height: 10,
),
Text(email),
const SizedBox(
height: 10,
),
Text(address),
],
),
));
}
}
ive been getting this error for days now but still cant figure it out. My objective is to put message response everytime user enter the same email into the snackbar but what i get is a null.
This is my signup screen.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:form_field_validator/form_field_validator.dart';
import 'package:formvalid/models/register_model.dart';
import 'package:http/http.dart' as http;
import 'package:formvalid/progressHUD.dart';
import 'package:formvalid/screens/login.dart';
class SignupScreen extends StatefulWidget {
const SignupScreen({Key? key}) : super(key: key);
#override
_SignupScreenState createState() => _SignupScreenState();
}
class RegisterAPI {
Future<RegisterResponse> register(
String name, String email, String password) async {
String url = 'http://api.staging.tarsoft.co/api/register';
final response = await http.post(Uri.parse('$url'),
body: {'name': name, 'email': email, 'password': password});
if (response.statusCode == 200 || response.statusCode == 401) {
print(response.body);
// print(response.statusCode);
return RegisterResponse.fromJson(json.decode(response.body));
} else {
print(response.body);
throw Exception('Failed to load data');
}
}
}
class _SignupScreenState extends State<SignupScreen> {
TextEditingController passController = new TextEditingController();
TextEditingController emailController = new TextEditingController();
TextEditingController nameController = new TextEditingController();
GlobalKey<FormState> formKey = GlobalKey<FormState>();
final scaffoldKey = GlobalKey<FormState>();
late RegisterResponse _user;
bool hidePassword = true;
bool isApiCallprocess = false;
#override
void initState() {
super.initState();
_user = new RegisterResponse();
}
#override
Widget build(BuildContext context) {
return ProgressHUD(
child: _uibuild(context),
inAsyncCall: isApiCallprocess,
opacity: 0.3,
);
}
Widget _uibuild(BuildContext context) {
final double height = MediaQuery.of(context).size.height;
return Scaffold(
appBar: AppBar(
key: scaffoldKey,
backgroundColor: Colors.transparent,
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.black),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => LoginScreen()));
},
)),
backgroundColor: Color(0xFFffffff),
body: SingleChildScrollView(
child: Form(
key: formKey,
child: Container(
padding: EdgeInsets.only(left: 40, right: 40),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: height * 0.04),
Text(
'Prizes and Suprises',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: Color(0xFF363f93)),
),
Text(
'Await you !',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: Color(0xFF363f93)),
),
SizedBox(
height: height * 0.03,
),
TextFormField(
controller: nameController,
onSaved: (input) => _user.name = input,
decoration: InputDecoration(labelText: 'Enter your name'),
validator: (value) {
if (value!.isEmpty ||
!RegExp(r'^[a-z A-Z]+$').hasMatch(value)) {
return 'Enter correct name';
} else {
return null;
}
}),
SizedBox(height: height * 0.05),
TextFormField(
controller: emailController,
onSaved: (input) => _user.email = input,
decoration: InputDecoration(labelText: 'Enter your email'),
validator: MultiValidator([
RequiredValidator(errorText: 'Enter your email'),
EmailValidator(errorText: 'Not A Valid Email')
])),
SizedBox(height: height * 0.05),
TextFormField(
controller: passController,
onSaved: (input) => _user.password = input,
decoration: InputDecoration(labelText: 'Enter your password'),
validator: MultiValidator([
RequiredValidator(errorText: 'Enter your password'),
MinLengthValidator(6,
errorText: 'Should be at least 6 characters')
]),
obscureText: hidePassword,
),
SizedBox(height: height * 0.05),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: () {
final String name = nameController.text;
final String email = emailController.text;
final String password = passController.text;
if (validateAndSave()) {
setState(() {
isApiCallprocess = true;
});
RegisterAPI registerAPI = new RegisterAPI();
registerAPI
.register(name, email, password)
.then((value) => {
setState(() {
isApiCallprocess = false;
}),
if (value.token?.isNotEmpty ?? false)
{
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content: Text(
'${_user.name}')))
}
else
{
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content:
Text('${_user.message}')))
}
});
print(_user.toJson());
}
},
child: Container(
margin: EdgeInsets.only(left: 100.0),
child: Padding(
padding: const EdgeInsets.only(left: 35, top: 10),
child: Text(
'Register',
style: TextStyle(color: Colors.white),
),
),
height: 40,
width: 120,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
color: Color(0xFF363f93),
),
)),
SizedBox(
height: height * 0.09,
),
],
),
],
),
),
),
),
);
}
bool validateAndSave() {
final form = formKey.currentState;
if (form!.validate()) {
form.save();
return true;
}
return false;
}
}
//ignore: must_be_immutable
class TextInput extends StatelessWidget {
final String textString;
TextEditingController textController;
final bool obscureText;
TextInput(
{Key? key,
required this.textString,
required this.textController,
required this.obscureText})
: super(key: key);
#override
Widget build(BuildContext context) {
return TextField(
style: TextStyle(color: Color(0xFF000000)),
cursorColor: Color(0xFF9b9b9b),
controller: textController,
keyboardType: TextInputType.text,
obscureText: obscureText,
decoration: InputDecoration(
hintText: this.textString,
hintStyle: TextStyle(
color: Color(0xFF9b9b9b),
fontSize: 15,
fontWeight: FontWeight.normal),
),
);
}
}
This is my responseregister
class RegisterResponse{
String? email;
String? password;
String? name;
String? token;
String? error;
String? message;
String? success;
RegisterResponse({this.email, this.password, this.name,this.token,this.error,this.message,this.success});
factory RegisterResponse.fromJson(Map<String, dynamic> json) =>
RegisterResponse(
name: json['name'],
email: json['email'],
password: json['password'],
token: json['token'] == null ? json['token'] : '',
error: json['error'],
message: json['email'],
success: json['Success']
);
Map<String, dynamic> toJson() {
Map<String, dynamic> map = {
'name': name,
'email': email,
'password': password,
};
return map;
}
}
this is in debugger console shows "the email has already been taken"
display null
Is it possible to POST data from flutter app to woocommerce localhost using woocommerce localhost server rest api.
i have GET & POST data with private domain but i want to POST & GET data with localhost woocommerce rest api. i have setup my wordpress and woocommerce on localhost I am trying to make flutter ecommerce app and trying to GET & POST data from woocommerce localhost. but its not working and i dont want to send from private domain rest api, i can get data on postman if i select OAuth 1.0 but if i dont use OAuth 1.0 i cant get data.
Config.dart
class Config {
static String key =
'ck_00000000000000000000000000';
static String sceret =
'cs_00000000000000000000000000';
static String url = 'http://10.0.2.2:80/wordpress_new/wp-json/wc/v3/';
static String customerURL = 'customers';
}
customer.dart
class CustomerModel {
String email;
String firstName;
String lastName;
String password;
CustomerModel({
this.email,
this.firstName,
this.lastName,
this.password,
});
Map<String, dynamic> toJson() {
Map<String, dynamic> map = {};
map.addAll({
'email': email,
'first_name': firstName,
'last_name': lastName,
'password': password,
'username': email,
});
return map;
}
}
apiservice.dart
class APIService {
Future<bool> createCustomer(CustomerModel model) async {
var authToken = base64.encode(
utf8.encode(Config.key + ':' + Config.sceret),
);
bool ret = false;
try {
var response = await Dio().post(
Config.url +
Config.customerURL,
data: model.toJson(),
options: new Options(headers: {
HttpHeaders.authorizationHeader: 'Basic $authToken',
HttpHeaders.contentTypeHeader: 'application/json',
}));
if (response.statusCode == 201) {
ret = true;
}
} on DioError catch (e) {
if (e.response.statusCode == 404) {
print(e.response.statusCode);
ret = false;
} else {
print(e.message);
print(e.request);
ret = false;
}
}
return ret;
}
Future<LoginResponseModel> loginCustomer(
String username,
String password,
) async {
LoginResponseModel model;
try {
var response = await Dio().post(Config.tokenURL,
data: {
'username': username,
'password': password,
},
options: new Options(headers: {
HttpHeaders.contentTypeHeader: 'application/x-www-form-urlencoded',
}));
if (response.statusCode == 200) {
model = LoginResponseModel.fromJson(response.data);
}
} on DioError catch (e) {
print(e.message);
}
return model;
}
}
signuppage.dart
class SignupPage extends StatefulWidget {
#override
_SignupPageState createState() => _SignupPageState();
}
class _SignupPageState extends State<SignupPage> {
APIService apiService;
CustomerModel model;
GlobalKey<FormState> globalKey = GlobalKey<FormState>();
bool hidePassword = true;
bool isApiCallProcess = false;
#override
void initState() {
apiService = new APIService();
model = new CustomerModel();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.red,
automaticallyImplyLeading: true,
title: Text('Sign Up'),
),
body: ProgressHUD(
child: Form(
key: globalKey,
child: _formUI(),
),
inAsyncCall: isApiCallProcess,
opacity: 0.3),
);
}
Widget _formUI() {
return SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(10.00),
child: Container(
child: Align(
alignment: Alignment.topLeft,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
FormHelper.fieldLabel('First Name'),
FormHelper.textInput(
context,
model.firstName,
(value) => {
this.model.firstName = value,
},
onValidate: (value) {
if (value.toString().isEmpty) {
return 'Please enter First Name.';
}
return null;
},
),
FormHelper.fieldLabel('Last Name'),
FormHelper.textInput(
context,
model.lastName,
(value) => {
this.model.lastName = value,
},
onValidate: (value) {
return null;
},
),
FormHelper.fieldLabel('Email Id'),
FormHelper.textInput(
context,
model.email,
(value) => {
this.model.email = value,
},
onValidate: (value) {
if (value.toString().isEmpty) {
return 'Please enter Email id.';
}
if (value.isNotEmpty && !value.toString().isValidEmail()) {
return 'Please enter valid email id';
}
},
),
FormHelper.fieldLabel('Password'),
FormHelper.textInput(
context,
model.password,
(value) => {
this.model.password = value,
},
onValidate: (value) {
if (value.toString().isEmpty) {
return 'Please enter Password.';
}
return null;
},
obscureText: hidePassword,
suffixIcon: IconButton(
onPressed: () {
setState(() {
hidePassword = !hidePassword;
});
},
color: Theme.of(context).accentColor.withOpacity(0.4),
icon: Icon(
hidePassword ? Icons.visibility_off : Icons.visibility,
),
),
),
SizedBox(
height: 20,
),
Center(
child: FormHelper.saveButton(
'Register',
() {
if (validateAndSave()) {
print(model.toJson());
setState(() {
isApiCallProcess = true;
});
apiService.createCustomer(model).then(
(ret) {
setState(() {
isApiCallProcess = false;
});
if (ret) {
FormHelper.showMessage(
context,
'WooCommerce App',
'Registration Successfull',
'Ok',
() {
Navigator.of(context).pop();
},
);
} else {
FormHelper.showMessage(
context,
'WooCommerce App',
'Email Id already registered.',
'Ok',
() {
Navigator.of(context).pop();
},
);
}
},
);
}
},
),
)
],
),
),
),
),
);
}
bool validateAndSave() {
final form = globalKey.currentState;
if (form.validate()) {
form.save();
return true;
}
return false;
}
}
form_helper.dart
class FormHelper {
static Widget textInput(
BuildContext context,
Object initialValue,
Function onChanged, {
bool isTextArea = false,
bool isNumberInput = false,
obscureText: false,
Function onValidate,
Widget prefixIcon,
Widget suffixIcon,
}) {
return TextFormField(
initialValue: initialValue != null ? initialValue.toString() : "",
decoration: fieldDecoration(
context,
"",
"",
suffixIcon: suffixIcon,
),
obscureText: obscureText,
maxLines: !isTextArea ? 1 : 3,
keyboardType: isNumberInput ? TextInputType.number : TextInputType.text,
onChanged: (String value) {
return onChanged(value);
},
validator: (value) {
return onValidate(value);
},
);
}
static InputDecoration fieldDecoration(
BuildContext context,
String hintText,
String helperText, {
Widget prefixIcon,
Widget suffixIcon,
}) {
return InputDecoration(
contentPadding: EdgeInsets.all(6),
hintText: hintText,
helperText: helperText,
prefixIcon: prefixIcon,
suffixIcon: suffixIcon,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).primaryColor,
width: 1,
),
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).primaryColor,
width: 1,
),
),
);
}
static Widget fieldLabel(String labelName) {
return new Padding(
padding: EdgeInsets.fromLTRB(0, 5, 0, 10),
child: Text(
labelName,
style: new TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15.0,
),
),
);
}
static Widget saveButton(String buttonText, Function onTap,
{String color, String textColor, bool fullWidth}) {
return Container(
height: 50.0,
width: 150,
child: GestureDetector(
onTap: () {
onTap();
},
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.redAccent,
style: BorderStyle.solid,
width: 1.0,
),
color: Colors.redAccent,
borderRadius: BorderRadius.circular(30.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: Text(
buttonText,
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w600,
letterSpacing: 1,
),
),
),
],
),
),
),
);
}
static void showMessage(
BuildContext context,
String title,
String message,
String buttonText,
Function onPressed,
) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(title),
content: new Text(message),
actions: [
new FlatButton(
onPressed: () {
return onPressed();
},
child: new Text(buttonText),
)
],
);
},
);
}
}
progressHUD.dart
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()),
],
);
widgetList.add(modal);
}
return Stack(
children: widgetList,
);
}
}