Sample application.

This commit is contained in:
2024-06-03 20:16:01 +05:30
parent 50a915f413
commit 2ee2a7f906
128 changed files with 5545 additions and 2 deletions

3
lib/config.dart Normal file
View File

@@ -0,0 +1,3 @@
String AuthEndpoint = 'http://192.168.1.15:8016/authclientuser';
String BaseEndpoint = 'user-graphql-apikey';
String DataEndpoint = 'http://192.168.1.15:8016/user-graphql-apikey';

View File

@@ -0,0 +1,157 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'contactService.dart';
import 'model/contactCreation.dart';
import 'dart:convert';
import 'dart:typed_data';
class ContactCreationPage extends StatefulWidget{
late ContactService contactService;
ContactCreationPage({Key? key, required this.contactService}) : super(key: key);
@override
_ContatcCreationPageState createState() => _ContatcCreationPageState();
}
class _ContatcCreationPageState extends State<ContactCreationPage> {
final TextEditingController _nameController = TextEditingController();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _phoneController = TextEditingController();
final TextEditingController _streetController = TextEditingController();
final TextEditingController _street2Controller = TextEditingController();
final TextEditingController _cityController = TextEditingController();
final ImagePicker _picker = ImagePicker();
String? _image;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Create Contact'),
),
body: SingleChildScrollView(
child:Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
controller: _nameController,
decoration: InputDecoration(labelText: 'Name'),
),
TextFormField(
controller: _emailController,
decoration: InputDecoration(labelText: 'Email'),
),
TextFormField(
controller: _phoneController,
decoration: InputDecoration(labelText: 'Phone'),
),
SizedBox(height: 16),
Text(
'Address:',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
TextFormField(
controller: _streetController,
decoration: InputDecoration(labelText: 'Street'),
),
TextFormField(
controller: _street2Controller,
decoration: InputDecoration(labelText: 'Street2'),
),
TextFormField(
controller: _cityController,
decoration: InputDecoration(labelText: 'City'),
),
FloatingActionButton(
onPressed: pickImage,
tooltip: 'Pick Image',
child: const Icon(Icons.add_a_photo),
),
Container(
child: getImage(),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () {
_saveContact(context);
},
child: Text('Save Contact'),
),
],
),
)
)
);
}
getImage() {
if (_image != null){
Uint8List imageBytes = base64.decode(_image.toString());
ImageProvider imageProvider = MemoryImage(imageBytes);
return Image(
image: imageProvider,
height: 150,
width: 150,
);
}
else{
return null;
}
}
Future pickImage() async {
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
final path = image?.path;
if (path != null){
if (kIsWeb){
final response = await fetchWebImage(path);
final imageValue = base64Encode(response);
setState(() {
_image = imageValue;
});
}
else {
final response = await File(path).readAsBytes();
final imageValue = base64Encode(response);
setState(() {
_image = imageValue;
});
}
}
}
Future<List<int>> fetchWebImage(path) async {
final response = await http.get(
Uri.parse(path),
);
return response.bodyBytes;
}
void _saveContact(BuildContext context) async {
final name = _nameController.text;
final email = _emailController.text;
final phone = _phoneController.text;
final street = _streetController.text;
final street2 = _street2Controller.text;
final city = _cityController.text;
final image1920 = _image;
if (name.isNotEmpty && email.isNotEmpty) {
final newContact = ContactCreation(
name: name, email: email, phone: phone, street: street, street2: street2, city: city, image1920: image1920);
final newCreatedContacts = await widget.contactService.contactCreateRequest(contact: newContact);
Navigator.pop(context, newCreatedContacts); // Return the new contact to the previous page
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Please enter name and email')),
);
}
}
}

210
lib/contactDetailPage.dart Normal file
View File

@@ -0,0 +1,210 @@
import 'package:flutter/material.dart';
import 'secureStorage.dart';
import 'homepage.dart';
import 'contactService.dart';
import 'model/contactDetail.dart';
import 'dart:convert';
import 'dart:typed_data';
class ContatcDetailPage extends StatefulWidget {
late ContactDetail contact ;
late ContactService contactService;
ContatcDetailPage({Key? key, required this.contact, required this.contactService}) : super(key: key);
@override
_ContatcDetailPageState createState() => _ContatcDetailPageState();
}
class _ContatcDetailPageState extends State<ContatcDetailPage> {
late TextEditingController _nameController;
late TextEditingController _emailController;
late TextEditingController _phoneController;
late TextEditingController _streetController;
late TextEditingController _street2Controller;
late TextEditingController _cityController;
late TextEditingController _countryController;
// Future<ContactDetail> getContactDetailValues() async {
// final response = await _contactService.contactDetailRequest();
// final ContactDetail contactResult = ContactDetail.fromJson(response);
// print(contactResult);
// return contactResult;
// }
@override
void initState() {
super.initState();
_nameController = TextEditingController(text: widget.contact.name.toString());
_emailController = TextEditingController(text: widget.contact.email.toString());
_phoneController = TextEditingController(text: widget.contact.phone.toString());
_streetController = TextEditingController(text: widget.contact.street.toString());
_street2Controller = TextEditingController(text: widget.contact.street2.toString());
_cityController = TextEditingController(text: widget.contact.city.toString());
_countryController = TextEditingController(text: widget.contact.country.toString());
}
@override
void dispose() {
_nameController.dispose();
_emailController.dispose();
_phoneController.dispose();
_streetController.dispose();
_cityController.dispose();
_countryController.dispose();
super.dispose();
}
getCircleAvatar(image) {
if (image != null && image != false){
Uint8List imageBytes = base64.decode(image);
ImageProvider imageProvider = MemoryImage(imageBytes);
return CircleAvatar(
backgroundImage: imageProvider,
radius: 50,
);
}
else{
return null;
}
}
Future<void> _deleteContact(BuildContext context) async {
final response = await widget.contactService.contactDeleteRequest(id: widget.contact.id);// Replace with your API endpoint
if (response['data']['delete'] == 'Success') {
final apikey = await userCreds.read(key: 'api-key');
Navigator.push(
context, MaterialPageRoute(builder: (_) => HomePage(apikey:apikey.toString()))
); // Go back to contact list page after successful delete
} else {
// Show error message if delete fails
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Error'),
content: Text('Failed to delete contact. Please try again.'),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('OK'),
),
],
),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Contact Details'),
actions: [
IconButton(
icon: Icon(Icons.save),
onPressed: () async {
// Save changes and navigate back
print('update clicked with name${widget.contact.name}');
final ContactDetail updatedrResult = await _saveUpdateChanges();
print(updatedrResult);
setState(() {
widget.contact = updatedrResult;
});
},
),
IconButton(
icon: Icon(Icons.delete),
onPressed:() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Confirm Delete'),
content: Text('Are you sure you want to delete this contact?'),
actions: [
TextButton(
onPressed: (){
Navigator.pop(context);
},
child: Text('Cancel')
),
TextButton(
onPressed: () async {
await _deleteContact(context);
},
child: Text('Delete')
)
],
)
);
},
)
]
),
body: SingleChildScrollView(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: getCircleAvatar(widget.contact.image1920),
),
SizedBox(height: 16),
TextFormField(
controller: _nameController,
decoration: InputDecoration(labelText: 'Name'),
),
TextFormField(
controller: _emailController,
decoration: InputDecoration(labelText: 'Email'),
),
TextFormField(
controller: _phoneController,
decoration: InputDecoration(labelText: 'Phone'),
),
SizedBox(height: 16),
Text(
'Address:',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
TextFormField(
controller: _streetController,
decoration: InputDecoration(labelText: 'Street'),
),
TextFormField(
controller: _street2Controller,
decoration: InputDecoration(labelText: 'Street2'),
),
TextFormField(
controller: _cityController,
decoration: InputDecoration(labelText: 'City'),
),
TextFormField(
controller: _countryController,
decoration: InputDecoration(labelText: 'Country'),
readOnly: true,
),
],
),
),
);
}
Future<ContactDetail> _saveUpdateChanges() async {
final updatedContact = ContactDetail(
id: widget.contact.id,
name: _nameController.text,
email: _emailController.text,
phone: _phoneController.text,
image1920: widget.contact.image1920,
street: _streetController.text,
street2: _street2Controller.text,
city: _cityController.text,
country: _countryController.text,
);
final newUpdatedContacts = await widget.contactService.contactUpdateRequest(contact: updatedContact);
return newUpdatedContacts;
}
}

61
lib/contactService.dart Normal file
View File

@@ -0,0 +1,61 @@
import 'graphqlScript.dart';
import 'model/contactDetail.dart';
import 'model/contactCreation.dart';
import 'config.dart';
class ContactService{
late GPLModel contactObj;
ContactService(String apikey) {
contactObj = GPLModel('res.partner', DataEndpoint, apikey);
}
Future<List<dynamic>> contactListRequest () async {
const queryDict = {'id': null, 'name': null, 'email': null, 'phone': null, 'image_1920': null};
final result = contactObj.search(domain: const [], querydict: queryDict);
return await result;
}
Future<Map<String, dynamic>> contactDetailRequest ({required int id}) async {
const queryDict = {'id': null, 'name': null, 'email': null, 'phone': null, 'street': null, 'street2': null, 'city':null,'country_id':{'name': null}, 'image_1920': null};
final result = contactObj.browse(id: id, querydict: queryDict);
return await result;
}
Future<Map<String, dynamic>> contactDeleteRequest ({required int id}) async {
final result = contactObj.delete(id: id);
return await result;
}
Future<ContactDetail> contactUpdateRequest ({required ContactDetail contact}) async {
final values = {
'name': contact.name,
'phone': contact.phone,
'email': contact.email,
'street': contact.street,
'street2': contact.street2,
'city': contact.city,
};
const queryDict = {'id': null, 'name': null, 'email': null, 'phone': null, 'street': null, 'street2': null, 'city':null,'country_id':{'name': null}, 'image_1920': null};
final result = await contactObj.write(id: contact.id, values: values, querydict: queryDict);
final ContactDetail updatedContact = ContactDetail.fromJson(result);
return updatedContact;
}
Future<ContactDetail> contactCreateRequest ({required ContactCreation contact}) async {
final values = {
'name': contact.name,
'phone': contact.phone,
'email': contact.email,
'street': contact.street,
'street2': contact.street2,
'city': contact.city,
'image_1920': contact.image1920,
};
const queryDict = {'id': null, 'name': null, 'email': null, 'phone': null, 'street': null, 'street2': null, 'city':null,'country_id':{'name': null}, 'image_1920': null};
final result = await contactObj.create(values: values, querydict: queryDict);
final ContactDetail createdContact = ContactDetail.fromJson(result);
return createdContact;
}
}

263
lib/graphqlScript.dart Normal file
View File

@@ -0,0 +1,263 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
// final String API_URL = DataEndpoint;
// final String API_KEY = '''Af+"E<y.-Rs<q9")|'%7A&@U.fU}JV~:''';
class SyncError implements Exception {
final String message;
SyncError([this.message = 'Connection not established.']);
@override
String toString() {
return 'Sync Error: $message';
}
}
class EasyAPIClient {
late String apiurl;
late String apikey;
late Map<String, String> headers;
EasyAPIClient(apiurl, apikey) {
this.apiurl = apiurl;
this.apikey = apikey;
this.headers = {
'x-api-key': apikey,
'Content-Type': 'application/json',
};
}
Future<http.Response> post(String query, [Map<String, dynamic> variables = const {}]) async {
final allbodyparts = {'query': query, 'variables': variables};
final body = Map.fromEntries(allbodyparts.entries.where((entry) => entry.value != null));
return await http.post(Uri.parse(this.apiurl), headers: this.headers, body: jsonEncode(body));
}
}
String pyDictToGPLString(dynamic values, [int indent = 0]) {
String formatValue(dynamic value, [int indent = 0]) {
if (value is bool) {
return value.toString().toLowerCase();
} else if (value is String) {
return '"$value"';
} else if (value is List) {
final items = value.map((item) => formatValue(item, indent)).join(', ');
return '[$items]';
} else if (value is Map) {
final items = value.entries.map((entry) => ' ' * (indent + 1) + '${entry.key}: ${formatValue(entry.value, indent + 1)}').join(',\n');
return '{\n$items\n${' ' * indent}}';
} else {
return value.toString();
}
}
final formattedValues = formatValue(values, indent);
return formattedValues;
}
String pyDictToGPLQueryString(Map<String, dynamic> querydict, [int indent = 1]) {
String generateQueryString(Map<String, dynamic> querydict, [int indent = 1]) {
var queryString = '';
querydict.forEach((key, value) {
if (value == null) {
queryString += ' ' * indent + '$key\n';
} else if (value is Map<String, dynamic>) {
queryString += ' ' * indent + '$key {\n';
queryString += generateQueryString(value, indent + 1);
queryString += ' ' * indent + '}\n';
} else {
throw ArgumentError('Invalid querydict format');
}
});
return queryString;
}
final queryString = generateQueryString(querydict, indent);
return '{\n$queryString${' ' * (indent - 1)}}';
}
class GPLModel {
late EasyAPIClient _syncapi;
final String omodel;
late String gpl_model;
GPLModel(this.omodel, String apiurl, String apikey) {
if (omodel.isNotEmpty) {
gpl_model = convertToCamelCase(omodel);
_syncapi = EasyAPIClient(apiurl, apikey);
}
else {
gpl_model = "";
}
}
String convertToCamelCase(String input) {
List<String> parts = input.split('.');
List<String> capitalizedParts = parts.map((part) {
if (part.isNotEmpty) {
return part[0].toUpperCase() + part.substring(1);
} else {
return '';
}
}).toList();
return capitalizedParts.join();
}
Future<Map<String, dynamic>> browse({required int id, Map<String, dynamic> querydict = const {'id': null}}) async {
final gplquery = pyDictToGPLQueryString(querydict, 2);
final browseBody = '''query MyQuery {
$gpl_model(id: "$id") $gplquery
}''';
final response = await _syncapi.post(browseBody);
final data = json.decode(response.body)['data'];
return data[gpl_model][0];
}
Future<List<dynamic>> search({List<List<dynamic>> domain = const [], String order = 'name', int limit = 80, int offset = 0, Map<String, dynamic> querydict = const {'id': null}}) async {
final gplquery = pyDictToGPLQueryString(querydict, 2);
final searchBody = '''query MyQuery(\$offset: Int, \$limit: Int, \$order: String, \$domain: [[Any]]) {
$gpl_model(
offset: \$offset
limit: \$limit
order: \$order
domain: \$domain
) $gplquery
}''';
final allVariables = {
'domain': domain,
'order': order,
'limit': limit,
'offset': offset,
};
final response = await _syncapi.post(searchBody, allVariables);
final data = json.decode(response.body)['data'];
return data[gpl_model];
}
Future<Map<String, dynamic>> create({Map<String, dynamic> values = const {}, Map<String, dynamic> querydict = const {'id': null}}) async {
final gplvalues = pyDictToGPLString(values, 2);
final gplquery = pyDictToGPLQueryString(querydict, 2);
print(gpl_model);
final createBody = '''mutation Create {
create$gpl_model: $gpl_model(
$gpl_model\Values: $gplvalues
) $gplquery
}''';
final response = await _syncapi.post(createBody);
final data = json.decode(response.body)['data'];
return data['create$gpl_model'][0];
}
Future<Map<String, dynamic>> write({required int id, required Map<String, dynamic> values, Map<String, dynamic> querydict = const {'id': null}}) async {
final gplvalues = pyDictToGPLString(values, 2);
final gplquery = pyDictToGPLQueryString(querydict, 2);
final writeBody = '''mutation Update {
update$gpl_model: $gpl_model(
id: $id,
$gpl_model\Values: $gplvalues
) $gplquery
}''';
final response = await _syncapi.post(writeBody);
final data = json.decode(response.body)['data'];
return data['update$gpl_model'][0];
}
Future<Map<String, dynamic>> delete({required int id, Map<String, dynamic> querydict = const {'id': null}}) async {
final gplquery = pyDictToGPLQueryString(querydict, 2);
final deleteBody = '''mutation Delete {
delete$gpl_model: $gpl_model(
id: $id
) $gplquery
}''';
final response = await _syncapi.post(deleteBody);
final data = json.decode(response.body);
return data;
}
Future<Map<String, dynamic>> methodExecute(int id, String methodName, Map<String, dynamic> methodParameters) async {
final methodExecuteBody = '''mutation Method {
method$gpl_model: $gpl_model(
id: $id,
method_name: $methodName,
method_parameters: ${pyDictToGPLString(methodParameters)}
)
}''';
final response = await _syncapi.post(methodExecuteBody);
final data = json.decode(response.body);
return data;
}
}
Future<dynamic> makeApiLoginRequest({required String username,required String password, required baseEndpoint, required authEndpoint}) async {
Map<String, dynamic> jsonData = {
'base_endpoint': baseEndpoint,
};
String basicAuth = 'Basic ' + base64Encode(utf8.encode('$username:$password'));
Map<String, String> headers = {
'Content-Type': 'application/json',
'Authorization': basicAuth,
};
String jsonEncoded = json.encode(jsonData);
try{
var response = await http.post(
Uri.parse(authEndpoint),
headers: headers,
body: jsonEncoded,
);
return response;
} catch(e) {
print(e);
}
}
// final GPLModel resObj = GPLModel('res.partner');
// void main() {
// // For normal query operation
// var query_dict = {'id': null, 'name': null, 'country_id': {'id': null, 'name': null}, 'mobile': null};
// // final result = resObj.search(domain: const [], querydict: query_dict);
// final result = resObj.browse(1079, query_dict);
// // final create_values = {
// // 'active': true,
// // 'name': 'Ekika Corporation PVT LTD.',
// // 'bank_ids': [
// // [0, 0, {
// // 'sequence': 10,
// // 'bank_id': 2,
// // 'acc_number': '1111111',
// // 'allow_out_payment': true,
// // 'acc_holder_name': false,
// // }],
// // [0, 0, {
// // 'sequence': 10,
// // 'bank_id': 3,
// // 'acc_number': '9999999999999',
// // 'allow_out_payment': true,
// // 'acc_holder_name': false,
// // }]
// // ],
// // 'city': 'Gandhinagar',
// // 'zip': '382421',
// // 'comment': '<h3>Comment Here</h3>',
// // 'mobile': '888888888',
// // };
// // final result = resObj.create(values: create_values, querydict: query_dict);
// // final write_values = {
// // 'name': 'Ekika Corporation',
// // 'mobile': 111111111,
// // };
// // final result = resObj.write(1115, write_values,query_dict);
// // final result = resObj.delete(1116);
// // final result = resObj.methodExecute(4, 'parameter_test_method', {'key_3': 'new_val_3', 'key_2': 'new_val_2', 'key_1': 'new_val_1'});
// result.then((value) => print(value));
// // You can call other functions here like resWriteOne.
// }

132
lib/homepage.dart Normal file
View File

@@ -0,0 +1,132 @@
import 'package:flutter/material.dart';
import 'package:odoo_contact_app/secureStorage.dart';
import 'main.dart';
import 'contactService.dart';
import 'model/contactList.dart';
import 'model/contactDetail.dart';
import 'contactDetailPage.dart';
import 'contactCreationPage.dart';
import 'dart:convert';
import 'dart:typed_data';
class HomePage extends StatefulWidget {
final String apikey ;
HomePage({Key? key, required this.apikey}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late ContactService contactService;
late Future<List<ContactList>> contactResultFuture;
Future<List<ContactList>> getContactValues() async {
final response = await contactService.contactListRequest();
final List<ContactList>contactResult = response.map((json) => ContactList.fromJson(json)).toList();
return contactResult;
}
@override
void initState() {
super.initState();
contactService = ContactService(widget.apikey);
contactResultFuture = getContactValues();
}
getCircleAvatar(image) {
if (image != null && image != false){
Uint8List imageBytes = base64.decode(image);
ImageProvider imageProvider = MemoryImage(imageBytes);
return CircleAvatar(
backgroundImage: imageProvider,
);
}
else{
return null;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Contact List'),
actions: [
TextButton(
child: Text('Create'),
onPressed: () async {
// Save changes and navigate back
_openContactCreationForm();
},
),
TextButton(
child: Text('Logout'),
onPressed: () async {
// Save changes and navigate back
print('Create Clicked');
_performLogoutOperation(context);
},
),
]
),
body: FutureBuilder(
future: contactResultFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
} else {
final contacts = snapshot.data!;
return ListView.builder(
itemCount: contacts.length,
itemBuilder: (context, index) {
final contact = contacts[index];
return ListTile(
leading: getCircleAvatar(contact.image1920),
title: Text(contact.name),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(contact.email.toString()),
// Text(contact.phone),
],
),
onTap: () async {
contactService.contactDetailRequest(id: contact.id).then((value){
final ContactDetail contactResult = ContactDetail.fromJson(value);
Navigator.push(
context, MaterialPageRoute(builder: (_) => ContatcDetailPage(contact: contactResult, contactService: contactService,)));
});
},
);
},
);
}
},
),
);
}
_openContactCreationForm() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ContactCreationPage(contactService: contactService,)),
).then((newContact) {
if (newContact != null) {
setState(() {
contactResultFuture = getContactValues(); // Add new contact to the list
});
}
});
}
void _performLogoutOperation(BuildContext context) async {
await userCreds.deleteAll(); // Reset user credentials
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => LoginPage()),
);
}
}

152
lib/main.dart Normal file
View File

@@ -0,0 +1,152 @@
import 'package:flutter/material.dart';
import 'package:odoo_contact_app/config.dart';
import 'secureStorage.dart';
import 'graphqlScript.dart';
import 'homepage.dart';
import 'dart:convert';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: LoginPage(),
);
}
}
class LoginPage extends StatefulWidget {
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
TextEditingController _loginFieldController = TextEditingController();
TextEditingController _passwordFieldController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("Login Page"),
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 60.0),
child: Center(
child: Container(
width: 200,
height: 150,
/*decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(50.0)),*/
child: Image.network('https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg')),
),
),
Padding(
//padding: const EdgeInsets.only(left:15.0,right: 15.0,top:0,bottom: 0),
padding: EdgeInsets.symmetric(horizontal: 15),
child: TextField(
controller: _loginFieldController,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Login',
hintText: 'Enter Login'),
),
),
Padding(
padding: const EdgeInsets.only(
left: 15.0, right: 15.0, top: 15, bottom: 0),
//padding: EdgeInsets.symmetric(horizontal: 15),
child: TextField(
controller: _passwordFieldController,
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
hintText: 'Enter password'),
),
),
TextButton(
onPressed: (){
//TODO FORGOT PASSWORD SCREEN GOES HERE
},
child: Text(
'Forgot Password',
style: TextStyle(color: Colors.blue, fontSize: 15),
),
),
Container(
height: 50,
width: 250,
decoration: BoxDecoration(
color: Colors.blue, borderRadius: BorderRadius.circular(20)),
child: TextButton(
onPressed: () async {
final loginRequest = makeApiLoginRequest(
username: _loginFieldController.text, password: _passwordFieldController.text,
baseEndpoint: BaseEndpoint, authEndpoint: AuthEndpoint
);
// Retrieve API key from secure storage
loginRequest.then((value){
if (value.statusCode == 200) {
final String apikey = json.decode(value.body)['your-api-key'];
userCreds.write(key: 'api-key', value: apikey).then((val) {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (_) => HomePage(apikey: apikey)));
},);
}
else {
showAlertBox();
}
}).catchError((onError){
showAlertBox();
});
},
child: Text(
'Login',
style: TextStyle(color: Colors.white, fontSize: 25),
),
),
),
SizedBox(
height: 130,
),
Text('New User? Create Account')
],
),
),
);
}
showAlertBox() {
return showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text("Invalid Credentials"),
content: const Text("Provide correct login and password"),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(ctx).pop();
},
child: Container(
color: Color.fromARGB(255, 247, 148, 0),
padding: const EdgeInsets.all(14),
child: const Text("Ok"),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,50 @@
class ContactCreation {
final dynamic name;
final dynamic email;
final dynamic phone;
final dynamic street;
final dynamic street2;
final dynamic city;
final dynamic image1920;
ContactCreation({
required this.name,
required this.email,
required this.phone,
required this.street,
required this.street2,
required this.city,
required this.image1920,
});
factory ContactCreation.fromJson(Map<dynamic, dynamic> json) {
getCountry(){
if (json['country_id'] != null){
return json['country_id']['name'];
}
else{
return null;
}
}
return ContactCreation(
name: json['name'],
email: json['email'],
phone: json['phone'],
street: json['street'],
street2: json['street2'],
city: json['city'],
image1920: json['image_1920'],
);
}
Map<dynamic, dynamic> toJson() {
return {
'name': name,
'email': email,
'phone': phone,
'street': street,
'street2': street2,
'city': city,
'image_1920': image1920,
};
}
}

View File

@@ -0,0 +1,58 @@
class ContactDetail {
final int id;
final dynamic name;
final dynamic email;
final dynamic phone;
final dynamic image1920;
final dynamic street;
final dynamic street2;
final dynamic city;
final dynamic country;
ContactDetail({
required this.id,
required this.name,
required this.email,
required this.phone,
required this.image1920,
required this.street,
required this.street2,
required this.city,
required this.country,
});
factory ContactDetail.fromJson(Map<dynamic, dynamic> json) {
getCountry(){
if (json['country_id'] != null){
return json['country_id']['name'];
}
else{
return null;
}
}
return ContactDetail(
id: json['id'],
name: json['name'],
email: json['email'],
phone: json['phone'],
image1920: json['image_1920'],
street: json['street'],
street2: json['street2'],
city: json['city'],
country: getCountry(),
);
}
Map<dynamic, dynamic> toJson() {
return {
'id': id,
'name': name,
'email': email,
'phone': phone,
'image1920': image1920,
'street': street,
'street2': street2,
'city': city,
'country': country,
};
}
}

View File

@@ -0,0 +1,27 @@
class ContactList {
final int id;
final String name;
final dynamic email;
final dynamic image1920;
ContactList({required this.id, required this.name, required this.email, required this.image1920});
factory ContactList.fromJson(Map<String, dynamic> json) {
return ContactList(
id: json['id'],
name: json['name'],
email: json['email'],
image1920: json['image_1920'],
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'email': email,
'image1920': image1920,
};
}
}

8
lib/secureStorage.dart Normal file
View File

@@ -0,0 +1,8 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
// Create storage
final userCreds = new FlutterSecureStorage();
Future getUserCredsValues(String key) async {
return await userCreds.read(key: key);
}