Auto complete textfield with suggestions from sqlite database. Show one of the data from salute database as title and another as subtitle in the listtile of suggestions. I have used the sqlite plugin
Data model
class Note {
int _id;
String _title;
String _description;
var _chatEnabled;
var _liveEnabled;
var _recordEnabled;
var _raiseEnabled;
var _shareYtEnabled;
var _kickOutEnabled;
String _time;
String _host;
Note(this._title, this._description, this._time, this._chatEnabled, this._liveEnabled, this._recordEnabled, this._raiseEnabled, this._shareYtEnabled, this._kickOutEnabled, this._host);
Note.map(dynamic obj) {
this._id = obj['id'];
this._title = obj['title'];
this._description = obj['description'];
this._time = obj['time'];
this._chatEnabled = obj['chatEnabled'];
this._liveEnabled = obj['liveEnabled'];
this._recordEnabled = obj['recordEnabled'];
this._raiseEnabled = obj['raiseEnabled'];
this._shareYtEnabled = obj['shareYtEnabled'];
this._kickOutEnabled = obj['kickOutEnabled'];
this._host = obj['host'];
}
int get id => _id;
String get title => _title;
String get description => _description;
String get time => _time;
int get chatEnabled => _chatEnabled;
int get liveEnabled => _liveEnabled;
int get recordEnabled => _recordEnabled;
int get raiseEnabled => _raiseEnabled;
int get shareYtEnabled => _shareYtEnabled;
int get kickOutEnabled => _kickOutEnabled;
String get host => _host;
Map<String, dynamic> toMap() {
var map = new Map<String, dynamic>();
if (_id != null) {
map['id'] = _id;
}
map['title'] = _title;
map['description'] = _description;
map['time'] = _time;
map['chatEnabled'] = _chatEnabled;
map['liveEnabled'] = _liveEnabled;
map['recordEnabled'] = _recordEnabled;
map['raiseEnabled'] = _raiseEnabled;
map['shareYtEnabled'] = _shareYtEnabled;
map['kickOutEnabled'] = _kickOutEnabled;
map['host'] = _host;
return map;
}
Note.fromMap(Map<String, dynamic> map) {
this._id = map['id'];
this._title = map['title'];
this._description = map['description'];
this._time = map['time'];
this._chatEnabled = map['chatEnabled'];
this._liveEnabled = map['liveEnabled'];
this._recordEnabled = map['recordEnabled'];
this._raiseEnabled = map['raiseEnabled'];
this._shareYtEnabled = map['shareYtEnabled'];
this._kickOutEnabled = map['kickOutEnabled'];
this._host = map['host'];
}
}
DB manager
class DatabaseHelper {
static final DatabaseHelper _instance = new DatabaseHelper.internal();
factory DatabaseHelper() => _instance;
final String tableRecentJ = 'jrecentTable';
final String columnId = 'id';
final String columnTitle = 'title';
final String columnDescription = 'description';
final String columnTime = 'time';
final String chatEnabled = 'chatEnabled';
final String liveEnabled = 'liveEnabled';
final String recordEnabled = 'recordEnabled';
final String raiseEnabled = 'raiseEnabled';
final String shareYtEnabled = 'shareYtEnabled';
final String kickOutEnabled = 'kickOutEnabled';
final String host = 'host';
static Database _db;
DatabaseHelper.internal();
Future<Database> get db async {
if (_db != null) {
return _db;
}
_db = await initDb();
return _db;
}
initDb() async {
String databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'jrecent.db');
// await deleteDatabase(path); // just for testing
var db = await openDatabase(path, version: 1, onCreate: _onCreate);
return db;
}
void _onCreate(Database db, int newVersion) async {
await db.execute(
'CREATE TABLE $tableRecentJ($columnId INTEGER PRIMARY KEY, $columnTitle TEXT, $columnDescription TEXT, $columnTime TEXT, $chatEnabled INTEGER, $liveEnabled INTEGER, $recordEnabled INTEGER, $raiseEnabled INTEGER, $shareYtEnabled INTEGER, $kickOutEnabled INTEGER, $host TEXT)');
}
Future<int> saveNote(Note note) async {
var dbClient = await db;
var result = await dbClient.insert(tableRecentJ, note.toMap());
// var result = await dbClient.rawInsert(
// 'INSERT INTO $tableNote ($columnTitle, $columnDescription) VALUES (\'${note.title}\', \'${note.description}\')');
return result;
}
Future<List> getAllNotes() async {
var dbClient = await db;
var result = await dbClient.query(tableRecentJ, columns: [columnId, columnTitle, columnDescription, columnTime, chatEnabled, liveEnabled, recordEnabled, raiseEnabled, shareYtEnabled, kickOutEnabled, host]);
// var result = await dbClient.rawQuery('SELECT * FROM $tableNote');
return result.toList();
}
Future<int> getCount() async {
var dbClient = await db;
return Sqflite.firstIntValue(await dbClient.rawQuery('SELECT COUNT(*) FROM $tableRecentJ'));
}
Future<Note> getNote(int id) async {
var dbClient = await db;
List<Map> result = await dbClient.query(tableRecentJ,
columns: [columnId, columnTitle, columnDescription, columnTime, chatEnabled, liveEnabled, recordEnabled, raiseEnabled, shareYtEnabled, kickOutEnabled, host],
where: '$columnId = ?',
whereArgs: [id]);
// var result = await dbClient.rawQuery('SELECT * FROM $tableNote WHERE $columnId = $id');
if (result.length > 0) {
return new Note.fromMap(result.first);
}
return null;
}
Future<int> deleteNote(int id) async {
var dbClient = await db;
return await dbClient.delete(tableRecentJ, where: '$columnId = ?', whereArgs: [id]);
// return await dbClient.rawDelete('DELETE FROM $tableNote WHERE $columnId = $id');
}
Future<int> cleanNote() async {
var dbClient = await db;
return await dbClient.delete(tableRecentJ);
// return await dbClient.rawDelete('DELETE FROM $tableNote WHERE $columnId = $id');
}
Future<int> updateNote(Note note) async {
var dbClient = await db;
return await dbClient.update(tableRecentJ, note.toMap(), where: "$columnId = ?", whereArgs: [note.id]);
// return await dbClient.rawUpdate(
// 'UPDATE $tableNote SET $columnTitle = \'${note.title}\', $columnDescription = \'${note.description}\' WHERE $columnId = ${note.id}');
}
Future close() async {
var dbClient = await db;
return dbClient.close();
}
}
I want to show title and description in the suggestion of the textfield. How get the desired result?
Sorry, but your question is not clear.
I am guessing you want to autocomplete view in your textfile
Here is the package that provides the same.
https://pub.dev/packages/flutter_typeahead
Take look at it.
Related
Can somebody help me in resolving the error. There is an error in UserModel.fromDocumentSnapshot. ITs telling userstatus has to be initialized. I have initialized it but yet the error remains. Can somebody tell me what could be the error in tis code. Looking forward to resolve this error. Please do help me
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:get/get.dart';
import 'package:hogoco_meet_app/model/base_model.dart';
import 'package:hogoco_meet_app/services/firebasedb.dart';
enum UserStatus { Active, Inactive, None }
extension on String {
UserStatus get getuserStatus {
switch (this) {
case 'Active':
return UserStatus.Active;
break;
case 'Inactive':
return UserStatus.Inactive;
break;
default:
return UserStatus.None;
break;
}
}
}
extension ExtendedUserStatus on UserStatus {
bool get active => this == UserStatus.Active;
bool get inactive => this == UserStatus.Inactive;
bool get none => this == UserStatus.None;
String get getuserStatus {
switch (this) {
case UserStatus.Active:
return 'Active';
break;
case UserStatus.Inactive:
return 'Inactive';
break;
default:
return 'None';
break;
}
}
}
class UserModel extends BaseModel {
String? id;
String? userProfilePhotoUrl;
String? userFullName;
String? userEmail;
UserStatus userStatus;
bool? userIsVerified;
Timestamp? userRegDate;
Timestamp? userLastLoginDate;
List<UserModel>? userFriends;
UserModel(
{this.id,
this.userProfilePhotoUrl,
this.userFullName,
this.userEmail,
this.userStatus = UserStatus.Inactive,
this.userIsVerified,
this.userRegDate,
this.userLastLoginDate,
this.userFriends});
#override
String toString() {
return '''UserModel: {userRegDate = ${this.userRegDate},id = ${this.id},userFullName = ${this.userFullName},userProfilePhotoURL = ${this.userProfilePhotoUrl},
userEmail = ${this.userEmail},userFriends = ${this.userFriends},userIsVerified = ${this.userIsVerified},userStatus = ${this.userStatus}}''';
}
UserModel.fromDocumentSnapshot({DocumentSnapshot? documentSnapshot}) {
final _userStatus = ((documentSnapshot!.data()! as Map<String, dynamic>)
.containsKey('userStatus'))
? ((documentSnapshot.data()! as Map<String, dynamic>)["userStatus"]
as String)
.getuserStatus
: UserStatus.None;
id = documentSnapshot.id;
userProfilePhotoUrl = (documentSnapshot.data()
as Map<String, dynamic>)['userProfilePhotoUrl'];
userFullName =
(documentSnapshot.data() as Map<String, dynamic>)["userFullName"];
userEmail = (documentSnapshot.data() as Map<String, dynamic>)["userEmail"];
userStatus = _userStatus;
userIsVerified =
(documentSnapshot.data() as Map<String, dynamic>)["userIsVerified"];
userRegDate =
(documentSnapshot.data() as Map<String, dynamic>)['userRegDate'];
userLastLoginDate =
(documentSnapshot.data() as Map<String, dynamic>)['userLastLoginDate'];
userFriends =
(documentSnapshot.data() as Map<String, dynamic>)["userFriends"];
}
}
class UserCrud {
final FirebaseService<UserModel> firebaseService =
Get.put(FirebaseService<UserModel>());
static const String Collection = FirebaseCollections.USER;
Stream<List<UserModel>> get getUsers {
return firebaseService.getListStream(
collection: Collection,
returnVal: (query) {
final retVal = <UserModel>[];
query.docs.forEach((element) {
retVal.add(UserModel.fromDocumentSnapshot(documentSnapshot: element));
});
return retVal;
},
);
}
}
Error
error: Non-nullable instance field 'userStatus' must be initialized. (not_initialized_non_nullable_instance_field at [hogoco_meet_app] lib\model\user_model.dart:71)
The userStatus field is the only field that is declared non-nullable, which means you have to initialize it before the constructor is executed.
UserModel.fromDocumentSnapshot({DocumentSnapshot? documentSnapshot}) is called a named constructor, therefore to be able to use it you need to initialize the userStatus first and you can do that by adding it to the initializer list:
UserModel.fromDocumentSnapshot({DocumentSnapshot? documentSnapshot}) : userStatus = UserStatus.Inactive {
final _userStatus = ((documentSnapshot!.data()! as Map<String, dynamic>)
.containsKey('userStatus'))
? ((documentSnapshot.data()! as Map<String, dynamic>)["userStatus"]
as String)
.getuserStatus
: UserStatus.None;
id = documentSnapshot.id;
userProfilePhotoUrl = (documentSnapshot.data()
as Map<String, dynamic>)['userProfilePhotoUrl'];
userFullName =
(documentSnapshot.data() as Map<String, dynamic>)["userFullName"];
userEmail = (documentSnapshot.data() as Map<String, dynamic>)["userEmail"];
userStatus = _userStatus;
userIsVerified =
(documentSnapshot.data() as Map<String, dynamic>)["userIsVerified"];
userRegDate =
(documentSnapshot.data() as Map<String, dynamic>)['userRegDate'];
userLastLoginDate =
(documentSnapshot.data() as Map<String, dynamic>)['userLastLoginDate'];
userFriends =
(documentSnapshot.data() as Map<String, dynamic>)["userFriends"];
}
I need to convert data from firebase to an object, but I get an error because of the Timestamp:
type 'Timestamp' is not a subtype of type 'Map<String, dynamic>'
In the model I added toDate() to the 'createdAt' but I still get the error.
How can I solve this in flutter?
import 'package:cloud_firestore/cloud_firestore.dart';
class AnimalWithLoc {
final String username;
final String userId;
final double longitude;
final double latitude;
final List<String> imageUrls;
final String description;
final DateTime createdAt;
final String address;
AnimalWithLoc(
this.username,
this.userId,
this.longitude,
this.latitude,
this.imageUrls,
this.description,
this.createdAt,
this.address,
);
AnimalWithLoc.fromMap(Map<String, dynamic> map)
: username = map['username'],
userId = map['userId'],
longitude = map['longitude'],
latitude = map['latitude'],
imageUrls = map['imageUrls'],
description = map['description'],
createdAt = (map["createdAt"] as Timestamp).toDate(),
address = map['address'];
}
in body.dart the foreach works correctly
...
final animalDocs = animalSnapshot.data!.docs;
final List<AnimalWithLoc> loadedAnimals = [];
for (var i = 0; i < animalDocs.length; i++) {
var currAnim = animalDocs[i].data();
currAnim.forEach((key, value) {
// print(value);
loadedAnimals.add(AnimalWithLoc.fromMap(value));
});
}
...
I think you are losing the toDate() method when you pass the data as a Map<String, dynamic>.
Try this in body.dart:
for (var i = 0; i < animalDocs.length; i++) {
var currAnim = animalDocs[i].data();
final createdAt = DateTime.parse(currAnim["createdAt"].toDate().toString());
}
I am trying to make a little project in Flutter where I add some data in a form along with a few images in the sqflite database, but I can't figure out why exactly my code isn't working.
Here is my db_helper class
Future<Database> get db async {
if (_db != null)
return _db;
_db = await initDb();
return _db;
}
//Creating a database with name test.dn in your directory
initDb() async {
io.Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, "products.db");
var theDb = await openDatabase(path, version: 1, onCreate: _onCreate);
return theDb;
}
// Creating a table name Employee with fields
void _onCreate(Database db, int version) async {
// When creating the db, create the table
await db.execute(
"""CREATE TABLE product(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
brand TEXT NOT NULL,
color TEXT NOT NULL,
size INTEGER NOT NULL,
qty INTEGER NOT NULL)""");
await db.execute(
"""CREATE TABLE image(
id INTEGER PRIMARY KEY AUTOINCREMENT,
path TEXT,
product_id INTEGER,
FOREIGN KEY(product_id) REFERENCES product (id)
ON DELETE NO ACTION ON UPDATE NO ACTION)""");
debugPrint("Created tables");
}
// Get all products
Future<List<Map<String, dynamic>>> getProducts() async {
Database db = this.initDb();
var result = await db.query('product');
return result;
}
// Inser or update image
Future<product_model.Image> insertImage(product_model.Image image) async {
// var count = Sqflite.firstIntValue(await _db.rawQuery("SELECT COUNT(*) FROM image WHERE username = ?", [image.id]));
Database database = await db;
await database.insert("image", image.toMap(), conflictAlgorithm: ConflictAlgorithm.ignore);
// await _db.update("image", image.toMap(), where: "id = ?", whereArgs: [image.id]);
return image;
}
// Inser or update product
Future<product_model.Product> insertProduct(product_model.Product product) async {
// var count = Sqflite.firstIntValue(await _db.rawQuery("SELECT COUNT(*) FROM product WHERE username = ?", [product.id]));
Database database = await db;
await database.insert("product", product.toMap(), conflictAlgorithm: ConflictAlgorithm.ignore);
// await _db.update("product", product.toMap(), where: "id = ?", whereArgs: [product.id]);
return product;
}
And here is where I call the db:
void _saveProductWithPictures() async{
db = DBHelper();
// var db = await dbHelper.initDb();
product = db_elements.Product();
imageModels = List<db_elements.Image>();
int result;
await db.insertProduct(product).whenComplete(() => {
_nameController.clear(),
_brandController.clear(),
_colorController.clear(),
_sizeController.clear(),
_qtyController.clear()
});
if (result != 0) { // Success
_showAlertDialog('Status', 'Note Saved Successfully');
} else { // Failure
_showAlertDialog('Status', 'Problem Saving Note');
}
//save the product
// end save the product
//save the product picture/s
for (var i in _files) imageModels.add(db_elements.Image(path: i.path.toString()));
for (var image in imageModels) await db.insertImage(image).whenComplete(() => {
_files.clear()
});
//end save the product picture/s
}
I tried for weeks to fix this, but still hopeless. I can't save data to the database. Appreciate any kind of help. Thanks in advance
I have problem and i think its a deadlock and i cant resolve the problem. I know there simular problem discuse but i need help. When i try to saveChange on productReposotory it stuck and dont execute the other code.
Here is my controller:
public IActionResult All(string type)
{
var viewModel = new AllFireplaceViewModel
{
Fireplaces =
this.fireplaceService.GetAllFireplaceAsync<IndexFireplaceViewModel>(type).Where(x => x.TypeOfChamber == type),
};
return this.View(viewModel);
}
My IFireplaceService:
using System.Collections.Generic;
using System.Threading.Tasks;
using KaminiCenter.Web.ViewModels.Fireplace;
public interface IFireplaceService
{
Task AddFireplaceAsync(FireplaceInputModel fireplaceInputModel);
IEnumerable<T> GetAllFireplaceAsync<T>(string type);
T GetByName<T>(string name);
}
And this is my implementation of the interface:
public async Task AddFireplaceAsync(FireplaceInputModel model)
{
var typeOfChamber = Enum.Parse<TypeOfChamber>(model.TypeOfChamber);
if (model.Power == null ||
model.Size == null ||
model.Chimney == null)
{
throw new ArgumentNullException("Cannot safe null or whitespace values!");
}
await this.productService.AddProductAsync(model.Name, model.Group);
var productId = this.productService.GetIdByNameAndGroup(model.Name, model.Group);
var groupId = this.groupService.FindByGroupName(model.Group).Id;
var fireplace = new Fireplace_chamber
{
Id = Guid.NewGuid().ToString(),
Power = model.Power,
Size = model.Size,
Chimney = model.Chimney,
Price = model.Price,
Description = model.Description,
ImagePath = model.ImagePath.ToString(),
TypeOfChamber = typeOfChamber,
ProductId = productId,
GroupId = groupId,
CreatedOn = DateTime.UtcNow,
ModifiedOn = DateTime.UtcNow,
};
await this.fireplaceRepository.AddAsync(fireplace);
await this.fireplaceRepository.SaveChangesAsync();
}
This is the productService.AddProductAsync:
public async Task AddProductAsync(string name, string groupName)
{
var group = this.groupService.FindByGroupName(groupName);
if (group == null)
{
await this.groupService.CreateAsync(groupName);
}
var product = new Product
{
// TO Check for Id Initializesion
Id = Guid.NewGuid().ToString(),
Name = name,
GroupId = group.Id,
CreatedOn = DateTime.UtcNow,
ModifiedOn = DateTime.UtcNow,
};
await this.productRepository.AddAsync(product);
await this.productRepository.SaveChangesAsync();
}
And my Add Action
public IActionResult Add()
{
var createFireplaceInputModel = new FireplaceInputModel();
return this.View(createFireplaceInputModel);
}
I am trying to make an ApI request with Digest Authentication. I found an answer to the above question FLUTTER How to implement Digest Authentification but it is not very clear. The docs for digest are very minimal.
Following is my code
import 'package:http/io_client.dart' as io_client;
import 'package:http/http.dart' as http;
try {
HttpClient authenticatingClient = HttpClient();
authenticatingClient.authenticate = (uri, scheme, realm) {
authenticatingClient.addCredentials(
uri,
realm,
HttpClientDigestCredentials(
DIGEST_AUTH_USERNAME, DIGEST_AUTH_PASSWORD));
return Future.value(true);
};
http.Client client = io_client.IOClient(authenticatingClient);
final response = await client.post(LOGIN_URL, body: {
"username": userName,
"password": password,
"user_group": 2
}).timeout(const Duration(seconds: 20));
if (response.statusCode == 200) {
debugPrint(response.body);
CurvesLoginModel curvesLoginModel = standardSerializers.deserializeWith(
CurvesLoginModel.serializer, json.decode(response.body));
return curvesLoginModel;
} else {
return null;
}
} on TimeoutException catch (_) {
return null;
} on SocketException catch (_) {
return null;
}
}
But what is realm in addCredentials.
Also is this the way to implement Digest Authentication in http for Flutter?
As soon as I hit my endpoint I get the following error Unhandled Exception: type 'int' is not a subtype of type 'String' in type cast
Realm is an arbitrary string provided by the web server to help you decide which username to use, in case you have more than one. It's sort of analogous to domain. In one domain your username might be fbloggs, in another fredb. By telling you the realm/domain you know which to provide.
Your cast problem is caused by using the value 2 in the body. That must be a Map<String, String>, but you have provided an integer. Replace it with 2.toString().
If somebody wants to know how to make digest auth with http then it is as follows
import 'dart:async';
import 'dart:convert';
import 'dart:math' as math;
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart' as crypto;
import 'package:http/http.dart' as http;
class DigestAuthClient extends http.BaseClient {
DigestAuthClient(String username, String password, {inner})
: _auth = DigestAuth(username, password),
// ignore: prefer_if_null_operators
_inner = inner == null ? http.Client() : inner;
final http.Client _inner;
final DigestAuth _auth;
void _setAuthString(http.BaseRequest request) {
request.headers['Authorization'] =
_auth.getAuthString(request.method, request.url);
}
#override
Future<http.StreamedResponse> send(http.BaseRequest request) async {
final response = await _inner.send(request);
if (response.statusCode == 401) {
final newRequest = copyRequest(request);
final String authInfo = response.headers['www-authenticate'];
_auth.initFromAuthorizationHeader(authInfo);
_setAuthString(newRequest);
return _inner.send(newRequest);
}
// we should reach this point only with errors other than 401
return response;
}
}
Map<String, String> splitAuthenticateHeader(String header) {
if (header == null || !header.startsWith('Digest ')) {
return null;
}
String token = header.substring(7); // remove 'Digest '
var ret = <String, String>{};
final components = token.split(',').map((token) => token.trim());
for (final component in components) {
final kv = component.split('=');
ret[kv[0]] = kv.getRange(1, kv.length).join('=').replaceAll('"', '');
}
return ret;
}
String md5Hash(String data) {
var content = const Utf8Encoder().convert(data);
var md5 = crypto.md5;
var digest = md5.convert(content).toString();
return digest;
}
// from http_retry
/// Returns a copy of [original].
http.Request _copyNormalRequest(http.Request original) {
var request = http.Request(original.method, original.url)
..followRedirects = original.followRedirects
..persistentConnection = original.persistentConnection
..body = original.body;
request.headers.addAll(original.headers);
request.maxRedirects = original.maxRedirects;
return request;
}
http.BaseRequest copyRequest(http.BaseRequest original) {
if (original is http.Request) {
return _copyNormalRequest(original);
} else {
throw UnimplementedError(
'cannot handle yet requests of type ${original.runtimeType}');
}
}
// Digest auth
String _formatNonceCount(int nc) {
return nc.toRadixString(16).padLeft(8, '0');
}
String _computeHA1(String realm, String algorithm, String username,
String password, String nonce, String cnonce) {
String ha1;
if (algorithm == null || algorithm == 'MD5') {
final token1 = "$username:$realm:$password";
ha1 = md5Hash(token1);
} else if (algorithm == 'MD5-sess') {
final token1 = "$username:$realm:$password";
final md51 = md5Hash(token1);
final token2 = "$md51:$nonce:$cnonce";
ha1 = md5Hash(token2);
}
return ha1;
}
Map<String, String> computeResponse(
String method,
String path,
String body,
String algorithm,
String qop,
String opaque,
String realm,
String cnonce,
String nonce,
int nc,
String username,
String password) {
var ret = <String, String>{};
// ignore: non_constant_identifier_names
String HA1 = _computeHA1(realm, algorithm, username, password, nonce, cnonce);
// ignore: non_constant_identifier_names
String HA2;
if (qop == 'auth-int') {
final bodyHash = md5Hash(body);
final token2 = "$method:$path:$bodyHash";
HA2 = md5Hash(token2);
} else {
// qop in [null, auth]
final token2 = "$method:$path";
HA2 = md5Hash(token2);
}
final nonceCount = _formatNonceCount(nc);
ret['username'] = username;
ret['realm'] = realm;
ret['nonce'] = nonce;
ret['uri'] = path;
ret['qop'] = qop;
ret['nc'] = nonceCount;
ret['cnonce'] = cnonce;
if (opaque != null) {
ret['opaque'] = opaque;
}
ret['algorithm'] = algorithm;
if (qop == null) {
final token3 = "$HA1:$nonce:$HA2";
ret['response'] = md5Hash(token3);
} else if (qop == 'auth' || qop == 'auth-int') {
final token3 = "$HA1:$nonce:$nonceCount:$cnonce:$qop:$HA2";
ret['response'] = md5Hash(token3);
}
return ret;
}
class DigestAuth {
DigestAuth(this.username, this.password);
String username;
String password;
// must get from first response
String _algorithm;
String _qop;
String _realm;
String _nonce;
String _opaque;
int _nc = 0; // request counter
String _cnonce; // client-generated; should change for each request
String _computeNonce() {
math.Random rnd = math.Random();
List<int> values = List<int>.generate(16, (i) => rnd.nextInt(256));
return hex.encode(values);
}
String getAuthString(String method, Uri url) {
_cnonce = _computeNonce();
_nc += 1;
// if url has query parameters, append query to path
var path = url.hasQuery ? "${url.path}?${url.query}" : url.path;
// after the first request we have the nonce, so we can provide credentials
var authValues = computeResponse(method, path, '', _algorithm, _qop,
_opaque, _realm, _cnonce, _nonce, _nc, username, password);
final authValuesString = authValues.entries
.where((e) => e.value != null)
.map((e) => [e.key, '="', e.value, '"'].join(''))
.toList()
.join(', ');
final authString = 'Digest $authValuesString';
return authString;
}
void initFromAuthorizationHeader(String authInfo) {
Map<String, String> values = splitAuthenticateHeader(authInfo);
_algorithm = values['algorithm'];
_qop = values['qop'];
_realm = values['realm'];
_nonce = values['nonce'];
_opaque = values['opaque'];
}
bool isReady() {
return _nonce != null;
}
}
Then when calling your api
final response =
await DigestAuthClient(DIGEST_AUTH_USERNAME, DIGEST_AUTH_PASSWORD)
.post(LOGIN_URL, body: {
"USERNAME": userName,
"PASSWORD": password,
"USER_GROUP": "2"
}).timeout(const Duration(seconds: 20));
All credit goes to the following library https://pub.dev/packages/http_auth