nabed implementation

dev_3.24.3_nabed
Aamir 1 year ago
parent 3826b854fc
commit a76358a871

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 651 KiB

@ -22,8 +22,8 @@ var PACKAGES_ORDER_HISTORY = '/api/orders/items';
var PACKAGES_TAMARA_OPT = '/api/orders/paymentoptions/tamara';
// var BASE_URL = 'http://10.50.100.198:2018/';
// var BASE_URL = 'http://10.50.100.198:4422/';
// var BASE_URL = 'https://uat.hmgwebservices.com/';
var BASE_URL = 'https://hmgwebservices.com/';
var BASE_URL = 'https://uat.hmgwebservices.com/';
// var BASE_URL = 'https://hmgwebservices.com/';
// var BASE_URL = 'http://10.20.200.111:1010/';
// var BASE_URL = 'https://orash.cloudsolutions.com.sa/';
// var BASE_URL = 'https://vidauat.cloudsolutions.com.sa/';
@ -685,6 +685,11 @@ var applePayMerchantId = "merchant.com.hmgwebservices";
// var payFortEnvironment = FortEnvironment.test;
// var applePayMerchantId = "merchant.com.hmgwebservices.uat";
// Patient Courses
var GET_PATIENT_COURSES_LIST = "Services/Nabed.svc/REST/PatientEducationalJourney";
var GET_PATIENT_COURSE_BY_ID = "Services/Nabed.svc/REST/PatientEducationalJourneyByID";
var INSERT_PATIENT_COURSE_VIEW_STATS = "Services/Nabed.svc/REST/InsertJourneyStatistics";
class AppGlobal {
static var context;

@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:flutter/material.dart';
extension CapExtension on String {
String get toCamelCase => "${this[0].toUpperCase()}${this.substring(1)}";
@ -17,3 +18,13 @@ extension HashSha on String {
return sha256.convert(bytes).toString();
}
}
extension OnTapWidget on Widget {
Widget onTap(VoidCallback onTap, {Key? key}) {
return GestureDetector(
key: key,
onTap: onTap,
child: this, // This refers to the widget on which the extension is called
);
}
}

@ -23,6 +23,7 @@ import 'core/viewModels/pharmacyModule/OrderPreviewViewModel.dart';
import 'core/viewModels/project_view_model.dart';
import 'locator.dart';
import 'pages/pharmacies/compare-list.dart';
import 'services/course_service/course_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@ -55,8 +56,6 @@ class _MyApp extends State<MyApp> {
//0567184134 mobile
//246305493
// checkForUpdate() {
// // todo need to verify 'imp'
// InAppUpdate.checkForUpdate().then((info) {
@ -118,6 +117,9 @@ class _MyApp extends State<MyApp> {
ChangeNotifierProvider<SearchProvider>(
create: (context) => SearchProvider(),
),
ChangeNotifierProvider<CourseServiceProvider>(
create: (context) => CourseServiceProvider(),
),
ChangeNotifierProvider.value(
value: SearchProvider(),
),
@ -140,14 +142,12 @@ class _MyApp extends State<MyApp> {
],
child: Consumer<ProjectViewModel>(
builder: (context, projectProvider, child) => MaterialApp(
builder: (_, mchild) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaler: TextScaler.linear(1.0),
), //set desired text scale factor here
child: mchild!
);
data: MediaQuery.of(context).copyWith(
textScaler: TextScaler.linear(1.0),
), //set desired text scale factor here
child: mchild!);
// Container(
// color: Colors.blue,
// ));

@ -0,0 +1,333 @@
// To parse this JSON data, do
//
// final patientEducationJourneyListModel = patientEducationJourneyListModelFromJson(jsonString);
import 'dart:convert';
PatientEducationJourneyListModel patientEducationJourneyListModelFromJson(String str) => PatientEducationJourneyListModel.fromJson(json.decode(str));
String patientEducationJourneyListModelToJson(PatientEducationJourneyListModel data) => json.encode(data.toJson());
class PatientEducationJourneyListModel {
NabedJourneyResponseResult? nabedJourneyResponseResult;
PatientEducationJourneyListModel({
this.nabedJourneyResponseResult,
});
factory PatientEducationJourneyListModel.fromJson(Map<String, dynamic> json) => PatientEducationJourneyListModel(
nabedJourneyResponseResult: json["NabedJourneyResponseResult"] == null ? null : NabedJourneyResponseResult.fromJson(json["NabedJourneyResponseResult"]),
);
Map<String, dynamic> toJson() => {
"NabedJourneyResponseResult": nabedJourneyResponseResult?.toJson(),
};
}
class NabedJourneyResponseResult {
List<Datum>? data;
Links? links;
Meta? meta;
NabedJourneyResponseResult({
this.data,
this.links,
this.meta,
});
factory NabedJourneyResponseResult.fromJson(Map<String, dynamic> json) => NabedJourneyResponseResult(
data: json["data"] == null ? [] : List<Datum>.from(json["data"]!.map((x) => Datum.fromJson(x))),
links: json["links"] == null ? null : Links.fromJson(json["links"]),
meta: json["meta"] == null ? null : Meta.fromJson(json["meta"]),
);
Map<String, dynamic> toJson() => {
"data": data == null ? [] : List<dynamic>.from(data!.map((x) => x.toJson())),
"links": links?.toJson(),
"meta": meta?.toJson(),
};
}
class Datum {
List<ContentClass>? contentClasses;
DateTime? date;
DefaultPocData? defaultPocData;
int? id;
String? integrationApproach;
bool? isNew;
DateTime? openedAt;
String? sourceType;
Stats? stats;
TagValues? tagValues;
Datum({
this.contentClasses,
this.date,
this.defaultPocData,
this.id,
this.integrationApproach,
this.isNew,
this.openedAt,
this.sourceType,
this.stats,
this.tagValues,
});
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
contentClasses: json["content_classes"] == null ? [] : List<ContentClass>.from(json["content_classes"]!.map((x) => ContentClass.fromJson(x))),
date: json["date"] == null ? null : DateTime.parse(json["date"]),
defaultPocData: json["default_poc_data"] == null ? null : DefaultPocData.fromJson(json["default_poc_data"]),
id: json["id"],
integrationApproach: json["integration_approach"],
isNew: json["is_new"],
openedAt: json["opened_at"] == null ? null : DateTime.parse(json["opened_at"]),
sourceType: json["source_type"],
stats: json["stats"] == null ? null : Stats.fromJson(json["stats"]),
tagValues: json["tag_values"] == null ? null : TagValues.fromJson(json["tag_values"]),
);
Map<String, dynamic> toJson() => {
"content_classes": contentClasses == null ? [] : List<dynamic>.from(contentClasses!.map((x) => x.toJson())),
"date": "${date!.year.toString().padLeft(4, '0')}-${date!.month.toString().padLeft(2, '0')}-${date!.day.toString().padLeft(2, '0')}",
"default_poc_data": defaultPocData?.toJson(),
"id": id,
"integration_approach": integrationApproach,
"is_new": isNew,
"opened_at": openedAt?.toIso8601String(),
"source_type": sourceType,
"stats": stats?.toJson(),
"tag_values": tagValues?.toJson(),
};
}
class ContentClass {
int? id;
Image? image;
int? readPercentage;
String? title;
dynamic topics;
String? type;
ContentClass({
this.id,
this.image,
this.readPercentage,
this.title,
this.topics,
this.type,
});
factory ContentClass.fromJson(Map<String, dynamic> json) => ContentClass(
id: json["id"],
image: json["image"] == null ? null : Image.fromJson(json["image"]),
readPercentage: json["read_percentage"],
title: json["title"],
topics: json["topics"],
type: json["type"],
);
Map<String, dynamic> toJson() => {
"id": id,
"image": image?.toJson(),
"read_percentage": readPercentage,
"title": title,
"topics": topics,
"type": type,
};
}
class Image {
String? thumbUrl;
String? url;
Image({
this.thumbUrl,
this.url,
});
factory Image.fromJson(Map<String, dynamic> json) => Image(
thumbUrl: json["thumb_url"],
url: json["url"],
);
Map<String, dynamic> toJson() => {
"thumb_url": thumbUrl,
"url": url,
};
}
class DefaultPocData {
Image? image;
String? title;
DefaultPocData({
this.image,
this.title,
});
factory DefaultPocData.fromJson(Map<String, dynamic> json) => DefaultPocData(
image: json["image"] == null ? null : Image.fromJson(json["image"]),
title: json["title"],
);
Map<String, dynamic> toJson() => {
"image": image?.toJson(),
"title": title,
};
}
class Stats {
int? attachmentsCount;
int? contentCount;
int? contentReadCount;
int? educationalContentCount;
int? educationalContentReadCount;
int? educationalReadPercentage;
bool? isCompleted;
int? readPercentage;
int? readRemainingPercentage;
int? screenshotsCount;
int? topicCount;
int? videosCount;
int? videosReadCount;
Stats({
this.attachmentsCount,
this.contentCount,
this.contentReadCount,
this.educationalContentCount,
this.educationalContentReadCount,
this.educationalReadPercentage,
this.isCompleted,
this.readPercentage,
this.readRemainingPercentage,
this.screenshotsCount,
this.topicCount,
this.videosCount,
this.videosReadCount,
});
factory Stats.fromJson(Map<String, dynamic> json) => Stats(
attachmentsCount: json["attachments_count"],
contentCount: json["content_count"],
contentReadCount: json["content_read_count"],
educationalContentCount: json["educational_content_count"],
educationalContentReadCount: json["educational_content_read_count"],
educationalReadPercentage: json["educational_read_percentage"],
isCompleted: json["is_completed"],
readPercentage: json["read_percentage"],
readRemainingPercentage: json["read_remaining_percentage"],
screenshotsCount: json["screenshots_count"],
topicCount: json["topic_count"],
videosCount: json["videos_count"],
videosReadCount: json["videos_read_count"],
);
Map<String, dynamic> toJson() => {
"attachments_count": attachmentsCount,
"content_count": contentCount,
"content_read_count": contentReadCount,
"educational_content_count": educationalContentCount,
"educational_content_read_count": educationalContentReadCount,
"educational_read_percentage": educationalReadPercentage,
"is_completed": isCompleted,
"read_percentage": readPercentage,
"read_remaining_percentage": readRemainingPercentage,
"screenshots_count": screenshotsCount,
"topic_count": topicCount,
"videos_count": videosCount,
"videos_read_count": videosReadCount,
};
}
class TagValues {
String? consultationCode;
String? title;
String? titleAr;
TagValues({
this.consultationCode,
this.title,
this.titleAr,
});
factory TagValues.fromJson(Map<String, dynamic> json) => TagValues(
consultationCode: json["consultation_code"],
title: json["title"],
titleAr: json["title_ar"],
);
Map<String, dynamic> toJson() => {
"consultation_code": consultationCode,
"title": title,
"title_ar": titleAr,
};
}
class Links {
String? first;
String? last;
dynamic next;
dynamic prev;
Links({
this.first,
this.last,
this.next,
this.prev,
});
factory Links.fromJson(Map<String, dynamic> json) => Links(
first: json["first"],
last: json["last"],
next: json["next"],
prev: json["prev"],
);
Map<String, dynamic> toJson() => {
"first": first,
"last": last,
"next": next,
"prev": prev,
};
}
class Meta {
int? currentPage;
int? from;
int? lastPage;
String? path;
int? perPage;
int? to;
int? total;
Meta({
this.currentPage,
this.from,
this.lastPage,
this.path,
this.perPage,
this.to,
this.total,
});
factory Meta.fromJson(Map<String, dynamic> json) => Meta(
currentPage: json["current_page"],
from: json["from"],
lastPage: json["last_page"],
path: json["path"],
perPage: json["per_page"],
to: json["to"],
total: json["total"],
);
Map<String, dynamic> toJson() => {
"current_page": currentPage,
"from": from,
"last_page": lastPage,
"path": path,
"per_page": perPage,
"to": to,
"total": total,
};
}

@ -0,0 +1,619 @@
import 'dart:async';
import 'dart:convert';
import 'package:video_player/video_player.dart';
class PatientEducationJourneyModel {
dynamic date;
int? languageId;
int? serviceName;
dynamic time;
dynamic androidLink;
dynamic authenticationTokenId;
dynamic data;
bool? dataw;
int? dietType;
int? dietTypeId;
dynamic errorCode;
dynamic errorEndUserMessage;
dynamic errorEndUserMessageN;
dynamic errorMessage;
int? errorStatusCode;
int? errorType;
int? foodCategory;
dynamic iosLink;
bool? isAuthenticated;
int? mealOrderStatus;
int? mealType;
int? messageStatus;
int? numberOfResultRecords;
dynamic patientBlodType;
dynamic successMsg;
dynamic successMsgN;
dynamic vidaUpdatedResponse;
NabedJourneyByIdResponseResult? nabedJourneyByIdResponseResult;
dynamic nabedJourneyResponseResult;
dynamic nabedPatientList;
dynamic nabedResponse;
PatientEducationJourneyModel({
this.date,
this.languageId,
this.serviceName,
this.time,
this.androidLink,
this.authenticationTokenId,
this.data,
this.dataw,
this.dietType,
this.dietTypeId,
this.errorCode,
this.errorEndUserMessage,
this.errorEndUserMessageN,
this.errorMessage,
this.errorStatusCode,
this.errorType,
this.foodCategory,
this.iosLink,
this.isAuthenticated,
this.mealOrderStatus,
this.mealType,
this.messageStatus,
this.numberOfResultRecords,
this.patientBlodType,
this.successMsg,
this.successMsgN,
this.vidaUpdatedResponse,
this.nabedJourneyByIdResponseResult,
this.nabedJourneyResponseResult,
this.nabedPatientList,
this.nabedResponse,
});
factory PatientEducationJourneyModel.fromRawJson(String str) => PatientEducationJourneyModel.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory PatientEducationJourneyModel.fromJson(Map<String, dynamic> json) => PatientEducationJourneyModel(
date: json["Date"],
languageId: json["LanguageID"],
serviceName: json["ServiceName"],
time: json["Time"],
androidLink: json["AndroidLink"],
authenticationTokenId: json["AuthenticationTokenID"],
data: json["Data"],
dataw: json["Dataw"],
dietType: json["DietType"],
dietTypeId: json["DietTypeID"],
errorCode: json["ErrorCode"],
errorEndUserMessage: json["ErrorEndUserMessage"],
errorEndUserMessageN: json["ErrorEndUserMessageN"],
errorMessage: json["ErrorMessage"],
errorStatusCode: json["ErrorStatusCode"],
errorType: json["ErrorType"],
foodCategory: json["FoodCategory"],
iosLink: json["IOSLink"],
isAuthenticated: json["IsAuthenticated"],
mealOrderStatus: json["MealOrderStatus"],
mealType: json["MealType"],
messageStatus: json["MessageStatus"],
numberOfResultRecords: json["NumberOfResultRecords"],
patientBlodType: json["PatientBlodType"],
successMsg: json["SuccessMsg"],
successMsgN: json["SuccessMsgN"],
vidaUpdatedResponse: json["VidaUpdatedResponse"],
nabedJourneyByIdResponseResult: json["NabedJourneyByIdResponseResult"] == null ? null : NabedJourneyByIdResponseResult.fromJson(json["NabedJourneyByIdResponseResult"]),
nabedJourneyResponseResult: json["NabedJourneyResponseResult"],
nabedPatientList: json["NabedPatientList"],
nabedResponse: json["NabedResponse"],
);
Map<String, dynamic> toJson() => {
"Date": date,
"LanguageID": languageId,
"ServiceName": serviceName,
"Time": time,
"AndroidLink": androidLink,
"AuthenticationTokenID": authenticationTokenId,
"Data": data,
"Dataw": dataw,
"DietType": dietType,
"DietTypeID": dietTypeId,
"ErrorCode": errorCode,
"ErrorEndUserMessage": errorEndUserMessage,
"ErrorEndUserMessageN": errorEndUserMessageN,
"ErrorMessage": errorMessage,
"ErrorStatusCode": errorStatusCode,
"ErrorType": errorType,
"FoodCategory": foodCategory,
"IOSLink": iosLink,
"IsAuthenticated": isAuthenticated,
"MealOrderStatus": mealOrderStatus,
"MealType": mealType,
"MessageStatus": messageStatus,
"NumberOfResultRecords": numberOfResultRecords,
"PatientBlodType": patientBlodType,
"SuccessMsg": successMsg,
"SuccessMsgN": successMsgN,
"VidaUpdatedResponse": vidaUpdatedResponse,
"NabedJourneyByIdResponseResult": nabedJourneyByIdResponseResult?.toJson(),
"NabedJourneyResponseResult": nabedJourneyResponseResult,
"NabedPatientList": nabedPatientList,
"NabedResponse": nabedResponse,
};
}
class NabedJourneyByIdResponseResult {
Consultation? consultation;
List<ContentClass>? contentClasses;
NabedJourneyByIdResponseResult({
this.consultation,
this.contentClasses,
});
factory NabedJourneyByIdResponseResult.fromRawJson(String str) => NabedJourneyByIdResponseResult.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory NabedJourneyByIdResponseResult.fromJson(Map<String, dynamic> json) => NabedJourneyByIdResponseResult(
consultation: json["consultation"] == null ? null : Consultation.fromJson(json["consultation"]),
contentClasses: json["content_classes"] == null ? [] : List<ContentClass>.from(json["content_classes"]!.map((x) => ContentClass.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"consultation": consultation?.toJson(),
"content_classes": contentClasses == null ? [] : List<dynamic>.from(contentClasses!.map((x) => x.toJson())),
};
}
class Consultation {
DateTime? date;
DefaultPocData? defaultPocData;
int? id;
String? integrationApproach;
bool? isNew;
String? openedAt;
String? sourceType;
Stats? stats;
TagValues? tagValues;
Consultation({
this.date,
this.defaultPocData,
this.id,
this.integrationApproach,
this.isNew,
this.openedAt,
this.sourceType,
this.stats,
this.tagValues,
});
factory Consultation.fromRawJson(String str) => Consultation.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Consultation.fromJson(Map<String, dynamic> json) => Consultation(
date: json["date"] == null ? null : DateTime.parse(json["date"]),
defaultPocData: json["default_poc_data"] == null ? null : DefaultPocData.fromJson(json["default_poc_data"]),
id: json["id"],
integrationApproach: json["integration_approach"],
isNew: json["is_new"],
openedAt: json["opened_at"],
sourceType: json["source_type"],
stats: json["stats"] == null ? null : Stats.fromJson(json["stats"]),
tagValues: json["tag_values"] == null ? null : TagValues.fromJson(json["tag_values"]),
);
Map<String, dynamic> toJson() => {
"date": "${date!.year.toString().padLeft(4, '0')}-${date!.month.toString().padLeft(2, '0')}-${date!.day.toString().padLeft(2, '0')}",
"default_poc_data": defaultPocData?.toJson(),
"id": id,
"integration_approach": integrationApproach,
"is_new": isNew,
"opened_at": openedAt,
"source_type": sourceType,
"stats": stats?.toJson(),
"tag_values": tagValues?.toJson(),
};
}
class DefaultPocData {
Image? image;
String? title;
DefaultPocData({
this.image,
this.title,
});
factory DefaultPocData.fromRawJson(String str) => DefaultPocData.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory DefaultPocData.fromJson(Map<String, dynamic> json) => DefaultPocData(
image: json["image"] == null ? null : Image.fromJson(json["image"]),
title: json["title"],
);
Map<String, dynamic> toJson() => {
"image": image?.toJson(),
"title": title,
};
}
class Image {
String? thumbUrl;
String? url;
Image({
this.thumbUrl,
this.url,
});
factory Image.fromRawJson(String str) => Image.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Image.fromJson(Map<String, dynamic> json) => Image(
thumbUrl: json["thumb_url"],
url: json["url"],
);
Map<String, dynamic> toJson() => {
"thumb_url": thumbUrl,
"url": url,
};
}
class Stats {
int? attachmentsCount;
int? contentCount;
int? contentReadCount;
int? educationalContentCount;
int? educationalContentReadCount;
int? educationalReadPercentage;
bool? isCompleted;
int? readPercentage;
int? readRemainingPercentage;
int? screenshotsCount;
int? topicCount;
int? videosCount;
int? videosReadCount;
Stats({
this.attachmentsCount,
this.contentCount,
this.contentReadCount,
this.educationalContentCount,
this.educationalContentReadCount,
this.educationalReadPercentage,
this.isCompleted,
this.readPercentage,
this.readRemainingPercentage,
this.screenshotsCount,
this.topicCount,
this.videosCount,
this.videosReadCount,
});
factory Stats.fromRawJson(String str) => Stats.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Stats.fromJson(Map<String, dynamic> json) => Stats(
attachmentsCount: json["attachments_count"],
contentCount: json["content_count"],
contentReadCount: json["content_read_count"],
educationalContentCount: json["educational_content_count"],
educationalContentReadCount: json["educational_content_read_count"],
educationalReadPercentage: json["educational_read_percentage"],
isCompleted: json["is_completed"],
readPercentage: json["read_percentage"],
readRemainingPercentage: json["read_remaining_percentage"],
screenshotsCount: json["screenshots_count"],
topicCount: json["topic_count"],
videosCount: json["videos_count"],
videosReadCount: json["videos_read_count"],
);
Map<String, dynamic> toJson() => {
"attachments_count": attachmentsCount,
"content_count": contentCount,
"content_read_count": contentReadCount,
"educational_content_count": educationalContentCount,
"educational_content_read_count": educationalContentReadCount,
"educational_read_percentage": educationalReadPercentage,
"is_completed": isCompleted,
"read_percentage": readPercentage,
"read_remaining_percentage": readRemainingPercentage,
"screenshots_count": screenshotsCount,
"topic_count": topicCount,
"videos_count": videosCount,
"videos_read_count": videosReadCount,
};
}
class TagValues {
String? consultationCode;
String? title;
String? titleAr;
TagValues({
this.consultationCode,
this.title,
this.titleAr,
});
factory TagValues.fromRawJson(String str) => TagValues.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory TagValues.fromJson(Map<String, dynamic> json) => TagValues(
consultationCode: json["consultation_code"],
title: json["title"],
titleAr: json["title_ar"],
);
Map<String, dynamic> toJson() => {
"consultation_code": consultationCode,
"title": title,
"title_ar": titleAr,
};
}
class ContentClass {
int? id;
Image? image;
int? readPercentage;
String? title;
List<Topic>? topics;
String? type;
ContentClass({
this.id,
this.image,
this.readPercentage,
this.title,
this.topics,
this.type,
});
factory ContentClass.fromRawJson(String str) => ContentClass.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory ContentClass.fromJson(Map<String, dynamic> json) => ContentClass(
id: json["id"],
image: json["image"] == null ? null : Image.fromJson(json["image"]),
readPercentage: json["read_percentage"],
title: json["title"],
topics: json["topics"] == null ? [] : List<Topic>.from(json["topics"]!.map((x) => Topic.fromJson(x))),
type: json["type"],
);
Map<String, dynamic> toJson() => {
"id": id,
"image": image?.toJson(),
"read_percentage": readPercentage,
"title": title,
"topics": topics == null ? [] : List<dynamic>.from(topics!.map((x) => x.toJson())),
"type": type,
};
}
class Topic {
List<Content>? contents;
int? contentsCount;
int? id;
Image? image;
int? readContentsCount;
int? readPercentage;
String? subjectId;
String? title;
Topic({
this.contents,
this.contentsCount,
this.id,
this.image,
this.readContentsCount,
this.readPercentage,
this.subjectId,
this.title,
});
factory Topic.fromRawJson(String str) => Topic.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Topic.fromJson(Map<String, dynamic> json) => Topic(
contents: json["contents"] == null ? [] : List<Content>.from(json["contents"]!.map((x) => Content.fromJson(x))),
contentsCount: json["contents_count"],
id: json["id"],
image: json["image"] == null ? null : Image.fromJson(json["image"]),
readContentsCount: json["read_contents_count"],
readPercentage: json["read_percentage"],
subjectId: json["subject_id"],
title: json["title"],
);
Map<String, dynamic> toJson() => {
"contents": contents == null ? [] : List<dynamic>.from(contents!.map((x) => x.toJson())),
"contents_count": contentsCount,
"id": id,
"image": image?.toJson(),
"read_contents_count": readContentsCount,
"read_percentage": readPercentage,
"subject_id": subjectId,
"title": title,
};
}
class Content {
String? body;
int? id;
Question? question;
dynamic read;
String? subjectId;
String? title;
Video? video;
VideoPlayerController? controller;
Timer? timer;
Content({this.body, this.id, this.question, this.read, this.subjectId, this.title, this.video, this.controller, this.timer});
factory Content.fromRawJson(String str) => Content.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Content.fromJson(Map<String, dynamic> json) => Content(
body: json["body"],
id: json["id"],
question: json["question"] == null ? null : Question.fromJson(json["question"]),
read: json["read"],
subjectId: json["subject_id"],
title: json["title"],
video: json["video"] == null ? null : Video.fromJson(json["video"]),
controller: null,
timer: null);
Map<String, dynamic> toJson() => {
"body": body,
"id": id,
"question": question?.toJson(),
"read": read,
"subject_id": subjectId,
"title": title,
"video": video?.toJson(),
};
}
class Question {
List<Answer>? answers;
int? id;
bool? shouldAnswer;
String? subjectId;
String? text;
int? triggerAt;
Question({
this.answers,
this.id,
this.shouldAnswer,
this.subjectId,
this.text,
this.triggerAt,
});
factory Question.fromRawJson(String str) => Question.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Question.fromJson(Map<String, dynamic> json) => Question(
answers: json["answers"] == null ? [] : List<Answer>.from(json["answers"]!.map((x) => Answer.fromJson(x))),
id: json["id"],
shouldAnswer: json["should_answer"],
subjectId: json["subject_id"],
text: json["text"],
triggerAt: json["trigger_at"],
);
Map<String, dynamic> toJson() => {
"answers": answers == null ? [] : List<dynamic>.from(answers!.map((x) => x.toJson())),
"id": id,
"should_answer": shouldAnswer,
"subject_id": subjectId,
"text": text,
"trigger_at": triggerAt,
};
}
class Answer {
String? description;
int? id;
bool? isCorrect;
String? text;
Answer({
this.description,
this.id,
this.isCorrect,
this.text,
});
factory Answer.fromRawJson(String str) => Answer.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Answer.fromJson(Map<String, dynamic> json) => Answer(
description: json["description"],
id: json["id"],
isCorrect: json["is_correct"],
text: json["text"],
);
Map<String, dynamic> toJson() => {
"description": description,
"id": id,
"is_correct": isCorrect,
"text": text,
};
}
class Video {
Flavor? flavor;
Video({
this.flavor,
});
factory Video.fromRawJson(String str) => Video.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Video.fromJson(Map<String, dynamic> json) => Video(
flavor: json["flavor"] == null ? null : Flavor.fromJson(json["flavor"]),
);
Map<String, dynamic> toJson() => {
"flavor": flavor?.toJson(),
};
}
class Flavor {
String? downloadable;
int? duration;
int? flavorId;
String? hls;
String? picture;
Flavor({
this.downloadable,
this.duration,
this.flavorId,
this.hls,
this.picture,
});
factory Flavor.fromRawJson(String str) => Flavor.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory Flavor.fromJson(Map<String, dynamic> json) => Flavor(
downloadable: json["downloadable"],
duration: json["duration"],
flavorId: json["flavor_id"],
hls: json["hls"],
picture: json["picture"],
);
Map<String, dynamic> toJson() => {
"downloadable": downloadable,
"duration": duration,
"flavor_id": flavorId,
"hls": hls,
"picture": picture,
};
}

@ -22,8 +22,7 @@ import 'package:diplomaticquarterapp/pages/rateAppointment/rate_appointment_doct
import 'package:diplomaticquarterapp/pages/videocall-webrtc-rnd/webrtc/start_video_call.dart';
import 'package:diplomaticquarterapp/services/authentication/auth_provider.dart';
import 'package:diplomaticquarterapp/services/clinic_services/get_clinic_service.dart';
import 'package:diplomaticquarterapp/services/family_files/family_files_provider.dart'
as family;
import 'package:diplomaticquarterapp/services/family_files/family_files_provider.dart' as family;
import 'package:diplomaticquarterapp/services/livecare_services/livecare_provider.dart';
import 'package:diplomaticquarterapp/services/payfort_services/payfort_view_model.dart';
import 'package:diplomaticquarterapp/services/robo_search/event_provider.dart';
@ -44,6 +43,7 @@ import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
// import 'package:flutter_app_icon_badge/flutter_app_icon_badge.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_svg/flutter_svg.dart';
@ -548,12 +548,19 @@ class _LandingPageState extends State<LandingPage> with WidgetsBindingObserver {
},
),
actions: [
// IconButton(
// //iconSize: 70,
// icon: Icon(
// projectViewModel.isLogin ? Icons.settings : Icons.login,
// color: Theme.of(context).textTheme.headline1.color,
// ),
IconButton(
//iconSize: 70,
icon: Icon(
Icons.video_call,
size: 30,
color: Colors.red,
),
onPressed: () {
Navigator.of(context).pushNamed(
COURSES_LIST_PAGE,
);
},
),
// onPressed: () {
// if (projectViewModel.isLogin)
// Navigator.of(context).pushNamed(

@ -0,0 +1,52 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class ContentWidget extends StatefulWidget {
final String body;
ContentWidget({required this.body});
@override
_ContentWidgetState createState() => _ContentWidgetState();
}
class _ContentWidgetState extends State<ContentWidget> {
bool isExpanded = false;
@override
Widget build(BuildContext context) {
final truncatedText = widget.body.length > 70 ? '${widget.body.substring(0, 70)}... ' : widget.body;
return RichText(
text: TextSpan(
children: [
TextSpan(
text: isExpanded ? widget.body : truncatedText,
style: TextStyle(
color: Color(0xFFA2A2A2),
fontSize: 13,
fontWeight: FontWeight.w400,
height: 13 / 10,
),
),
if (widget.body.length > 100)
TextSpan(
text: isExpanded ? 'See Less' : 'See More',
style: TextStyle(
color: Color(0xFF2B353E),
fontSize: 13,
fontWeight: FontWeight.w500,
height: 13 / 10,
),
recognizer: TapGestureRecognizer()
..onTap = () {
setState(() {
isExpanded = !isExpanded;
});
},
),
],
),
);
}
}

@ -0,0 +1,441 @@
import 'dart:async';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:diplomaticquarterapp/core/viewModels/project_view_model.dart';
import 'package:diplomaticquarterapp/extensions/string_extensions.dart';
import 'package:diplomaticquarterapp/pages/learning/content_widget.dart';
import 'package:diplomaticquarterapp/services/course_service/course_service.dart';
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:video_player/video_player.dart';
class CourseDetailedPage extends StatefulWidget {
@override
State<CourseDetailedPage> createState() => _CourseDetailedPageState();
}
class _CourseDetailedPageState extends State<CourseDetailedPage> {
@override
void initState() {
super.initState();
context.read<CourseServiceProvider>().getCourseById(context);
}
@override
void dispose() {
// _timer?.cancel();
// _controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AppScaffold(
isShowAppBar: false,
isShowDecPage: false,
showNewAppBarTitle: true,
showNewAppBar: true,
appBarTitle: "Course Name",
backgroundColor: Color(0xFFF7F7F7),
onTap: () {},
body: Consumer<CourseServiceProvider>(
builder: (context, provider, child) {
if (provider.courseData != null) {
return ListView(
shrinkWrap: true,
padding: EdgeInsets.zero,
children: [
if (provider.controller != null && provider.controller!.value.isInitialized) ...[
Column(
children: [
AspectRatio(
aspectRatio: provider.controller!.value.aspectRatio,
child: VideoPlayer(provider.controller!),
).onTap(() {
setState(() {
provider.controller!.value.isPlaying ? provider.controller!.pause() : provider.controller!.play();
});
}),
VideoProgressIndicator(
provider.controller!,
padding: EdgeInsets.only(bottom: 0),
colors: VideoProgressColors(
backgroundColor: Color(0xFFD9D9D9),
bufferedColor: Colors.black12,
playedColor: Color(0xFFD02127),
),
allowScrubbing: true,
),
],
)
] else ...[
CachedNetworkImage(
width: double.infinity,
height: MediaQuery.of(context).size.height * .265,
imageUrl: provider.courseData!.nabedJourneyByIdResponseResult!.consultation!.defaultPocData!.image!.thumbUrl!,
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
),
child: Center(
child: Icon(
Icons.play_circle,
size: 35,
color: Colors.white,
),
),
),
placeholder: (context, url) => Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) => Icon(Icons.error),
).onTap(() {
setState(() {
if (provider.controller != null) {
provider.controller!.value.isPlaying ? provider.controller!.pause() : provider.controller!.play();
}
});
}),
],
if (provider.consultation != null) ...[
ListView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: 20),
children: [
SizedBox(height: 20.0),
Text(
context.read<ProjectViewModel>().isArabic ? provider.consultation!.tagValues!.titleAr! : provider.consultation!.tagValues!.title ?? "Basics of Heart",
style: TextStyle(
fontSize: 16.0,
height: 24 / 16,
color: Color(0xFF2E303A),
fontWeight: FontWeight.w600,
),
),
// SizedBox(height: 2.0),
// Text(
// "Heart diseases include conditions like coronary and heart failure, often caused by factors ",
// style: TextStyle(fontSize: 10.0, height: 15 / 10, color: Color(0xFF575757)),
// ),
// SizedBox(height: 6.0),
Text(
provider.consultation!.stats!.videosCount.toString() + " Videos",
style: TextStyle(
fontSize: 10.0,
height: 15 / 10,
color: Color(0xFF2E303A),
fontWeight: FontWeight.w600,
),
),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Row(
// children: [
// Row(
// children: List.generate(5, (starIndex) {
// return Icon(
// Icons.star,
// color: Color(0xFFD1272D),
// size: 15.0,
// );
// }),
// ),
// SizedBox(width: 8.0),
// Text(
// '540 reviews',
// style: TextStyle(
// fontSize: 10.0,
// height: 15 / 10,
// color: Color(0xFF2B353E),
// fontWeight: FontWeight.w700,
// ),
// ),
// ],
// ),
// ],
// ),
],
),
],
SizedBox(height: 30.0),
if (provider.courseTopics != null) ...[
ListView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: 20),
children: provider.courseTopics!.map((topic) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Topic title
Text(
(topic.title.toString() + "(${topic.contentsCount.toString()})") ?? "Topic",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Color(0xFF2B353E),
),
),
SizedBox(height: 8),
...topic.contents!.asMap().entries.map((entry) {
final index = entry.key + 1;
final content = entry.value;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(index.toString()),
SizedBox(
width: 9,
),
Expanded(
flex: 10,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
content.title ?? "Course overview",
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: Color(0xFF2B353E),
),
),
Text(
"Video - ${provider.getDurationOfVideo(content.video!.flavor!.duration!)}",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575757),
),
),
ContentWidget(
body: content.body!,
)
],
),
),
Expanded(
flex: 4,
child: Column(
children: [
SizedBox(
height: 10,
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (content.controller != null) ...[
Container(
width: 55,
child: VideoProgressIndicator(content.controller!,
padding: EdgeInsets.only(bottom: 1),
colors: VideoProgressColors(
backgroundColor: Color(0xFFD9D9D9),
bufferedColor: Colors.black12,
playedColor: Color(0xFF359846),
),
allowScrubbing: true),
)
] else ...[
Container(
width: 55,
child: Container(
height: 4,
color: Colors.black12,
width: double.infinity,
),
)
],
SizedBox(
width: 10,
),
if (content.controller != null) ...[
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: Color(0xFFDFDFDF),
borderRadius: BorderRadius.all(
Radius.circular(30),
),
),
child: Icon(
content.controller!.value.isPlaying ? Icons.pause : Icons.play_arrow,
size: 18,
).onTap(() {
setState(() {
content.controller!.value.isPlaying ? content.controller!.pause() : content.controller!.play();
});
}),
)
] else ...[
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: Color(0xFFDFDFDF),
borderRadius: BorderRadius.all(
Radius.circular(30),
),
),
child: Icon(
Icons.play_arrow,
size: 18,
).onTap(() {
provider.playVideo(context, content: content);
}),
)
]
],
),
],
),
),
],
),
);
}).toList(),
],
);
}).toList(),
),
]
// if(provider.courseTopics != null)...[
// ListView(
// shrinkWrap: true,
// physics: NeverScrollableScrollPhysics(),
// padding: EdgeInsets.symmetric(horizontal: 20),
// children: [
// Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text("1"),
// SizedBox(
// width: 9,
// ),
// Expanded(
// flex: 10,
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// "Course overview",
// style: TextStyle(
// fontSize: 14,
// fontWeight: FontWeight.w600,
// color: Color(0xFF2B353E),
// ),
// ),
// Text(
// "Video - 02:56 mins",
// style: TextStyle(
// fontSize: 12,
// fontWeight: FontWeight.w500,
// color: Color(0xFF575757),
// ),
// ),
// RichText(
// text: TextSpan(
// children: [
// TextSpan(
// text: 'Heart diseases include conditions like coronary and heart failure... ',
// style: TextStyle(
// color: Color(
// 0xFFA2A2A2,
// ),
// fontSize: 13,
// fontWeight: FontWeight.w400,
// height: 13 / 10,
// ),
// ),
// TextSpan(
// text: 'See More',
// style: TextStyle(
// color: Color(0xFF2B353E),
// fontSize: 13,
// fontWeight: FontWeight.w500,
// height: 13 / 10,
// ),
// recognizer: TapGestureRecognizer()..onTap = () {},
// ),
// ],
// ),
// ),
// ],
// ),
// ),
// Expanded(
// flex: 3,
// child: Column(
// children: [
// SizedBox(
// height: 10,
// ),
// Row(
// crossAxisAlignment: CrossAxisAlignment.center,
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Container(
// width: 55,
// child: VideoProgressIndicator(_controller,
// padding: EdgeInsets.only(bottom: 1),
// colors: VideoProgressColors(
// backgroundColor: Color(0xFFD9D9D9),
// bufferedColor: Colors.black12,
// playedColor: Color(0xFF359846),
// ),
// allowScrubbing: true),
// ),
// SizedBox(
// width: 10,
// ),
// Container(
// width: 18,
// height: 18,
// decoration: BoxDecoration(
// color: Color(0xFFDFDFDF),
// borderRadius: BorderRadius.all(
// Radius.circular(30),
// ),
// ),
// child: Icon(
// _controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
// size: 18,
// ).onTap(() {
// setState(() {
// _controller.value.isPlaying ? _controller.pause() : _controller.play();
// });
// }),
// )
// ],
// ),
// ],
// ),
// ),
// ],
// )
// ],
// )
// ]
],
);
} else {
return SizedBox();
}
},
),
);
}
}

@ -0,0 +1,168 @@
import 'dart:async';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:diplomaticquarterapp/core/enum/viewstate.dart';
import 'package:diplomaticquarterapp/extensions/string_extensions.dart';
import 'package:diplomaticquarterapp/models/course/education_journey_list_model.dart' as model;
import 'package:diplomaticquarterapp/routes.dart';
import 'package:diplomaticquarterapp/services/course_service/course_service.dart';
import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart';
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class CourseList extends StatefulWidget {
@override
State<CourseList> createState() => _CourseListState();
}
class _CourseListState extends State<CourseList> {
CourseServiceProvider? _courseServiceProvider;
@override
void initState() {
super.initState();
_courseServiceProvider = context.read<CourseServiceProvider>();
_courseServiceProvider!.getCourses(context);
}
@override
void dispose() {
_clearData();
super.dispose();
}
Future<void> _clearData() async {
await _courseServiceProvider!.clearData();
}
@override
Widget build(BuildContext context) {
return AppScaffold(
isShowAppBar: false,
isShowDecPage: false,
showNewAppBarTitle: true,
showNewAppBar: true,
appBarTitle: "Learning",
backgroundColor: Color(0xFFF7F7F7),
onTap: () {},
body: Consumer<CourseServiceProvider>(builder: (context, provider, child) {
if (provider.nabedJourneyResponse != null) {
return ListView.separated(
itemCount: provider.data!.length,
padding: EdgeInsets.zero,
itemBuilder: (context, index) {
List<model.ContentClass> conClass = provider.data![index].contentClasses!;
model.Datum data = provider.data![index];
return Card(
margin: EdgeInsets.only(left: 20, right: 20, top: 20),
shape: RoundedRectangleBorder(
side: BorderSide(width: 1, color: Color(0xFFEFEFEF)),
borderRadius: BorderRadius.circular(10.0), // Set your desired radius here
),
child: Padding(
padding: EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: CachedNetworkImage(
imageUrl: conClass[index].image!.thumbUrl!,
width: double.infinity,
height: MediaQuery.of(context).size.height * .25,
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
//colorFilter: ColorFilter.mode(Colors.red, BlendMode.colorBurn),
),
),
),
placeholder: (context, url) => Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) => Icon(Icons.error),
),
),
SizedBox(height: 20.0),
Text(
conClass[index].title ?? "",
// "Basics of Heart",
style: TextStyle(
fontSize: 16.0,
height: 24 / 16,
color: Color(0xFF2E303A),
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 2.0),
Text(
//data.date!.toIso8601String(),
"Heart diseases include conditions like coronary and heart failure, often caused by factors ",
style: TextStyle(fontSize: 10.0, height: 15 / 10, color: Color(0xFF575757)),
),
SizedBox(height: 6.0),
Text(
conClass[index].readPercentage.toString(),
// "2 Hours",
style: TextStyle(
fontSize: 10.0,
height: 15 / 10,
color: Color(0xFF2E303A),
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 8.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Row(
children: List.generate(5, (starIndex) {
return Icon(
Icons.star,
color: Color(0xFFD1272D),
size: 15.0,
);
}),
),
SizedBox(width: 8.0),
Text(
'540 reviews',
style: TextStyle(
fontSize: 10.0,
height: 15 / 10,
color: Color(0xFF2B353E),
fontWeight: FontWeight.w700,
),
),
],
),
Icon(
Icons.arrow_right_alt,
size: 18.0,
color: Color(0xFF2B353E),
),
],
),
],
),
),
).onTap(() {
provider.navigate(context, data.id!, onBack: (val) {
provider.clear();
});
});
},
separatorBuilder: (context, index) {
return SizedBox();
},
);
} else {
return SizedBox();
}
}),
);
}
}

@ -0,0 +1,221 @@
import 'package:diplomaticquarterapp/models/course/education_journey_model.dart';
import 'package:diplomaticquarterapp/services/course_service/course_service.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
enum QuestionSheetAction { skip, rewatch }
class QuestionSheet extends StatefulWidget {
final Content content;
final Function(QuestionSheetAction) onActionSelected;
const QuestionSheet({Key? key, required this.content, required this.onActionSelected}) : super(key: key);
@override
_QuestionSheetState createState() => _QuestionSheetState();
}
class _QuestionSheetState extends State<QuestionSheet> {
int? selectedAnswerId;
Answer? selectedAnswer;
@override
Widget build(BuildContext context) {
return AnimatedPadding(
duration: Duration(milliseconds: 300),
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 10),
_QuestionText(questionText: widget.content.question!.text!),
SizedBox(height: 20),
...widget.content.question!.answers!.map((answer) => _AnswerOption(
answer: answer,
isSelected: selectedAnswerId == answer.id,
onSelect: () {
if (selectedAnswer == null) {
setState(() {
selectedAnswerId = answer.id;
selectedAnswer = answer;
});
}
},
)),
SizedBox(height: 20),
if (selectedAnswer != null)
_AnswerDescription(
description: selectedAnswer!.description!,
isCorrect: selectedAnswer!.isCorrect!,
),
SizedBox(height: 20),
if (selectedAnswer != null)
_ActionButtons(
onSkip: () => widget.onActionSelected(QuestionSheetAction.skip),
onRewatch: () => widget.onActionSelected(QuestionSheetAction.rewatch),
),
SizedBox(height: 10),
],
),
),
);
}
}
class _QuestionText extends StatelessWidget {
final String questionText;
const _QuestionText({Key? key, required this.questionText}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text(
questionText,
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
);
}
}
class _AnswerOption extends StatelessWidget {
final Answer answer;
final bool isSelected;
final VoidCallback onSelect;
const _AnswerOption({
Key? key,
required this.answer,
required this.isSelected,
required this.onSelect,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return AnimatedContainer(
width: double.infinity,
duration: Duration(milliseconds: 300),
margin: EdgeInsets.symmetric(vertical: 5),
decoration: BoxDecoration(
border: Border.all(
color: isSelected ? (answer.isCorrect! ? Colors.green : Color(0xFFD1272D)) : Colors.grey,
width: 2,
),
borderRadius: BorderRadius.circular(10),
),
child: TextButton(
onPressed: onSelect,
child: Text(
answer.text!,
style: TextStyle(fontSize: 16),
),
),
);
}
}
class _AnswerDescription extends StatelessWidget {
final String description;
final bool isCorrect;
const _AnswerDescription({
Key? key,
required this.description,
required this.isCorrect,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
description,
style: TextStyle(
color: isCorrect ? Colors.green : Color(0xFFD1272D),
),
),
);
}
}
class _ActionButtons extends StatelessWidget {
final VoidCallback onSkip;
final VoidCallback onRewatch;
const _ActionButtons({
Key? key,
required this.onSkip,
required this.onRewatch,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: _ActionButton(
label: "SKIP",
onPressed: onSkip,
),
),
SizedBox(width: 20),
Expanded(
child: _ActionButton(
label: "RE-WATCH",
onPressed: onRewatch,
),
),
],
);
}
}
class _ActionButton extends StatelessWidget {
final String label;
final VoidCallback onPressed;
const _ActionButton({
Key? key,
required this.label,
required this.onPressed,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return AnimatedContainer(
width: double.infinity,
duration: Duration(milliseconds: 300),
margin: EdgeInsets.symmetric(vertical: 5),
decoration: BoxDecoration(
border: Border.all(color: Color(0xFFD1272D), width: 2),
borderRadius: BorderRadius.circular(10),
),
child: TextButton(
onPressed: onPressed,
child: Text(
label,
style: TextStyle(fontSize: 16),
),
),
);
}
}
// Modified showQuestionSheet function
Future<QuestionSheetAction?> showQuestionSheet(BuildContext con, {required Content content}) {
return showModalBottomSheet<QuestionSheetAction>(
context: con,
isDismissible: false,
isScrollControlled: true,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
builder: (context) => QuestionSheet(
content: content,
onActionSelected: (action) {
Navigator.of(context).pop(action);
},
),
);
}

@ -7,6 +7,8 @@ import 'package:diplomaticquarterapp/pages/TestPage.dart';
import 'package:diplomaticquarterapp/pages/appUpdatePage/app_update_page.dart';
import 'package:diplomaticquarterapp/pages/conference/zoom/call_screen.dart';
import 'package:diplomaticquarterapp/pages/landing/landing_page.dart';
import 'package:diplomaticquarterapp/pages/learning/course_detailed.dart';
import 'package:diplomaticquarterapp/pages/learning/course_list.dart';
import 'package:diplomaticquarterapp/pages/livecare/incoming_call.dart';
import 'package:diplomaticquarterapp/pages/livecare/livecare_home.dart';
import 'package:diplomaticquarterapp/pages/login/confirm-login.dart';
@ -60,6 +62,8 @@ const String HEALTH_WEATHER = 'health-weather';
const APP_UPDATE = 'app-update';
const ZOOM_CALL_PAGE = 'zoom_call_page';
const COURSES_LIST_PAGE = 'courses_list_page';
const COURSES_DETAILED_PAGE = 'courses_detailed_page';
var routes = {
SPLASH: (_) => SplashScreen(),
@ -91,6 +95,9 @@ var routes = {
ZOOM_CALL_PAGE: (_) => CallScreen(),
COURSES_LIST_PAGE: (_) => CourseList(),
COURSES_DETAILED_PAGE: (_) => CourseDetailedPage(),
OPENTOK_CALL_PAGE: (_) => OpenTokConnectCallPage(
apiKey: OPENTOK_API_KEY,
sessionId: '1_MX40NjIwOTk2Mn5-MTY0NzE3MzcwNjA0MH55RUJoZnd0ZGh2U3BPc01ENVZBelQvT1Z-fg',

@ -0,0 +1,302 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:diplomaticquarterapp/config/config.dart';
import 'package:diplomaticquarterapp/config/shared_pref_kay.dart';
import 'package:diplomaticquarterapp/core/service/client/base_app_client.dart';
import 'package:diplomaticquarterapp/models/Authentication/authenticated_user.dart';
import 'package:diplomaticquarterapp/models/course/education_journey_list_model.dart';
import 'package:diplomaticquarterapp/models/course/education_journey_model.dart';
import 'package:diplomaticquarterapp/pages/learning/question_sheet.dart';
import 'package:diplomaticquarterapp/routes.dart';
import 'package:diplomaticquarterapp/services/authentication/auth_provider.dart';
import 'package:diplomaticquarterapp/uitl/app_shared_preferences.dart';
import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart';
import 'package:diplomaticquarterapp/uitl/utils.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
class CourseServiceProvider with ChangeNotifier {
AppSharedPreferences sharedPref = AppSharedPreferences();
AppGlobal appGlobal = AppGlobal();
AuthenticatedUser authUser = AuthenticatedUser();
AuthProvider authProvider = AuthProvider();
bool isDataLoaded = false;
int? selectedJourney;
List<Datum>? data;
PatientEducationJourneyListModel? nabedJourneyResponse;
PatientEducationJourneyModel? courseData;
List<Topic>? courseTopics;
Consultation? consultation;
//Main Video Controller & Timer
VideoPlayerState _videoState = VideoPlayerState.paused;
VideoPlayerState get videoState => _videoState;
VideoPlayerController? controller;
Timer? timer;
// Learning Page
Future<dynamic> fetchPatientCoursesList() async {
print("====== Api Initiated =========");
Map<String, dynamic> request;
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
var data = AuthenticatedUser.fromJson(await this.sharedPref.getObject(USER_PROFILE));
authUser = data;
}
request = {"Channel": 3, "TokenID": "@dm!n", "PatientID": 22335};
dynamic localRes;
await BaseAppClient().post(GET_PATIENT_COURSES_LIST, onSuccess: (response, statusCode) async {
print("====== Api Response =========");
print("${response["NabedJourneyResponseResult"]}");
localRes = PatientEducationJourneyListModel.fromJson(response);
}, onFailure: (String error, int statusCode) {
throw error;
}, body: request);
return localRes;
}
void getCourses(BuildContext context) async {
GifLoaderDialogUtils.showMyDialog(context);
dynamic response = await fetchPatientCoursesList();
GifLoaderDialogUtils.hideDialog(context);
if (response.nabedJourneyResponseResult != null) {
nabedJourneyResponse = response;
isDataLoaded = true;
data = nabedJourneyResponse!.nabedJourneyResponseResult!.data;
}
notifyListeners();
}
// Detailed Page
Future<dynamic> fetchPatientCourseById() async {
print("====== Api Initiated =========");
Map<String, dynamic> request;
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
var data = AuthenticatedUser.fromJson(await this.sharedPref.getObject(USER_PROFILE));
authUser = data;
}
request = {"Channel": 3, "TokenID": "@dm!n", "JourneyID": selectedJourney};
dynamic localRes;
await BaseAppClient().post(GET_PATIENT_COURSE_BY_ID, onSuccess: (response, statusCode) async {
print("====== Api Response =========");
print("${response}");
localRes = response;
}, onFailure: (String error, int statusCode) {
throw error;
}, body: request);
return localRes;
}
void getCourseById(BuildContext context) async {
GifLoaderDialogUtils.showMyDialog(context);
dynamic response = await fetchPatientCourseById();
GifLoaderDialogUtils.hideDialog(context);
if (response != null) {
courseData = PatientEducationJourneyModel.fromRawJson(jsonEncode(response));
courseTopics = courseData!.nabedJourneyByIdResponseResult!.contentClasses!.first.topics;
consultation = courseData!.nabedJourneyByIdResponseResult!.consultation;
}
notifyListeners();
}
Future navigate(BuildContext context, int journey, {required Function(Object? val) onBack}) async {
selectedJourney = journey;
Navigator.of(context).pushNamed(COURSES_DETAILED_PAGE).then((val) {
onBack(val);
});
}
String getDurationOfVideo(int duration) {
final durationInSeconds = duration;
final minutes = durationInSeconds ~/ 60;
final seconds = durationInSeconds % 60;
return "$minutes:${seconds.toString().padLeft(2, '0')} mins";
}
void playVideo(BuildContext context, {required Content content}) async {
print("OnTap");
if (_videoState == VideoPlayerState.playing) {
controller?.pause();
setVideoState(VideoPlayerState.paused);
} else {
try {
final videoUrl = content.video?.flavor?.downloadable ?? "";
if (videoUrl.isEmpty) {
Utils.showErrorToast("No video URL provided.");
return;
}
controller = VideoPlayerController.networkUrl(
Uri.parse(videoUrl),
formatHint: VideoFormat.hls,
);
await controller!.initialize();
controller!.play();
setVideoState(VideoPlayerState.playing);
notifyListeners();
content.controller = controller;
controller!.addListener(() {
if (controller!.value.isPlaying && timer == null) {
_startTimer(context, content: content);
} else if (!controller!.value.isPlaying && timer != null) {
stopTimer();
}
if (controller!.value.hasError) {
Utils.showErrorToast("Failed to load video.");
controller = null;
setVideoState(VideoPlayerState.paused);
}
});
} catch (e) {
Utils.showErrorToast("Error loading video: $e");
controller = null;
setVideoState(VideoPlayerState.paused);
}
}
}
void setVideoState(VideoPlayerState state) {
_videoState = state;
notifyListeners();
}
void _startTimer(BuildContext context, {required Content content}) {
timer = Timer.periodic(Duration(seconds: 1), (timer) async {
if (controller != null && _videoState == VideoPlayerState.playing) {
final position = await controller!.position;
if (position != null) {
print("Current position: ${position.inSeconds} seconds");
notifyListeners();
if (position.inSeconds == content.question!.triggerAt) {
controller!.pause();
setVideoState(VideoPlayerState.paused);
QuestionSheetAction? action = await showQuestionSheet(context, content: content);
if (action == QuestionSheetAction.skip) {
controller!.play();
setVideoState(VideoPlayerState.playing);
} else if (action == QuestionSheetAction.rewatch) {
playVideo(context, content: content);
}
}
}
}
});
}
void stopTimer() {
timer?.cancel();
timer = null;
}
// void playVideo(BuildContext context, {required Content content}) async {
// print("OnTap");
// if (_videoState == VideoPlayerState.playing) {
// controller?.pause();
// setVideoState(VideoPlayerState.paused);
// } else {
// try {
// controller = VideoPlayerController.networkUrl(
// Uri.parse(
// Platform.isIOS ? content.video!.flavor!.downloadable! : content.video!.flavor!.downloadable!,
// ),
// formatHint: VideoFormat.hls)
// ..initialize()
// ..play();
// notifyListeners();
// content.controller = controller;
// notifyListeners();
// controller!.addListener(() {
// if (controller!.value.isPlaying && timer == null) {
// startTimer(context, content: content);
// notifyListeners();
// } else if (!controller!.value.isPlaying && timer != null) {
// stopTimer();
// notifyListeners();
// }
// });
//
// controller!.addListener(() {
// if (controller!.value.hasError) {
// Utils.showErrorToast("Failed to load video.");
// controller = null;
// setVideoState(VideoPlayerState.paused);
// notifyListeners();
// }
// });
// notifyListeners();
// } catch (e) {
// Utils.showErrorToast("Error loading video: $e");
// controller = null;
// setVideoState(VideoPlayerState.paused);
// notifyListeners();
// }
// controller?.play();
// setVideoState(VideoPlayerState.playing);
// }
// }
//
//
// void setVideoState(VideoPlayerState state) {
// _videoState = state;
// notifyListeners();
// }
//
// void startTimer(BuildContext context, {required Content content}) {
// timer = Timer.periodic(Duration(seconds: 1), (timer) async {
// if (controller != null && _videoState == VideoPlayerState.playing) {
// final position = await controller!.position;
// if (position != null) {
// print("Current position: ${position.inSeconds} seconds");
// if (position.inSeconds == content.question!.triggerAt) {
// print("position: ${position.inSeconds} - ${content.question!.triggerAt} seconds");
// controller!.pause();
// setVideoState(VideoPlayerState.paused);
// QuestionSheetAction? action = await showQuestionSheet(context, content: content);
// if (action == QuestionSheetAction.skip) {
// print("Skip");
// controller!.play();
// } else if (action == QuestionSheetAction.rewatch) {
// print("Re-watch");
// playVideo(context, content: content);
// }
// }
// notifyListeners();
// }
// }
// });
// }
//
// void stopTimer() {
// timer?.cancel();
// timer = null;
// }
void onComplete() {
stopTimer();
setVideoState(VideoPlayerState.completed);
}
Future<void> clearData() async {
data = courseData = nabedJourneyResponse = selectedJourney = courseTopics = consultation = timer = controller = null;
}
Future<void> clear() async {
if (controller != null) controller!.dispose();
courseData = courseTopics = timer = controller = null;
}
}
enum VideoPlayerState {
playing,
paused,
completed,
}

@ -155,11 +155,9 @@ dependencies:
badges: ^3.1.2
# flutter_app_icon_badge: ^2.0.0
# dropdown_search: 5.0.6
youtube_player_flutter: ^9.1.0
video_player: ^2.9.2
# shimmer: ^3.0.0
# carousel_slider: ^4.0.0
# flutter_staggered_grid_view: ^0.7.0
huawei_hmsavailability: ^6.11.0+301
huawei_location: ^6.11.0+301
@ -183,6 +181,7 @@ dependencies:
cloudflare_turnstile: ^2.0.1
dependency_overrides:
dev_dependencies:

Loading…
Cancel
Save