Conflicts:
	lib/screens/dashboard_screen.dart
merge-requests/146/head
hussam al-habibeh 6 years ago
commit 629ccbeda8

@ -13,7 +13,7 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
AF260AD724A4922F006461D3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF260AD624A4922F006461D3 /* ViewController.swift */; };
9CE61EBD24AB366E008D68DD /* ViewControlleraa.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CE61EBC24AB366E008D68DD /* ViewControlleraa.swift */; };
B650DC3076E9D70CB188286A /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93A5F83B23AB032D1E096663 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
@ -46,8 +46,8 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
9CE61EBC24AB366E008D68DD /* ViewControlleraa.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControlleraa.swift; sourceTree = "<group>"; };
9D4B7DB43C6A6C849D2387CE /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
AF260AD624A4922F006461D3 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ViewController.swift; path = ../../../../AndroidStudioProjects/open_tok_app/ios/Runner/ViewController.swift; sourceTree = "<group>"; };
E698D7B14B12DF768FE47A1A /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -106,7 +106,6 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
AF260AD624A4922F006461D3 /* ViewController.swift */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
@ -116,6 +115,7 @@
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
9CE61EBC24AB366E008D68DD /* ViewControlleraa.swift */,
);
path = Runner;
sourceTree = "<group>";
@ -281,8 +281,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
AF260AD724A4922F006461D3 /* ViewController.swift in Sources */,
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
9CE61EBD24AB366E008D68DD /* ViewControlleraa.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

@ -91,7 +91,7 @@
<state key="normal" title="Button" image="muteButton"/>
<state key="selected" image="muteButtonSelected"/>
<connections>
<action selector="didClickMuteButton:" destination="t2c-G5-7AE" eventType="touchUpInside" id="xYM-PE-Zzq"/>
<action selector="didClickMuteButton:" destination="t2c-G5-7AE" eventType="touchUpInside" id="7lr-uj-db1"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ygd-al-G4F" userLabel="switchCameraButton">

@ -0,0 +1,330 @@
//
// ViewController.swift
// Runner
//
// Created by Mohammad Aljammal & Elham Rababah on 23/6/20.
// Copyright © 2020 The Chromium Authors. All rights reserved.
//
import UIKit
import OpenTok
class ViewController: UIViewController {
var session: OTSession?
var publisher: OTPublisher?
var subscriber: OTSubscriber?
var kApiKey:String = ""
var kSessionId:String = ""
var kToken:String = ""
override func viewDidLoad() {
super.viewDidLoad()
setupButtons()
askForMicrophonePermission()
requestCameraPermissionsIfNeeded()
hideVideoMuted()
setupSession()
// Do any additional setup after loading the view.
}
func setupButtons() {
perform(#selector(hideControlButtons), with: nil, afterDelay: 3)
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(remoteVideoTapped(_:)))
view.addGestureRecognizer(tapGestureRecognizer)
view.isUserInteractionEnabled = true
}
// MARK: -Microphone Camera and Permission Request
func askForMicrophonePermission() {
switch AVAudioSession.sharedInstance().recordPermission {
case AVAudioSession.RecordPermission.granted:
break
case AVAudioSession.RecordPermission.denied:
break
case AVAudioSession.RecordPermission.undetermined:
// This is the initial state before a user has made any choice
// You can use this spot to request permission here if you want
AVAudioSession.sharedInstance().requestRecordPermission({ granted in
// Check for granted
})
default:
break
}
}
func notifyUserOfCameraAccessDenial() {
// display a useful message asking the user to grant permissions from within Settings > Privacy > Camera
}
@IBAction func didClickMuteButton(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
publisher!.publishAudio = !sender.isSelected
}
@IBAction func didClickSpeakerButton(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
subscriber?.subscribeToAudio = !sender.isSelected
// resetHideButtonsTimer()
}
@IBAction func didClickVideoMuteButton(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
if publisher!.publishVideo {
publisher!.publishVideo = false
} else {
publisher!.publishVideo = true
}
localVideo.isHidden = sender.isSelected
localVideoMutedBg.isHidden = !sender.isSelected
localVideoMutedIndicator.isHidden = !sender.isSelected
// resetHideButtonsTimer()
}
@IBAction func didClickSwitchCameraButton(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
if sender.isSelected {
publisher!.cameraPosition = AVCaptureDevice.Position.front
} else {
publisher!.cameraPosition = AVCaptureDevice.Position.back
}
/// resetHideButtonsTimer()
}
@IBAction func hangUp(_ sender: UIButton) {
sessionDisconnect()
}
func sessionDisconnect() {
if (session != nil) {
print("disconnecting....")
session!.disconnect(nil)
dismiss(animated: true)
return
}
dismiss(animated: true)
}
// Converted to Swift 5.2 by Swiftify v5.2.26743 - https://swiftify.com/
func requestCameraPermissionsIfNeeded() {
// check camera authorization status
let authStatus: AVAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: .video)
switch authStatus {
case .authorized: break
// camera authorized
// do camera intensive stuff
case .notDetermined:
// request authorization
AVCaptureDevice.requestAccess(for: .video, completionHandler: { granted in
DispatchQueue.main.async(execute: {
if granted {
// do camera intensive stuff
} else {
self.notifyUserOfCameraAccessDenial()
}
})
})
case .restricted, .denied:
DispatchQueue.main.async(execute: {
self.notifyUserOfCameraAccessDenial()
})
default:
break
}
}
func hideVideoMuted() {
remoteVideoMutedIndicator.isHidden = true
localVideoMutedBg.isHidden = true
localVideoMutedIndicator.isHidden = true
}
func setupSession() {
//setup one time session
if (session != nil) {
session = nil
}
session = OTSession(
apiKey: kApiKey,
sessionId: kSessionId,
delegate: self)
var error: OTError?
session!.connect(withToken: kToken,error: &error)
}
func connectToAnOpenTokSession() {
session = OTSession(apiKey: kApiKey, sessionId: kSessionId, delegate: self)
var error: OTError?
session?.connect(withToken: kToken, error: &error)
if error != nil {
print(error!)
}
}
func showAlert(_ string: String?) {
// show alertview on main UI
DispatchQueue.main.async(execute: {
let alertVC = UIAlertController(
title: "OTError",
message: string,
preferredStyle: .alert)
self.present(alertVC, animated: true)
})
}
@IBOutlet weak var localVideo: UIView!
@IBOutlet weak var remoteVideo: UIView!
@IBOutlet weak var controlButtons: UIView!
@IBOutlet weak var remoteVideoMutedIndicator: UIImageView!
@IBOutlet weak var localVideoMutedBg: UIImageView!
@IBOutlet weak var localVideoMutedIndicator: UIImageView!
}
extension ViewController: OTSessionDelegate {
func sessionDidConnect(_ session: OTSession) {
print("The client connected to the OpenTok session.")
setupPublisher()
}
func setupPublisher() {
let settings = OTPublisherSettings()
settings.name = UIDevice.current.name
publisher = OTPublisher(delegate: self, settings: settings)
var error: OTError? = nil
session!.publish(publisher!, error: &error)
if error != nil {
showAlert(error?.localizedDescription)
}
publisher?.view!.frame = CGRect(x: localVideo.bounds.origin.x, y: localVideo.bounds.origin.y, width: localVideo.bounds.size.width, height: localVideo.bounds.size.height)
localVideo.addSubview((publisher?.view)!)
}
func sessionDidDisconnect(_ session: OTSession) {
print("The client disconnected from the OpenTok session.")
}
func session(_ session: OTSession, didFailWithError error: OTError) {
print("The client failed to connect to the OpenTok session: \(error).")
}
func session(
_ session: OTSession,
connectionDestroyed connection: OTConnection
) {
if subscriber?.stream!.connection.connectionId == connection.connectionId {
cleanupSubscriber()
}
sessionDisconnect()
}
func session(_ session: OTSession, streamCreated stream: OTStream) {
subscriber = OTSubscriber(stream: stream, delegate: self)
guard let subscriber = subscriber else {
return
}
var error: OTError?
session.subscribe(subscriber, error: &error)
guard error == nil else {
print(error!)
return
}
guard let subscriberView = subscriber.view else {
return
}
subscriberView.frame = UIScreen.main.bounds
view.insertSubview(subscriberView, at: 0)
// if nil == subscriber {
// setupSubscribe(stream)
// }
}
func setupSubscribe(_ stream: OTStream?) {
subscriber = OTSubscriber(stream: stream!, delegate: self)
var error: OTError? = nil
session!.subscribe(subscriber!, error: &error)
if error != nil {
showAlert(error!.localizedDescription)
}
}
func session(_ session: OTSession, streamDestroyed stream: OTStream) {
if subscriber?.stream?.streamId == stream.streamId {
cleanupSubscriber()
}
print("A stream was destroyed in the session.")
}
func cleanupSubscriber() {
subscriber?.view!.removeFromSuperview()
subscriber = nil
}
}
extension ViewController: OTPublisherDelegate {
func publisher(_ publisher: OTPublisherKit, didFailWithError error: OTError) {
print("The publisher failed: \(error)")
}
@objc func remoteVideoTapped(_ recognizer: UITapGestureRecognizer?) {
if controlButtons.isHidden {
controlButtons.isHidden = false
perform(#selector(hideControlButtons), with: nil, afterDelay: 3)
}
}
}
extension ViewController: OTSubscriberDelegate {
public func subscriberDidConnect(toStream subscriber: OTSubscriberKit) {
print("The subscriber did connect to the stream.")
}
public func subscriber(_ subscriber: OTSubscriberKit, didFailWithError error: OTError) {
print("The subscriber failed to connect to the stream.")
}
@objc func hideControlButtons() {
controlButtons.isHidden = true
}
}

@ -5,6 +5,7 @@ import 'package:doctor_app_flutter/screens/doctor/services_screen.dart';
import 'package:doctor_app_flutter/widgets/shared/app_drawer_widget.dart';
import 'package:doctor_app_flutter/widgets/shared/bottom_nav_bar.dart';
import 'package:doctor_app_flutter/widgets/shared/profile_image_widget.dart';
import 'package:doctor_app_flutter/widgets/shared/user-guid/app_showcase_widget.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:hexcolor/hexcolor.dart';
@ -59,7 +60,9 @@ class _LandingPageState extends State<LandingPage> {
physics: NeverScrollableScrollPhysics(),
controller: pageController,
children: [
DashboardScreen(),
ShowCaseWidget(
builder: Builder(builder: (context) => DashboardScreen()),
),
MessagesScreen(),
MyScheduleScreen(),
ServicesScreen()

@ -1,24 +1,24 @@
class LiveCarePendingListResponse {
Null acceptedBy;
Null acceptedOn;
dynamic acceptedBy;
dynamic acceptedOn;
int age;
Null appointmentNo;
dynamic appointmentNo;
String arrivalTime;
String arrivalTimeD;
int callStatus;
String clientRequestID;
String clinicName;
Null consoltationEnd;
Null consultationNotes;
Null createdOn;
dynamic consoltationEnd;
dynamic consultationNotes;
dynamic createdOn;
String dateOfBirth;
String deviceToken;
String deviceType;
Null doctorName;
dynamic doctorName;
String editOn;
String gender;
bool isFollowUP;
Null isFromVida;
dynamic isFromVida;
int isLoginB;
bool isOutKSA;
int isRejected;
@ -26,16 +26,16 @@ class LiveCarePendingListResponse {
double latitude;
double longitude;
String mobileNumber;
Null openSession;
Null openTokenID;
dynamic openSession;
dynamic openTokenID;
String patientID;
String patientName;
int patientStatus;
String preferredLanguage;
int projectID;
int scoring;
double scoring;
int serviceID;
Null tokenID;
dynamic tokenID;
int vCID;
String voipToken;

@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:doctor_app_flutter/client/base_app_client.dart';
import 'package:doctor_app_flutter/config/config.dart';
import 'package:doctor_app_flutter/models/livecare/get_panding_req_list.dart';
import 'package:doctor_app_flutter/models/livecare/get_pending_res_list.dart';
import 'package:doctor_app_flutter/models/livecare/start_call_req.dart';
import 'package:doctor_app_flutter/util/dr_app_shared_pref.dart';
import 'package:doctor_app_flutter/util/helpers.dart';
@ -12,7 +13,7 @@ import 'package:doctor_app_flutter/config/shared_pref_kay.dart';
class LiveCareProvider with ChangeNotifier {
DrAppSharedPreferances sharedPref = new DrAppSharedPreferances();
var liveCarePendingList = [];
List<LiveCarePendingListResponse> liveCarePendingList = [];
var inCallResponse = {};
bool isFinished = true;
bool hasError = false;
@ -25,7 +26,7 @@ class LiveCareProvider with ChangeNotifier {
var profile = await sharedPref.getObj(DOCTOR_PROFILE);
_pendingRequestModel.projectID = await sharedPref.getInt(PROJECT_ID);
_pendingRequestModel.doctorID = profile['DoctorID'];
_pendingRequestModel.sErServiceID = "1,3";
_pendingRequestModel.sErServiceID = "1,3,7";
_pendingRequestModel.sourceID = 1;
_pendingRequestModel.patientData = PatientData(isOutKSA: false);
resetDefaultValues();
@ -33,7 +34,10 @@ class LiveCareProvider with ChangeNotifier {
await BaseAppClient.post(GET_LIVECARE_PENDINGLIST,
onSuccess: (response, statusCode) async {
isFinished = true;
liveCarePendingList = response["List_PendingPatientList"];
liveCarePendingList = [];
response['List_PendingPatientList'].forEach((v) {
liveCarePendingList.add(new LiveCarePendingListResponse.fromJson(v));
});
}, onFailure: (String error, int statusCode) {
isFinished = true;
throw error;

@ -17,6 +17,8 @@ import 'package:doctor_app_flutter/util/dr_app_shared_pref.dart';
import 'package:doctor_app_flutter/util/helpers.dart';
import 'package:doctor_app_flutter/util/translations_delegate_base.dart';
import 'package:doctor_app_flutter/widgets/shared/app_scaffold_widget.dart';
import 'package:doctor_app_flutter/widgets/shared/user-guid/app_showcase.dart';
import 'package:doctor_app_flutter/widgets/shared/user-guid/app_showcase_widget.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:percent_indicator/circular_percent_indicator.dart';
@ -59,9 +61,13 @@ class _DashboardScreenState extends State<DashboardScreen> {
}
_isInit = false;
}
BuildContext myContext;
GlobalKey _one = GlobalKey();
@override
Widget build(BuildContext context) {
myContext = context;
hospitalProvider = Provider.of(context);
authProvider = Provider.of(context);
projectsProvider = Provider.of(context);
@ -84,28 +90,49 @@ class _DashboardScreenState extends State<DashboardScreen> {
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 10, top: 10),
child: AppText(
authProvider.selectedClinicName != null
? authProvider.selectedClinicName
: '',
fontWeight: FontWeight.bold,
fontSize: SizeConfig.textMultiplier * 2.5,
Expanded(
flex: 1,
child: Container(
margin: EdgeInsets.only(left: 10, top: 10),
child: AppText(
authProvider.selectedClinicName != null
? authProvider.selectedClinicName
: '',
fontWeight: FontWeight.bold,
fontSize: SizeConfig.textMultiplier * 2.5,
),
alignment: Alignment.centerLeft,
),
alignment: Alignment.centerLeft,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
InkWell(
onTap: () {
showCupertinoPicker(
context: context,
actionList: projectsProvider.clinicList);
},
child: Container(
margin:
EdgeInsets.only(left: 5, top: 10, right: 5),
child: Icon(
Icons.settings,
size: SizeConfig.textMultiplier * 2.5,
)),
),
],
),
InkWell(
onTap: () {
showCupertinoPicker(
context: context,
actionList: projectsProvider.clinicList);
ShowCaseWidget.of(context).startShowCase([_one]);
},
child: Container(
margin:
EdgeInsets.only(left: 10, top: 10, right: 10),
margin: EdgeInsets.only(left: 5, top: 10, right: 5),
child: Icon(
Icons.settings,
Icons.info,
size: SizeConfig.textMultiplier * 2.5,
)),
)
@ -127,28 +154,36 @@ class _DashboardScreenState extends State<DashboardScreen> {
children: <Widget>[
Expanded(
flex: 2,
child: RoundedContainer(
child: CircularPercentIndicator(
radius: 100,
animation: true,
animationDuration: 1200,
lineWidth: 7.0,
percent: .75,
center: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AppText("38",
fontWeight: FontWeight.bold,
fontSize: SizeConfig.textMultiplier * 4),
AppText(TranslationBase.of(context).outPatients,
fontWeight: FontWeight.normal,
fontSize: SizeConfig.textMultiplier * 1.1,
color: Colors.grey[800]),
],
child: AppShowcase(
key: _one,
description: "Test User Guid",
onSkipClick: () {
ShowCaseWidget.of(context).dismiss();
},
child: RoundedContainer(
child: CircularPercentIndicator(
radius: 100,
animation: true,
animationDuration: 1200,
lineWidth: 7.0,
percent: .75,
center: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AppText("38",
fontWeight: FontWeight.bold,
fontSize: SizeConfig.textMultiplier * 4),
AppText(
TranslationBase.of(context).outPatients,
fontWeight: FontWeight.normal,
fontSize: SizeConfig.textMultiplier * 1.1,
color: Colors.grey[800]),
],
),
circularStrokeCap: CircularStrokeCap.butt,
backgroundColor: Colors.blueGrey[100],
progressColor: Colors.red,
),
circularStrokeCap: CircularStrokeCap.butt,
backgroundColor: Colors.blueGrey[100],
progressColor: Colors.red,
),
),
),
@ -551,7 +586,6 @@ class _DashboardScreenState extends State<DashboardScreen> {
changeIsLoading(true);
Map profile = await sharedPref.getObj(DOCTOR_PROFILE);
DoctorProfileModel doctorProfile = new DoctorProfileModel.fromJson(profile);
ProfileReqModel docInfo = new ProfileReqModel(
doctorID: doctorProfile.doctorID,
clinicID: clinicId,
@ -559,6 +593,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
projectID: doctorProfile.projectID,
tokenID: '',
languageID: 2);
// authProvider.getDocProfiles(docInfo)
authProvider.getDocProfiles(docInfo.toJson()).then((res) async {
changeIsLoading(false);

@ -1,6 +1,5 @@
import 'package:doctor_app_flutter/config/size_config.dart';
import 'package:doctor_app_flutter/providers/livecare_provider.dart';
import 'package:doctor_app_flutter/util/VideoChannel.dart';
import 'package:doctor_app_flutter/util/dr_app_shared_pref.dart';
import 'package:doctor_app_flutter/util/helpers.dart';
import 'package:doctor_app_flutter/widgets/shared/app_scaffold_widget.dart';
@ -30,14 +29,18 @@ class LiveCarePandingListScreen extends StatefulWidget {
}
class _LiveCarePandingListState extends State<LiveCarePandingListScreen> {
var _data = [];
List<LiveCarePendingListResponse> _data = [];
Helpers helpers = new Helpers();
bool _isInit = true;
LiveCareProvider _liveCareProvider;
@override
void didChangeDependencies() {
super.didChangeDependencies();
_liveCareProvider = Provider.of<LiveCareProvider>(context);
if (_data.isEmpty) pendingList();
if (_isInit) {
_liveCareProvider = Provider.of<LiveCareProvider>(context);
pendingList();
}
_isInit = false;
}
@override
@ -64,7 +67,8 @@ class _LiveCarePandingListState extends State<LiveCarePandingListScreen> {
),
)
: Column(
children: _data.map<Widget>((item) {
children: _liveCareProvider.liveCarePendingList
.map<Widget>((item) {
return Container(
decoration: myBoxDecoration(),
child: InkWell(
@ -76,124 +80,121 @@ class _LiveCarePandingListState extends State<LiveCarePandingListScreen> {
// decoration: myBoxDecoration(),
child: Row(
children: <Widget>[
Column(
children: <Widget>[
Container(
decoration:
BoxDecoration(
gradient:
LinearGradient(
begin:
Alignment(
-1,
-1),
end:
Alignment(
1,
1),
colors: [
Colors.grey[
100],
Colors.grey[
200],
]),
boxShadow: [
BoxShadow(
color: Color
.fromRGBO(
0,
0,
0,
0.08),
offset:
Offset(
0.0,
5.0),
blurRadius:
16.0)
],
borderRadius:
BorderRadius.all(
Radius.circular(
50.0)),
),
width: 80,
height: 80,
child: Icon(
item["Gender"] ==
"1"
? DoctorApp.male
: DoctorApp
.femaleicon,
size: 80,
)),
],
),
SizedBox(
width: 20,
),
Column(
crossAxisAlignment:
CrossAxisAlignment
.start,
children: <Widget>[
AppText(
item["PatientName"],
fontSize: 2.0 *
SizeConfig
.textMultiplier,
fontWeight:
FontWeight.bold,
backGroundcolor:
Colors.white,
),
SizedBox(
height: 8,
),
AppText(
TranslationBase.of(
context)
.fileNo +
item["PatientID"]
.toString(),
fontSize: 2.0 *
SizeConfig
.textMultiplier,
fontWeight:
FontWeight.bold,
backGroundcolor:
Colors.white,
),
// SizedBox(
// height: 8,
// ),
AppText(
TranslationBase.of(
context)
.age +
' ' +
item["Age"]
.toString(),
fontSize: 2.0 *
SizeConfig
.textMultiplier,
fontWeight:
FontWeight.bold,
backGroundcolor:
Colors.white,
),
SizedBox(
height: 8,
),
],
),
SizedBox(
width: 80,
Expanded(
flex: 4,
child: Row(
children: <Widget>[
Column(
children: <Widget>[
Container(
decoration:
BoxDecoration(
gradient: LinearGradient(
begin: Alignment(
-1,
-1),
end: Alignment(
1, 1),
colors: [
Colors.grey[
100],
Colors.grey[
200],
]),
boxShadow: [
BoxShadow(
color: Color.fromRGBO(
0,
0,
0,
0.08),
offset: Offset(
0.0,
5.0),
blurRadius:
16.0)
],
borderRadius:
BorderRadius.all(
Radius.circular(
50.0)),
),
width: 80,
height: 80,
child: Icon(
item.gender ==
"1"
? DoctorApp
.male
: DoctorApp
.femaleicon,
size: 80,
)),
],
),
SizedBox(
width: 20,
),
Column(
crossAxisAlignment:
CrossAxisAlignment
.start,
children: <Widget>[
AppText(
item.patientName,
fontSize: 2.0 *
SizeConfig
.textMultiplier,
fontWeight:
FontWeight
.bold,
backGroundcolor:
Colors.white,
),
SizedBox(
height: 8,
),
AppText(
TranslationBase.of(
context)
.fileNo +
item.patientID
.toString(),
fontSize: 2.0 *
SizeConfig
.textMultiplier,
fontWeight:
FontWeight
.bold,
backGroundcolor:
Colors.white,
),
AppText(
TranslationBase.of(
context)
.age +
' ' +
item.age
.toString(),
fontSize: 2.0 *
SizeConfig
.textMultiplier,
fontWeight:
FontWeight
.bold,
backGroundcolor:
Colors.white,
),
SizedBox(
height: 8,
),
],
),
],
),
),
// Divider(color: Colors.grey
Expanded(
flex: 3,
flex: 1,
child: IconButton(
icon: Icon(
Icons.video_call,
@ -202,8 +203,7 @@ class _LiveCarePandingListState extends State<LiveCarePandingListScreen> {
color: Colors
.green, //Colors.black,
onPressed: () => {
_isInit = true,
sharedPref.setObj(
LIVE_CARE_PATIENT,
item),
@ -220,15 +220,7 @@ class _LiveCarePandingListState extends State<LiveCarePandingListScreen> {
],
),
),
onTap: () {
sharedPref.setObj(
LIVE_CARE_PATIENT,
item);
Navigator.of(context)
.pushNamed(
VIDEO_CALL);
},
onTap: () {},
),
);
}).toList(),
@ -241,51 +233,6 @@ class _LiveCarePandingListState extends State<LiveCarePandingListScreen> {
_liveCareProvider.getpendingList().then((result) {
this.setState(() {
_data = _liveCareProvider.liveCarePendingList;
// _data = [
// {
// "AcceptedBy": null,
// "AcceptedOn": null,
// "Age": 33,
// "AppointmentNo": null,
// "ArrivalTime": "2020-06-14 09:34",
// "ArrivalTimeD": "/Date(1592116471237+0300)/",
// "CallStatus": 1,
// "ClientRequestID": "2344670985-1231755-1584378710",
// "ClinicName": "ER Clinic",
// "ConsoltationEnd": null,
// "ConsultationNotes": null,
// "CreatedOn": null,
// "DateOfBirth": "1987-01-04",
// "DeviceToken":
// "fbxt2XpseU_DpO8Msqa1V6:APA91bHUxsfKpAewTt0DzGg_mSTN0T7KIQM92VpubUTobgTm0-DGcJMgcc3DmERbEiWbyTup3zGttU3_9RBaRBxZUKj9ju5wnTGSrIelEE-qxOK30YnIaDhbhs8fJixACbyzgr72HU8Y",
// "DeviceType": "iOS",
// "DoctorName": null,
// "EditOn": "/Date(1592116471237+0300)/",
// "Gender": "1",
// "IsFollowUP": false,
// "IsFromVida": null,
// "IsLoginB": 0,
// "IsOutKSA": false,
// "IsRejected": 0,
// "Language": "AR",
// "Latitude": 24.708879,
// "Longitude": 46.665827,
// "MobileNumber": "0537503378",
// "OpenSession": null,
// "OpenTokenID": null,
// "PatientID": "1231755",
// "PatientName": "TAMER FANASHEH",
// "PatientStatus": 1,
// "PreferredLanguage": "Ar",
// "ProjectID": 0,
// "Scoring": 0.00,
// "ServiceID": 1,
// "TokenID": null,
// "VC_ID": 2240,
// "VoipToken":
// "8dcc17e2365d4f3c4afadc6b5d625a88407398ee517f0e9beda7f663007e88b9"
// }
// ];
});
});
return true;

@ -22,7 +22,7 @@ class _VideoCallPageState extends State<VideoCallPage> {
String _timmer = '';
LiveCareProvider _liveCareProvider;
bool _isInit = true;
var _tokenData;
var _tokenData;
var patientData = {};
String image_url = 'https://hmgwebservices.com/Images/MobileImages/DUBAI/';
//bool _isOutOfStuck = false;
@ -41,11 +41,11 @@ class _VideoCallPageState extends State<VideoCallPage> {
_tokenData = tokenData;
/* opentok functionalites need to be written */
VideoChannel.openVideoCallScreen(kApiKey: '46209962',
VideoChannel.openVideoCallScreen(
kApiKey: '46209962',
kSessionId: _tokenData["OpenSessionID"],
kToken: _tokenData["OpenTokenID"],
kToken: _tokenData["OpenTokenID"],
);
}
String getTimerTime(int start) {
@ -74,12 +74,6 @@ class _VideoCallPageState extends State<VideoCallPage> {
{helpers.showErrorToast(error), Navigator.of(context).pop()});
}
// @override
// void initState() {
// super.initState();
// }
@override
void dispose() {
super.dispose();
@ -90,8 +84,8 @@ class _VideoCallPageState extends State<VideoCallPage> {
return Scaffold(
body: SafeArea(
child: Container(
height: MediaQuery.of(context).size.height * 1.09,
// width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.white,
),
@ -105,7 +99,7 @@ class _VideoCallPageState extends State<VideoCallPage> {
height: 10.0,
),
Text(
'Calling',
'Dailing...',
style: TextStyle(
color: Colors.deepPurpleAccent,
fontWeight: FontWeight.w300,
@ -146,58 +140,25 @@ class _VideoCallPageState extends State<VideoCallPage> {
),
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.02,
height: MediaQuery.of(context).size.height * .2,
),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
FunctionalButton(
title: 'Speaker',
icon: Icons.phone_in_talk,
Container(
width: 70.0,
height: 70.0,
child: FloatingActionButton(
onPressed: () {
print(_tokenData["OpenSessionID"]);
print(_tokenData["OpenTokenID"]);
VideoChannel.openVideoCallScreen(kApiKey: '46209962',
kSessionId: _tokenData["OpenSessionID"],
kToken: _tokenData["OpenTokenID"],
);
Navigator.of(context).pop();
},
),
FunctionalButton(
title: 'Flip',
icon: Icons.flip_to_back,
onPressed: () {},
),
FunctionalButton(
title: 'Mute',
icon: Icons.mic_off,
onPressed: () {},
),
],
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.1,
),
FloatingActionButton(
onPressed: () {
Navigator.of(context).pop();
},
elevation: 20.0,
shape: CircleBorder(side: BorderSide(color: Colors.red)),
mini: false,
child: Icon(
Icons.call_end,
color: Colors.red,
),
backgroundColor: Colors.red[100],
)
elevation: 30.0,
shape: CircleBorder(side: BorderSide(color: Colors.red)),
mini: false,
child: Icon(
Icons.call_end,
color: Colors.red,
size: 35,
),
backgroundColor: Colors.red[100],
))
],
),
),

@ -0,0 +1,183 @@
/*
* Copyright © 2020, Simform Solutions
* All rights reserved.
* https://github.com/simformsolutions/flutter_showcaseview
*/
/*
Customized By: Ibrahim Albitar
*/
import 'package:flutter/material.dart';
/// Displays an overlay Widget anchored directly above the center of this
/// [AnchoredOverlay].
///
/// The overlay Widget is created by invoking the provided [overlayBuilder].
///
/// The [anchor] position is provided to the [overlayBuilder], but the builder
/// does not have to respect it. In other words, the [overlayBuilder] can
/// interpret the meaning of "anchor" however it wants - the overlay will not
/// be forced to be centered about the [anchor].
///
/// The overlay built by this [AnchoredOverlay] can be conditionally shown
/// and hidden by settings the [showOverlay] property to true or false.
///
/// The [overlayBuilder] is invoked every time this Widget is rebuilt.
///
class AnchoredOverlay extends StatelessWidget {
final bool showOverlay;
final Widget Function(BuildContext, Rect anchorBounds, Offset anchor)
overlayBuilder;
final Widget child;
AnchoredOverlay({
key,
this.showOverlay = false,
this.overlayBuilder,
this.child,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return OverlayBuilder(
showOverlay: showOverlay,
overlayBuilder: (BuildContext overlayContext) {
// To calculate the "anchor" point we grab the render box of
// our parent Container and then we find the center of that box.
RenderBox box = context.findRenderObject() as RenderBox;
final topLeft =
box.size.topLeft(box.localToGlobal(const Offset(0.0, 0.0)));
final bottomRight =
box.size.bottomRight(box.localToGlobal(const Offset(0.0, 0.0)));
final Rect anchorBounds = Rect.fromLTRB(
topLeft.dx,
topLeft.dy,
bottomRight.dx,
bottomRight.dy,
);
final anchorCenter = box.size.center(topLeft);
return overlayBuilder(overlayContext, anchorBounds, anchorCenter);
},
child: child,
);
},
);
}
}
//
// Displays an overlay Widget as constructed by the given [overlayBuilder].
//
// The overlay built by the [overlayBuilder] can be conditionally shown and hidden by settings the [showOverlay]
// property to true or false.
//
// The [overlayBuilder] is invoked every time this Widget is rebuilt.
//
// Implementation note: the reason we rebuild the overlay every time our state changes is because there doesn't seem
// to be any better way to invalidate the overlay itself than to invalidate this Widget.
// Remember, overlay Widgets exist in [OverlayEntry]s which are inaccessible to outside Widgets.
// But if a better approach is found then feel free to use it.
//
class OverlayBuilder extends StatefulWidget {
final bool showOverlay;
final Widget Function(BuildContext) overlayBuilder;
final Widget child;
OverlayBuilder({
key,
this.showOverlay = false,
this.overlayBuilder,
this.child,
}) : super(key: key);
@override
_OverlayBuilderState createState() => _OverlayBuilderState();
}
class _OverlayBuilderState extends State<OverlayBuilder> {
OverlayEntry _overlayEntry;
@override
void initState() {
super.initState();
if (widget.showOverlay) {
WidgetsBinding.instance.addPostFrameCallback((_) => showOverlay());
}
}
@override
void didUpdateWidget(OverlayBuilder oldWidget) {
super.didUpdateWidget(oldWidget);
WidgetsBinding.instance.addPostFrameCallback((_) => syncWidgetAndOverlay());
}
@override
void reassemble() {
super.reassemble();
WidgetsBinding.instance.addPostFrameCallback((_) => syncWidgetAndOverlay());
}
@override
void dispose() {
if (isShowingOverlay()) {
hideOverlay();
}
super.dispose();
}
bool isShowingOverlay() => _overlayEntry != null;
void showOverlay() {
if (_overlayEntry == null) {
// Create the overlay.
_overlayEntry = OverlayEntry(
builder: widget.overlayBuilder,
);
addToOverlay(_overlayEntry);
} else {
// Rebuild overlay.
buildOverlay();
}
}
void addToOverlay(OverlayEntry overlayEntry) async {
Overlay.of(context).insert(overlayEntry);
final overlay = Overlay.of(context);
if (overlayEntry == null)
WidgetsBinding.instance
.addPostFrameCallback((_) => overlay.insert(overlayEntry));
}
void hideOverlay() {
if (_overlayEntry != null) {
_overlayEntry.remove();
_overlayEntry = null;
}
}
void syncWidgetAndOverlay() {
if (isShowingOverlay() && !widget.showOverlay) {
hideOverlay();
} else if (!isShowingOverlay() && widget.showOverlay) {
showOverlay();
}
}
void buildOverlay() async {
WidgetsBinding.instance
.addPostFrameCallback((_) => _overlayEntry?.markNeedsBuild());
}
@override
Widget build(BuildContext context) {
buildOverlay();
return widget.child;
}
}

@ -0,0 +1,75 @@
/*
* Copyright © 2020, Simform Solutions
* All rights reserved.
* https://github.com/simformsolutions/flutter_showcaseview
*/
/*
Customized By: Ibrahim Albitar
*/
import 'package:flutter/material.dart';
class GetPosition {
final GlobalKey key;
GetPosition({this.key});
Rect getRect() {
RenderBox box = key.currentContext.findRenderObject();
final topLeft = box.size.topLeft(box.localToGlobal(const Offset(0.0, 0.0)));
final bottomRight =
box.size.bottomRight(box.localToGlobal(const Offset(0.0, 0.0)));
Rect rect = Rect.fromLTRB(
topLeft.dx,
topLeft.dy,
bottomRight.dx,
bottomRight.dy,
);
return rect;
}
///Get the bottom position of the widget
double getBottom() {
RenderBox box = key.currentContext.findRenderObject();
final bottomRight =
box.size.bottomRight(box.localToGlobal(const Offset(0.0, 0.0)));
return bottomRight.dy;
}
///Get the top position of the widget
double getTop() {
RenderBox box = key.currentContext.findRenderObject();
final topLeft = box.size.topLeft(box.localToGlobal(const Offset(0.0, 0.0)));
return topLeft.dy;
}
///Get the left position of the widget
double getLeft() {
RenderBox box = key.currentContext.findRenderObject();
final topLeft = box.size.topLeft(box.localToGlobal(const Offset(0.0, 0.0)));
return topLeft.dx;
}
///Get the right position of the widget
double getRight() {
RenderBox box = key.currentContext.findRenderObject();
final bottomRight =
box.size.bottomRight(box.localToGlobal(const Offset(0.0, 0.0)));
return bottomRight.dx;
}
double getHeight() {
return getBottom() - getTop();
}
double getWidth() {
return getRight() - getLeft();
}
double getCenter() {
return (getLeft() + getRight()) / 2;
}
}

@ -0,0 +1,42 @@
/*
* Copyright © 2020, Simform Solutions
* All rights reserved.
* https://github.com/simformsolutions/flutter_showcaseview
*/
/*
Customized By: Ibrahim Albitar
*/
import 'package:flutter/material.dart';
class ShapePainter extends CustomPainter {
Rect rect;
final ShapeBorder shapeBorder;
final Color color;
final double opacity;
ShapePainter({
@required this.rect,
this.color,
this.shapeBorder,
this.opacity,
});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint();
paint.color = color.withOpacity(opacity);
RRect outer =
RRect.fromLTRBR(0, 0, size.width, size.height, Radius.circular(0));
double radius = shapeBorder == CircleBorder() ? 50 : 3;
RRect inner = RRect.fromRectAndRadius(rect, Radius.circular(radius));
canvas.drawDRRect(outer, inner, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}

@ -0,0 +1,349 @@
/*
* Copyright © 2020, Simform Solutions
* All rights reserved.
* https://github.com/simformsolutions/flutter_showcaseview
*/
/*
Customized By: Ibrahim Albitar
*/
import 'package:doctor_app_flutter/widgets/shared/app_texts_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'app_anchored_overlay_widget.dart';
import 'app_get_position.dart';
import 'app_shape_painter.dart';
import 'app_showcase_widget.dart';
import 'app_tool_tip_widget.dart';
class AppShowcase extends StatefulWidget {
final Widget child;
final String title;
final String description;
final ShapeBorder shapeBorder;
final TextStyle titleTextStyle;
final TextStyle descTextStyle;
final GlobalKey key;
final Color overlayColor;
final double overlayOpacity;
final Widget container;
final Color showcaseBackgroundColor;
final Color textColor;
final bool showArrow;
final double height;
final double width;
final Duration animationDuration;
final VoidCallback onToolTipClick;
final VoidCallback onTargetClick;
final VoidCallback onSkipClick;
final bool disposeOnTap;
final bool disableAnimation;
const AppShowcase(
{@required this.key,
@required this.child,
this.title,
@required this.description,
this.shapeBorder,
this.overlayColor = Colors.black,
this.overlayOpacity = 0.75,
this.titleTextStyle,
this.descTextStyle,
this.showcaseBackgroundColor = Colors.white,
this.textColor = Colors.black,
this.showArrow = true,
this.onTargetClick,
this.onSkipClick,
this.disposeOnTap,
this.animationDuration = const Duration(milliseconds: 2000),
this.disableAnimation = false})
: height = null,
width = null,
container = null,
this.onToolTipClick = null,
assert(overlayOpacity >= 0.0 && overlayOpacity <= 1.0,
"overlay opacity should be >= 0.0 and <= 1.0."),
assert(
onTargetClick == null
? true
: (disposeOnTap == null ? false : true),
"disposeOnTap is required if you're using onTargetClick"),
assert(
disposeOnTap == null
? true
: (onTargetClick == null ? false : true),
"onTargetClick is required if you're using disposeOnTap"),
assert(key != null ||
child != null ||
title != null ||
showArrow != null ||
description != null ||
shapeBorder != null ||
overlayColor != null ||
titleTextStyle != null ||
descTextStyle != null ||
showcaseBackgroundColor != null ||
textColor != null ||
shapeBorder != null ||
animationDuration != null);
const AppShowcase.withWidget(
{this.key,
@required this.child,
@required this.container,
@required this.height,
@required this.width,
this.title,
this.description,
this.shapeBorder,
this.overlayColor = Colors.black,
this.overlayOpacity = 0.75,
this.titleTextStyle,
this.descTextStyle,
this.showcaseBackgroundColor = Colors.white,
this.textColor = Colors.black,
this.onTargetClick,
this.onSkipClick,
this.disposeOnTap,
this.animationDuration = const Duration(milliseconds: 2000),
this.disableAnimation = false})
: this.showArrow = false,
this.onToolTipClick = null,
assert(overlayOpacity >= 0.0 && overlayOpacity <= 1.0,
"overlay opacity should be >= 0.0 and <= 1.0."),
assert(key != null ||
child != null ||
title != null ||
description != null ||
shapeBorder != null ||
overlayColor != null ||
titleTextStyle != null ||
descTextStyle != null ||
showcaseBackgroundColor != null ||
textColor != null ||
shapeBorder != null ||
animationDuration != null);
@override
_AppShowcaseState createState() => _AppShowcaseState();
}
class _AppShowcaseState extends State<AppShowcase>
with TickerProviderStateMixin {
bool _showShowCase = false;
Animation<double> _slideAnimation;
AnimationController _slideAnimationController;
GetPosition position;
@override
void initState() {
super.initState();
_slideAnimationController = AnimationController(
duration: widget.animationDuration,
vsync: this,
)..addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed) {
_slideAnimationController.reverse();
}
if (_slideAnimationController.isDismissed) {
if (!widget.disableAnimation) {
_slideAnimationController.forward();
}
}
});
_slideAnimation = CurvedAnimation(
parent: _slideAnimationController,
curve: Curves.easeInOut,
);
position = GetPosition(key: widget.key);
}
@override
void dispose() {
_slideAnimationController.dispose();
super.dispose();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
showOverlay();
}
///
/// show overlay if there is any target widget
///
void showOverlay() {
GlobalKey activeStep = ShowCaseWidget.activeTargetWidget(context);
setState(() {
_showShowCase = activeStep == widget.key;
});
if (activeStep == widget.key) {
if (!widget.disableAnimation) {
_slideAnimationController.forward();
}
}
}
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return AnchoredOverlay(
overlayBuilder: (BuildContext context, Rect rectBound, Offset offset) =>
buildOverlayOnTarget(offset, rectBound.size, rectBound, size),
showOverlay: true,
child: widget.child,
);
}
_nextIfAny() {
ShowCaseWidget.of(context).completed(widget.key);
if (!widget.disableAnimation) {
_slideAnimationController.forward();
}
}
_getOnTargetTap() {
if (widget.disposeOnTap == true) {
return widget.onTargetClick == null
? () {
ShowCaseWidget.of(context).dismiss();
}
: () {
ShowCaseWidget.of(context).dismiss();
widget.onTargetClick();
};
} else {
return widget.onTargetClick ?? _nextIfAny;
}
}
_getOnTooltipTap() {
if (widget.disposeOnTap == true) {
return widget.onToolTipClick == null
? () {
ShowCaseWidget.of(context).dismiss();
}
: () {
ShowCaseWidget.of(context).dismiss();
widget.onToolTipClick();
};
} else {
return widget.onToolTipClick ?? () {};
}
}
buildOverlayOnTarget(
Offset offset,
Size size,
Rect rectBound,
Size screenSize,
) =>
Visibility(
visible: _showShowCase,
maintainAnimation: true,
maintainState: true,
child: Stack(
children: [
GestureDetector(
onTap: _nextIfAny,
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: CustomPaint(
painter: ShapePainter(
opacity: widget.overlayOpacity,
rect: position.getRect(),
shapeBorder: widget.shapeBorder,
color: widget.overlayColor),
),
),
),
_TargetWidget(
offset: offset,
size: size,
onTap: _getOnTargetTap(),
shapeBorder: widget.shapeBorder,
),
AppToolTipWidget(
position: position,
offset: offset,
screenSize: screenSize,
title: widget.title,
description: widget.description,
animationOffset: _slideAnimation,
titleTextStyle: widget.titleTextStyle,
descTextStyle: widget.descTextStyle,
container: widget.container,
tooltipColor: widget.showcaseBackgroundColor,
textColor: widget.textColor,
showArrow: widget.showArrow,
contentHeight: widget.height,
contentWidth: widget.width,
onTooltipTap: _getOnTooltipTap(),
),
GestureDetector(
child: AppText(
"Skip",
color: Colors.white,
fontSize: 20,
marginRight: 15,
marginLeft: 15,
marginTop: 15,
),
onTap: widget.onSkipClick)
],
),
);
}
class _TargetWidget extends StatelessWidget {
final Offset offset;
final Size size;
final Animation<double> widthAnimation;
final VoidCallback onTap;
final ShapeBorder shapeBorder;
_TargetWidget({
Key key,
@required this.offset,
this.size,
this.widthAnimation,
this.onTap,
this.shapeBorder,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Positioned(
top: offset.dy,
left: offset.dx,
child: FractionalTranslation(
translation: const Offset(-0.5, -0.5),
child: GestureDetector(
onTap: onTap,
child: Container(
height: size.height + 16,
width: size.width + 16,
decoration: ShapeDecoration(
shape: shapeBorder ??
RoundedRectangleBorder(
borderRadius: const BorderRadius.all(
Radius.circular(8),
),
),
),
),
),
),
);
}
}

@ -0,0 +1,97 @@
/*
* Copyright © 2020, Simform Solutions
* All rights reserved.
* https://github.com/simformsolutions/flutter_showcaseview
*/
/*
Customized By: Ibrahim Albitar
*/
import 'package:flutter/material.dart';
class ShowCaseWidget extends StatefulWidget {
final Builder builder;
final VoidCallback onFinish;
const ShowCaseWidget({@required this.builder, this.onFinish});
static activeTargetWidget(BuildContext context) {
return context
.dependOnInheritedWidgetOfExactType<_InheritedShowCaseView>()
.activeWidgetIds;
}
static ShowCaseWidgetState of(BuildContext context) {
ShowCaseWidgetState state =
context.findAncestorStateOfType<ShowCaseWidgetState>();
if (state != null) {
return context.findAncestorStateOfType<ShowCaseWidgetState>();
} else {
throw Exception('Please provide ShowCaseView context');
}
}
@override
ShowCaseWidgetState createState() => ShowCaseWidgetState();
}
class ShowCaseWidgetState extends State<ShowCaseWidget> {
List<GlobalKey> ids;
int activeWidgetId;
void startShowCase(List<GlobalKey> widgetIds) {
setState(() {
this.ids = widgetIds;
activeWidgetId = 0;
});
}
void completed(GlobalKey id) {
if (ids != null && ids[activeWidgetId] == id) {
setState(() {
++activeWidgetId;
if (activeWidgetId >= ids.length) {
_cleanupAfterSteps();
if (widget.onFinish != null) {
widget.onFinish();
}
}
});
}
}
void dismiss() {
setState(() {
_cleanupAfterSteps();
});
}
void _cleanupAfterSteps() {
ids = null;
activeWidgetId = null;
}
@override
Widget build(BuildContext context) {
return _InheritedShowCaseView(
child: widget.builder,
activeWidgetIds: ids?.elementAt(activeWidgetId),
);
}
}
class _InheritedShowCaseView extends InheritedWidget {
final GlobalKey activeWidgetIds;
_InheritedShowCaseView({
@required this.activeWidgetIds,
@required child,
}) : super(child: child);
@override
bool updateShouldNotify(_InheritedShowCaseView oldWidget) =>
oldWidget.activeWidgetIds != activeWidgetIds;
}

@ -0,0 +1,289 @@
/*
* Copyright © 2020, Simform Solutions
* All rights reserved.
* https://github.com/simformsolutions/flutter_showcaseview
*/
/*
Customized By: Ibrahim Albitar
*/
import 'package:doctor_app_flutter/icons_app/doctor_app_icons.dart';
import 'package:doctor_app_flutter/widgets/shared/app_texts_widget.dart';
import 'package:flutter/material.dart';
import 'app_get_position.dart';
class AppToolTipWidget extends StatelessWidget {
final GetPosition position;
final Offset offset;
final Size screenSize;
final String title;
final String description;
final Animation<double> animationOffset;
final TextStyle titleTextStyle;
final TextStyle descTextStyle;
final Widget container;
final Color tooltipColor;
final Color textColor;
final bool showArrow;
final double contentHeight;
final double contentWidth;
static bool isArrowUp;
final VoidCallback onTooltipTap;
AppToolTipWidget({
this.position,
this.offset,
this.screenSize,
this.title,
this.description,
this.animationOffset,
this.titleTextStyle,
this.descTextStyle,
this.container,
this.tooltipColor,
this.textColor,
this.showArrow,
this.contentHeight,
this.contentWidth,
this.onTooltipTap,
});
bool isCloseToTopOrBottom(Offset position) {
double height = 120;
if (contentHeight != null) {
height = contentHeight;
}
return (screenSize.height - position.dy) <= height;
}
String findPositionForContent(Offset position) {
if (isCloseToTopOrBottom(position)) {
return 'ABOVE';
} else {
return 'BELOW';
}
}
double _getTooltipWidth() {
double titleLength = title == null ? 0 : (title.length * 10.0);
double descriptionLength = (description.length * 7.0);
if (titleLength > descriptionLength) {
return titleLength + 10;
} else {
return descriptionLength + 10;
}
}
bool _isLeft() {
double screenWidth = screenSize.width / 3;
return !(screenWidth <= position.getCenter());
}
bool _isRight() {
double screenWidth = screenSize.width / 3;
return ((screenWidth * 2) <= position.getCenter());
}
double _getLeft() {
if (_isLeft()) {
double leftPadding = position.getCenter() - (_getTooltipWidth() * 0.1);
if (leftPadding + _getTooltipWidth() > screenSize.width) {
leftPadding = (screenSize.width - 20) - _getTooltipWidth();
}
if (leftPadding < 20) {
leftPadding = 14;
}
return leftPadding;
} else if (!(_isRight())) {
return position.getCenter() - (_getTooltipWidth() * 0.5);
} else {
return null;
}
}
double _getRight() {
if (_isRight()) {
double rightPadding = position.getCenter() + (_getTooltipWidth() / 2);
if (rightPadding + _getTooltipWidth() > screenSize.width) {
rightPadding = 14;
}
return rightPadding;
} else if (!(_isLeft())) {
return position.getCenter() - (_getTooltipWidth() * 0.5);
} else {
return null;
}
}
double _getSpace() {
double space = position.getCenter() - (contentWidth / 2);
if (space + contentWidth > screenSize.width) {
space = screenSize.width - contentWidth - 8;
} else if (space < (contentWidth / 2)) {
space = 16;
}
return space;
}
@override
Widget build(BuildContext context) {
final contentOrientation = findPositionForContent(offset);
final contentOffsetMultiplier = contentOrientation == "BELOW" ? 1.0 : -1.0;
isArrowUp = contentOffsetMultiplier == 1.0 ? true : false;
final contentY = isArrowUp
? position.getBottom() + (contentOffsetMultiplier * 3)
: position.getTop() + (contentOffsetMultiplier * 3);
final contentFractionalOffset = contentOffsetMultiplier.clamp(-1.0, 0.0);
double paddingTop = isArrowUp ? 22 : 0;
double paddingBottom = isArrowUp ? 0 : 27;
if (!showArrow) {
paddingTop = 10;
paddingBottom = 10;
}
if (container == null) {
return Stack(
children: <Widget>[
showArrow ? _getArrow(contentOffsetMultiplier) : Container(),
Positioned(
top: contentY,
left: _getLeft(),
right: _getRight(),
child: FractionalTranslation(
translation: Offset(0.0, contentFractionalOffset),
child: SlideTransition(
position: Tween<Offset>(
begin: Offset(0.0, contentFractionalOffset / 10),
end: Offset(0.0, 0.100),
).animate(animationOffset),
child: Material(
color: Colors.transparent,
child: Container(
padding:
EdgeInsets.only(top: paddingTop, bottom: paddingBottom),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: GestureDetector(
onTap: onTooltipTap,
child: Container(
width: _getTooltipWidth(),
padding: EdgeInsets.symmetric(vertical: 8),
color: tooltipColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Column(
crossAxisAlignment: title != null
? CrossAxisAlignment.start
: CrossAxisAlignment.center,
children: <Widget>[
title != null
? Row(
children: <Widget>[
Padding(
padding:
const EdgeInsets.all(8.0),
child: Icon(
DoctorApp.search_patient),
),
AppText(
title,
color: textColor,
margin: 2,
fontWeight: FontWeight.bold,
fontSize: 16,
),
],
)
: Container(),
AppText(
description,
color: textColor,
margin: 8,
),
],
),
)
],
),
),
),
),
),
),
),
),
)
],
);
} else {
return Stack(
children: <Widget>[
Positioned(
left: _getSpace(),
top: contentY - 10,
child: FractionalTranslation(
translation: Offset(0.0, contentFractionalOffset),
child: SlideTransition(
position: Tween<Offset>(
begin: Offset(0.0, contentFractionalOffset / 5),
end: Offset(0.0, 0.100),
).animate(animationOffset),
child: Material(
color: Colors.transparent,
child: GestureDetector(
onTap: onTooltipTap,
child: Container(
padding: EdgeInsets.only(
top: paddingTop,
),
color: Colors.transparent,
child: Center(
child: container,
),
),
),
),
),
),
),
],
);
}
}
Widget _getArrow(contentOffsetMultiplier) {
final contentFractionalOffset = contentOffsetMultiplier.clamp(-1.0, 0.0);
return Positioned(
top: isArrowUp ? position.getBottom() : position.getTop() - 1,
left: position.getCenter() - 24,
child: FractionalTranslation(
translation: Offset(0.0, contentFractionalOffset),
child: SlideTransition(
position: Tween<Offset>(
begin: Offset(0.0, contentFractionalOffset / 5),
end: Offset(0.0, 0.150),
).animate(animationOffset),
child: isArrowUp
? Icon(
Icons.arrow_drop_up,
color: tooltipColor,
size: 50,
)
: Icon(
Icons.arrow_drop_down,
color: tooltipColor,
size: 50,
),
),
),
);
}
}

@ -127,13 +127,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "7.1.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
charcode:
dependency: transitive
description:
@ -162,13 +155,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
code_builder:
dependency: transitive
description:
@ -260,13 +246,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.1.4"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
fixnum:
dependency: transitive
description:
@ -371,6 +350,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.4"
image:
dependency: transitive
description:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.12"
imei_plugin:
dependency: "direct main"
description:
@ -482,7 +468,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
version: "1.6.4"
pedantic:
dependency: transitive
description:
@ -511,6 +497,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.0"
platform:
dependency: transitive
description:
@ -683,7 +676,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.16"
version: "0.2.15"
timing:
dependency: transitive
description:
@ -747,6 +740,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
xml:
dependency: transitive
description:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "3.6.1"
yaml:
dependency: transitive
description:
@ -755,5 +755,5 @@ packages:
source: hosted
version: "2.2.1"
sdks:
dart: ">=2.9.0-14.0.dev <3.0.0"
dart: ">=2.7.0 <3.0.0"
flutter: ">=1.12.13+hotfix.5 <2.0.0"

Loading…
Cancel
Save