first commit

This commit is contained in:
Sas Andy
2025-01-31 10:12:08 +07:00
commit 6554441a4b
180 changed files with 8490 additions and 0 deletions

55
lib/app/constant.dart Normal file
View File

@@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
class Constant {
static double designHeight = 982;
static double designWidth = 1512;
static String versi = "Versi 16";
static TextStyle title({required BuildContext context}) {
return TextStyle(
fontSize: Constant.getActualX(context: context, x: 32),
fontWeight: FontWeight.w600,
);
}
static TextStyle subTitle({required BuildContext context}) {
return TextStyle(
fontSize: Constant.getActualX(context: context, x: 22),
fontWeight: FontWeight.w600,
);
}
static TextStyle layananTitle({required BuildContext context}) {
return TextStyle(
fontSize: Constant.getActualX(context: context, x: 18),
fontWeight: FontWeight.w600,
);
}
static TextStyle label({required BuildContext context}) {
return TextStyle(
fontSize: Constant.getActualX(context: context, x: 16),
fontStyle: FontStyle.italic);
}
static TextStyle normal({required BuildContext context}) {
return TextStyle(
fontSize: Constant.getActualX(context: context, x: 16),
);
}
//size convertion
static double getActualX({
required BuildContext context,
required double x,
}) {
return x / designWidth * MediaQuery.of(context).size.width;
}
static double getActualY({
required BuildContext context,
required double y,
}) {
return y / designHeight * MediaQuery.of(context).size.height;
}
}

367
lib/app/print_ticket.dart Normal file
View File

@@ -0,0 +1,367 @@
import 'dart:async';
import 'dart:io';
import 'package:esc_pos_utils/esc_pos_utils.dart';
import 'package:flutter_pos_printer_platform_image_3/flutter_pos_printer_platform_image_3.dart';
import 'package:intl/intl.dart';
import 'package:path_provider/path_provider.dart';
class PrintTicket {
// Future printTest() async {
// List<int> bytes = [];
// var profile = await CapabilityProfile.load();
// var generator = Generator(PaperSize.mm80, profile);
// bytes += generator.setGlobalCodeTable('CP1252');
// bytes += generator.text('TEST PRINT',
// styles: const PosStyles(align: PosAlign.center));
// bytes += generator.cut();
// PrinterManager.instance.send(type: PrinterType.usb, bytes: bytes);
// }
Future printTicket(
{required String boothId,
required String header,
required String date,
required String time,
required String antrianNumb,
required String name,
required String location,
required StringqrCode,
required String isConsult,
required String DoctorName,
required String layananName,
required String capabilityName,
required String footer}) async {
try {
List<int> bytes = [];
final dt = DateFormat('yyyy-MM-dd-HH-mm-ss').format(DateTime.now());
final splited = dt.split('-');
final date = splited[2].toString();
final month = splited[1].toString();
final year = splited[0].toString();
final hour = splited[3].toString();
final minute = splited[4].toString();
final displayDate = "$date/$month/$year - $hour.$minute WIB";
final profile = await CapabilityProfile.load(name: capabilityName);
final avl = await CapabilityProfile.getAvailableProfiles();
// final avl = await CapabilityProfile.getAvailableProfiles(name: "XP-N160I");
// print(avl);
var generator = Generator(PaperSize.mm80, profile);
bytes += generator.text(
"Booth :$boothId",
styles: const PosStyles(
align: PosAlign.right,
bold: true,
height: PosTextSize.size1,
width: PosTextSize.size1),
);
// bytes += generator.text(
// "capability :$capabilityName",
// styles: const PosStyles(
// align: PosAlign.right,
// bold: true,
// height: PosTextSize.size1,
// width: PosTextSize.size1),
// );
bytes += generator.hr(ch: " ");
bytes += generator.text(
header,
styles: const PosStyles(
align: PosAlign.center,
bold: true,
height: PosTextSize.size1,
width: PosTextSize.size1),
);
bytes += generator.hr(ch: " ");
bytes += generator.text(
displayDate,
styles: const PosStyles(
align: PosAlign.center,
bold: false,
height: PosTextSize.size1,
width: PosTextSize.size1),
);
bytes += generator.hr(ch: " ");
bytes += generator.text("NOMOR ANTRIAN",
styles: const PosStyles(
align: PosAlign.center,
bold: true,
height: PosTextSize.size1,
width: PosTextSize.size1),
linesAfter: 1);
if (isConsult == 'N') {
bytes += generator.text("${layananName}",
styles: const PosStyles(
align: PosAlign.center,
bold: true,
height: PosTextSize.size1,
width: PosTextSize.size1),
linesAfter: 1);
}
if (isConsult == 'Y') {
bytes += generator.text("${layananName} ${DoctorName}",
styles: const PosStyles(
align: PosAlign.center,
bold: true,
height: PosTextSize.size1,
width: PosTextSize.size1),
linesAfter: 1);
}
bytes += generator.text(antrianNumb,
styles: const PosStyles(
align: PosAlign.center,
bold: true,
height: PosTextSize.size2,
width: PosTextSize.size2),
linesAfter: 1);
// final List<dynamic> barData = "{A$antrianNumb".split("");
// bytes += generator.barcode(Barcode.code128(barData),
// height: 60, textPos: BarcodeText.none);
// bytes += generator.hr(ch: " ");
bytes += generator.text(
"SIMPAN TIKET INI HINGGA",
styles: const PosStyles(
align: PosAlign.center,
bold: false,
height: PosTextSize.size1,
width: PosTextSize.size1),
);
bytes += generator.text(
"SELURUH LAYANAN ANDA SELESAI",
styles: const PosStyles(
align: PosAlign.center,
bold: false,
height: PosTextSize.size1,
width: PosTextSize.size1),
);
bytes += generator.hr(ch: " ");
if (name != "") {
bytes += generator.text(
name,
styles: const PosStyles(
align: PosAlign.center,
bold: false,
height: PosTextSize.size1,
width: PosTextSize.size1),
);
}
// bytes += generator.text(
// location,
// linesAfter: 1,
// styles: const PosStyles(
// align: PosAlign.center,
// bold: false,
// height: PosTextSize.size1,
// width: PosTextSize.size1),
// );
// bytes += generator.text(
// "LAYANAN LABORATORIUM MEDIS",
// styles: const PosStyles(
// align: PosAlign.center,
// bold: false,
// height: PosTextSize.size1,
// width: PosTextSize.size1),
// );
// bytes +=
// generator.qrcode("$StringqrCode$antrianNumb", size: QRSize.Size8);
// bytes += generator.hr(ch: " ");
bytes += generator.text(
footer,
styles: const PosStyles(
align: PosAlign.center,
bold: false,
height: PosTextSize.size1,
width: PosTextSize.size1),
);
bytes += generator.cut();
// final directory = await getApplicationDocumentsDirectory();
// final file =
// File('${directory.path}/$antrianNumb-$date-$month-$year.txt');
// await file.writeAsString(bytes.toString());
PrinterManager.instance.send(type: PrinterType.usb, bytes: bytes);
} catch (e) {
print(e);
}
}
Future printTest(
{String boothId = "Test Booth",
String header = "Test header",
String date = "Test Date",
String time = "Test time",
String antrianNumb = "Test01",
String name = "Test name",
String location = "Test location",
StringqrCode = "Test01",
String isConsult = "N",
String DoctorName = "-",
String layananName = "Test Layanan",
String footer = "Test footer"}) async {
try {
List<int> bytes = [];
final dt = DateFormat('yyyy-MM-dd-HH-mm-ss').format(DateTime.now());
final splited = dt.split('-');
final date = splited[2].toString();
final month = splited[1].toString();
final year = splited[0].toString();
final hour = splited[3].toString();
final minute = splited[4].toString();
final displayDate = "$date/$month/$year - $hour.$minute WIB";
final profile = await CapabilityProfile.load(name: "XP-N160I");
var generator = Generator(PaperSize.mm80, profile);
bytes += generator.text(
"Booth :$boothId",
styles: const PosStyles(
align: PosAlign.right,
bold: true,
height: PosTextSize.size1,
width: PosTextSize.size1),
);
bytes += generator.hr(ch: " ");
bytes += generator.text(
header,
styles: const PosStyles(
align: PosAlign.center,
bold: true,
height: PosTextSize.size1,
width: PosTextSize.size1),
);
bytes += generator.hr(ch: " ");
bytes += generator.text(
displayDate,
styles: const PosStyles(
align: PosAlign.center,
bold: false,
height: PosTextSize.size1,
width: PosTextSize.size1),
);
bytes += generator.hr(ch: " ");
bytes += generator.text("NOMOR ANTRIAN",
styles: const PosStyles(
align: PosAlign.center,
bold: true,
height: PosTextSize.size1,
width: PosTextSize.size1),
linesAfter: 1);
if (isConsult == 'N') {
bytes += generator.text("${layananName}",
styles: const PosStyles(
align: PosAlign.center,
bold: true,
height: PosTextSize.size1,
width: PosTextSize.size1),
linesAfter: 1);
}
if (isConsult == 'Y') {
bytes += generator.text("${layananName} ${DoctorName}",
styles: const PosStyles(
align: PosAlign.center,
bold: true,
height: PosTextSize.size1,
width: PosTextSize.size1),
linesAfter: 1);
}
bytes += generator.text(antrianNumb,
styles: const PosStyles(
align: PosAlign.center,
bold: true,
height: PosTextSize.size8,
width: PosTextSize.size8),
linesAfter: 1);
final List<dynamic> barData = "{A$antrianNumb".split("");
bytes += generator.barcode(Barcode.code128(barData),
height: 60, textPos: BarcodeText.none);
bytes += generator.hr(ch: " ");
bytes += generator.text(
"SIMPAN TIKET INI HINGGA",
styles: const PosStyles(
align: PosAlign.center,
bold: false,
height: PosTextSize.size1,
width: PosTextSize.size1),
);
bytes += generator.text(
"SELURUH LAYANAN ANDA SELESAI",
styles: const PosStyles(
align: PosAlign.center,
bold: false,
height: PosTextSize.size1,
width: PosTextSize.size1),
);
bytes += generator.hr(ch: " ");
if (name != "") {
bytes += generator.text(
name,
styles: const PosStyles(
align: PosAlign.center,
bold: false,
height: PosTextSize.size1,
width: PosTextSize.size1),
);
}
bytes += generator.text(
location,
linesAfter: 1,
styles: const PosStyles(
align: PosAlign.center,
bold: false,
height: PosTextSize.size1,
width: PosTextSize.size1),
);
// bytes += generator.text(
// "LAYANAN LABORATORIUM MEDIS",
// styles: const PosStyles(
// align: PosAlign.center,
// bold: false,
// height: PosTextSize.size1,
// width: PosTextSize.size1),
// );
// bytes +=
// generator.qrcode("$StringqrCode$antrianNumb", size: QRSize.Size8);
// bytes += generator.hr(ch: " ");
bytes += generator.text(
footer,
styles: const PosStyles(
align: PosAlign.center,
bold: false,
height: PosTextSize.size1,
width: PosTextSize.size1),
);
bytes += generator.cut();
// final directory = await getApplicationDocumentsDirectory();
// final file =
// File('${directory.path}/$antrianNumb-$date-$month-$year.txt');
// await file.writeAsString(bytes.toString());
await PrinterManager.instance.send(type: PrinterType.usb, bytes: bytes);
} catch (e) {
print(e);
}
}
}

98
lib/app/route.dart Normal file
View File

@@ -0,0 +1,98 @@
import 'package:flutter/material.dart';
import 'package:ticket_booth/screen/initial/initial_screen.dart';
import 'package:ticket_booth/screen/queue/queue_screen.dart';
import 'package:ticket_booth/screen/settings/setting_screen.dart';
import 'package:ticket_booth/screen/splash/splash_screen.dart';
const splashScreen = "/splashScreen";
const settingRoute = "/settingRoute";
const displayRoute = "/displayScreenRoute";
const queueRoute = "/queueRoute";
const tvRoute = "/tvRoute";
class AppRoute {
static Route<dynamic>? generateRoute(RouteSettings settings) {
// initial screen
if (settings.name == displayRoute) {
return MaterialPageRoute(builder: (context) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaleFactor: 1.0,
padding: const EdgeInsets.all(0),
),
child: const InitialScreen());
});
}
if (settings.name == settingRoute) {
return PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) =>
const SettingScreen(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
const begin = Offset(0.0, 1.0);
const end = Offset.zero;
const curve = Curves.ease;
var tween =
Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
return SlideTransition(
position: animation.drive(tween),
child: child,
);
},
);
}
//Queue screen
if (settings.name == queueRoute) {
return PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) =>
const QueueScreen(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
const begin = Offset(0.0, 1.0);
const end = Offset.zero;
const curve = Curves.ease;
var tween =
Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
return SlideTransition(
position: animation.drive(tween),
child: child,
);
},
);
}
// //Queue screen
// if (settings.name == tvRoute) {
// return PageRouteBuilder(
// pageBuilder: (context, animation, secondaryAnimation) =>
// const TvTemplate(),
// transitionsBuilder: (context, animation, secondaryAnimation, child) {
// const begin = Offset(0.0, 1.0);
// const end = Offset.zero;
// const curve = Curves.ease;
// var tween =
// Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
// return SlideTransition(
// position: animation.drive(tween),
// child: child,
// );
// },
// );
// }
//splash screen
if (settings.name == splashScreen) {
return MaterialPageRoute(builder: (context) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaleFactor: 1.0,
padding: const EdgeInsets.all(0),
),
child: const SplashScreen());
});
}
}
}

40
lib/main.dart Normal file
View File

@@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:window_manager/window_manager.dart';
import 'app/route.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Must add this line.
await windowManager.ensureInitialized();
WindowOptions windowOptions = const WindowOptions(
skipTaskbar: false,
titleBarStyle: TitleBarStyle.hidden,
);
windowManager.waitUntilReadyToShow(windowOptions, () async {
await windowManager.show();
await WindowManager.instance.setFullScreen(true);
await windowManager.focus();
});
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: splashScreen,
onGenerateRoute: AppRoute.generateRoute,
);
}
}

18
lib/model/booth.dart Normal file
View File

@@ -0,0 +1,18 @@
import 'package:equatable/equatable.dart';
class Booth extends Equatable {
int id;
String name;
String code;
Booth({required this.name, required this.id, required this.code});
Booth.fromJson(Map<String, dynamic> json)
: id = json['id'].runtimeType == int ? json['id'] : int.parse(json['id']),
name = json['name'],
code = json['code'];
Map<String, dynamic> toJson() => {'name': name, 'id': id, 'code': code};
@override
List<Object?> get props => [id];
}

View File

@@ -0,0 +1,60 @@
class BranchModel {
String? mBranchID;
String? mBranchCode;
String? mBranchCodeLab;
String? mBranchName;
String? mBranchAddress;
String? mBranchIsActive;
String? mBranchCreated;
String? mBranchCreatedUserID;
String? mBranchLastUpdated;
String? mBranchLastUpdatedUserID;
String? mBranchDeleted;
String? mBranchDeletedUserID;
BranchModel(
{this.mBranchID,
this.mBranchCode,
this.mBranchCodeLab,
this.mBranchName,
this.mBranchAddress,
this.mBranchIsActive,
this.mBranchCreated,
this.mBranchCreatedUserID,
this.mBranchLastUpdated,
this.mBranchLastUpdatedUserID,
this.mBranchDeleted,
this.mBranchDeletedUserID});
BranchModel.fromJson(Map<String, dynamic> json) {
mBranchID = json['M_BranchID'];
mBranchCode = json['M_BranchCode'];
mBranchCodeLab = json['M_BranchCodeLab'];
mBranchName = json['M_BranchName'];
mBranchAddress = json['M_BranchAddress'];
mBranchIsActive = json['M_BranchIsActive'];
mBranchCreated = json['M_BranchCreated'];
mBranchCreatedUserID = json['M_BranchCreatedUserID'];
mBranchLastUpdated = json['M_BranchLastUpdated'];
mBranchLastUpdatedUserID = json['M_BranchLastUpdatedUserID'];
mBranchDeleted = json['M_BranchDeleted'];
mBranchDeletedUserID = json['M_BranchDeletedUserID'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['M_BranchID'] = this.mBranchID;
data['M_BranchCode'] = this.mBranchCode;
data['M_BranchCodeLab'] = this.mBranchCodeLab;
data['M_BranchName'] = this.mBranchName;
data['M_BranchAddress'] = this.mBranchAddress;
data['M_BranchIsActive'] = this.mBranchIsActive;
data['M_BranchCreated'] = this.mBranchCreated;
data['M_BranchCreatedUserID'] = this.mBranchCreatedUserID;
data['M_BranchLastUpdated'] = this.mBranchLastUpdated;
data['M_BranchLastUpdatedUserID'] = this.mBranchLastUpdatedUserID;
data['M_BranchDeleted'] = this.mBranchDeleted;
data['M_BranchDeletedUserID'] = this.mBranchDeletedUserID;
return data;
}
}

View File

@@ -0,0 +1,24 @@
class CapabilityProfileModel {
String? key;
String? vendor;
String? model;
String? description;
CapabilityProfileModel({this.key, this.vendor, this.model, this.description});
CapabilityProfileModel.fromJson(Map<String, dynamic> json) {
key = json['key'];
vendor = json['vendor'];
model = json['model'];
description = json['description'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['key'] = this.key;
data['vendor'] = this.vendor;
data['model'] = this.model;
data['description'] = this.description;
return data;
}
}

40
lib/model/layanan.dart Normal file
View File

@@ -0,0 +1,40 @@
import 'package:equatable/equatable.dart';
class Layanan extends Equatable {
int id;
String name;
String code;
bool value;
int priority;
String isConsultDoctor;
String doctorName;
String? img;
Layanan(
{required this.name,
required this.id,
required this.priority,
required this.isConsultDoctor,
this.doctorName = '',
this.value = false,
this.img,
required this.code});
Layanan.fromJson(Map<String, dynamic> json)
: name = (json['serviceName'] as String).replaceAll("</br>", "\n"),
code = json['serviceCode'].toString(),
priority = int.parse(json['servicePriority']),
value = false,
isConsultDoctor = json['serviceIsConsultDoctor'].toString(),
doctorName = json['serviceDoctorName'].toString(),
id = int.parse(json['serviceID']);
Map<String, dynamic> toJson() => {
'serviceName': name,
'serviceID': id,
'serviceID': code,
'servicePriority': priority,
'value': value
};
@override
List<Object?> get props => [id];
}

View File

@@ -0,0 +1,46 @@
import 'package:equatable/equatable.dart';
class PrinterDev extends Equatable {
int id;
String deviceName;
String? address;
String? port;
String? vendorId;
String? productId;
bool? isBle;
bool? state;
PrinterDev(
{required this.deviceName,
required this.id,
this.address,
this.port,
this.state = true,
this.vendorId,
this.productId,
this.isBle = false});
PrinterDev.fromJson(Map<String, dynamic> json)
: deviceName = json['deviceName'],
id = json['id'],
address = json['address'],
port = json['port'],
state = json['state'],
vendorId = json['vendorId'],
productId = json['productId'],
isBle = json['this.isBle'];
Map<String, dynamic> toJson() => {
'deviceName': deviceName,
'address': address,
'port': port,
'state': state,
'vendorId': vendorId,
'productId': productId,
'isBle': isBle,
'id': id
};
@override
List<Object?> get props => [deviceName];
}

View File

@@ -0,0 +1,6 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:ticket_booth/model/layanan.dart';
final allServiceProvider = StateProvider<List<Layanan>>(
(ref) => List.empty(),
);

View File

@@ -0,0 +1,4 @@
import 'package:dio/dio.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final dioProvider = Provider<Dio>((ref) => Dio());

View File

@@ -0,0 +1,153 @@
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
abstract class BaseRepository {
final Dio dio;
BaseRepository({required this.dio});
Future<Map<String, dynamic>> post({
required Map<String, dynamic> param,
required String service,
String? token,
}) async {
try {
final response = await dio.post(
// Constant.baseUrl + service,
service,
data: jsonEncode(param),
options: Options(
headers: token != null
? {
HttpHeaders.contentTypeHeader: "application/json",
HttpHeaders.authorizationHeader: "Bearer $token",
}
: {
HttpHeaders.contentTypeHeader: "application/json",
},
contentType: "application/json",
),
);
if (response.statusCode != 200) {
throw BaseRepositoryException(
message: "Invalid Http Response ${response.statusCode}",
);
}
Map<String, dynamic> jsonData = jsonDecode(response.data);
if (jsonData["status"] != "OK") {
throw BaseRepositoryException(
message: jsonData["message"],
);
} else {
return jsonData;
}
} on DioError catch (e) {
throw BaseRepositoryException(message: e.message ?? '');
} on SocketException catch (e) {
throw BaseRepositoryException(message: e.message);
} on BaseRepositoryException catch (e) {
throw BaseRepositoryException(message: e.message);
}
}
Future<Map<String, dynamic>> get({
required String service,
}) async {
try {
final response = await dio.get(
// Constant.baseUrl + service,
service,
);
if (response.statusCode != 200) {
throw BaseRepositoryException(
message: "Invalid Http Response ${response.statusCode}",
);
}
// print(jsonDecode(response.data));
Map<String, dynamic> jsonData = response.data;
if (jsonData["status"] != "OK") {
throw BaseRepositoryException(
message: jsonData["message"],
);
} else {
return jsonData;
}
} on DioError catch (e) {
throw BaseRepositoryException(message: e.message ?? '');
} on SocketException catch (e) {
throw BaseRepositoryException(message: e.message);
} on BaseRepositoryException catch (e) {
throw BaseRepositoryException(message: e.message);
}
}
Future<Map<String, dynamic>> getBooth({
required String service,
}) async {
try {
final response = await dio.get(
// Constant.baseUrl + service,
service,
);
if (response.statusCode != 200) {
throw BaseRepositoryException(
message: "Invalid Http Response ${response.statusCode}",
);
}
// print(jsonDecode(response.data));
Map<String, dynamic> jsonData = jsonDecode(response.data);
if (jsonData["status"] != "OK") {
throw BaseRepositoryException(
message: jsonData["message"],
);
} else {
return jsonData;
}
} on DioError catch (e) {
throw BaseRepositoryException(message: e.message ?? '');
} on SocketException catch (e) {
throw BaseRepositoryException(message: e.message);
} on BaseRepositoryException catch (e) {
throw BaseRepositoryException(message: e.message);
}
}
Future<Map<String, dynamic>> getService({
required String service,
}) async {
try {
final response = await dio.get(
// Constant.baseUrl + service,
service,
);
if (response.statusCode != 200) {
throw BaseRepositoryException(
message: "Invalid Http Response ${response.statusCode}",
);
}
// print(jsonDecode(response.data));
Map<String, dynamic> jsonData = jsonDecode(response.data);
if (jsonData["status"] != "OK") {
throw BaseRepositoryException(
message: jsonData["message"],
);
} else {
return jsonData;
}
} on DioError catch (e) {
throw BaseRepositoryException(message: e.message ?? "");
} on SocketException catch (e) {
throw BaseRepositoryException(message: e.message);
} on BaseRepositoryException catch (e) {
throw BaseRepositoryException(message: e.message);
}
}
}
class BaseRepositoryException implements Exception {
final String message;
BaseRepositoryException({
required this.message,
});
}

View File

@@ -0,0 +1,36 @@
import 'package:ticket_booth/app/constant.dart';
import 'package:ticket_booth/model/booth.dart';
import 'package:ticket_booth/model/branch_model.dart';
import 'package:ticket_booth/repository/base_repository.dart';
class BoothRepository extends BaseRepository {
BoothRepository({required super.dio});
Future<List<Booth>> getData({required String hostIP}) async {
final url =
"$hostIP/one-api/mockup/masterdata/one-md-ticket-dispenser/ticketbooth/index";
print(url);
// final url = "$hostIP/one-api/training/ticketbooth/index";
final resp = await getBooth(service: url);
final List<Booth> listBooth = List.empty(growable: true);
resp['data']['records'].forEach((e) {
final model = Booth.fromJson(e);
listBooth.add(model);
});
return listBooth;
}
Future<List<BranchModel>> getBranch({required String hostIP}) async {
// https://devcpone.aplikasi.web.id/one-api/mockup/fo/antrian/AntrianByStationAndLocation/getbranch
final url =
"$hostIP/one-api/mockup/fo/antrian/AntrianByStationAndLocation/getbranch";
print(url);
// final url = "$hostIP/one-api/training/ticketbooth/index";
final resp = await getBooth(service: url);
final List<BranchModel> listBooth = List.empty(growable: true);
resp['data']['records'].forEach((e) {
final model = BranchModel.fromJson(e);
listBooth.add(model);
});
return listBooth;
}
}

View File

@@ -0,0 +1,24 @@
import '../app/constant.dart';
import 'base_repository.dart';
class QueueRepository extends BaseRepository {
QueueRepository({required super.dio});
Future<Map<String, dynamic>> getData(
{required service_id,
required booth_id,
required hostIp,
required branchID}) async {
// var url = "$hostIp/ticket/$service_id/$booth_id";
var url = "$hostIp/one-api/antrian/ticket/getAntrian";
// final resp = await get(
// service: url,
// );
final resp = await post(param: {
"service_id": service_id,
"booth_id": booth_id,
"branch_id": branchID
}, service: url);
// print(resp);
return resp;
}
}

View File

@@ -0,0 +1,24 @@
import 'dart:developer';
import 'package:ticket_booth/model/layanan.dart';
import '../app/constant.dart';
import 'base_repository.dart';
class ServiceRepository extends BaseRepository {
ServiceRepository({required super.dio});
Future<List<Layanan>> getData({required String hostIp}) async {
final url =
"$hostIp/one-api/mockup/masterdata/one-md-fo-service/service/listService";
print(url);
final resp = await getService(service: url);
final List<Layanan> listLayanan = List.empty(growable: true);
resp['data']['records'].forEach((e) {
final model = Layanan.fromJson(e);
listLayanan.add(model);
});
return listLayanan;
}
}

View File

@@ -0,0 +1,242 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:ticket_booth/app/constant.dart';
import 'package:ticket_booth/app/print_ticket.dart';
import 'package:ticket_booth/app/route.dart';
import 'package:ticket_booth/provider/all_service_provider.dart';
import 'package:ticket_booth/screen/initial/service_list_provider.dart';
import '../widgets/error_dialog.dart';
class InitialScreen extends HookConsumerWidget {
const InitialScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final isLoading = useState(false);
final errorMsg = useState('');
final hostIp = useState('N');
final testPrintDisabled = useState(true);
Future getData() async {
try {
final prefs = await SharedPreferences.getInstance();
var raw_data = prefs.getString('tb-westerindo') ?? 'a';
if (raw_data != 'a') {
var data = json.decode(raw_data);
hostIp.value = data['hostIp'];
testPrintDisabled.value = false;
}
} catch (e) {
print(e);
ErrorDialog(context, e.toString(), 'ERROR');
}
}
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await getData();
if (hostIp.value != 'N') {
ref.read(ServiceListProvider.notifier).list(hostIp: hostIp.value);
}
});
// Navigator.of(context).pushNamed(queueRoute);
// final timer = Timer(const Duration(seconds: 10), () {
// });
return () {
// timer.cancel();
};
}, []);
ref.listen(ServiceListProvider, (previous, next) {
if (next is ServiceListStateLoading) {
isLoading.value = true;
} else if (next is ServiceListStateError) {
errorMsg.value = next.message;
ErrorDialog(context, errorMsg.value, 'ERROR');
} else if (next is ServiceListStateDone) {
ref.read(allServiceProvider.notifier).state = next.model;
// print(ref.read(allServiceProvider));
}
});
return Material(
child: Container(
height: Constant.getActualY(context: context, y: 982),
width: Constant.getActualX(context: context, x: 1512),
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: AssetImage("assets/images/new-bg-icon.png"))),
child: Column(
children: [
SizedBox(
height: Constant.getActualY(context: context, y: 600),
width: Constant.getActualX(context: context, x: 1512),
),
Text(
Constant.versi,
style: Constant.subTitle(context: context)
.copyWith(fontStyle: FontStyle.italic, color: Colors.grey),
),
SizedBox(
height: Constant.getActualY(context: context, y: 302),
width: Constant.getActualX(context: context, x: 1512),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
maximumSize: Size(
Constant.getActualX(context: context, x: 350),
Constant.getActualY(context: context, y: 100)),
elevation: 5,
side: const BorderSide(
width: 0.5,
color: Color.fromARGB(162, 204, 92, 146)),
backgroundColor: Colors.white,
foregroundColor: Colors.grey,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50))),
onPressed: () {
Navigator.of(context).popAndPushNamed(settingRoute);
},
child: Padding(
padding: EdgeInsets.symmetric(
horizontal:
Constant.getActualX(context: context, x: 51),
vertical:
Constant.getActualY(context: context, y: 21)),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'assets/images/setting.png',
height:
Constant.getActualY(context: context, y: 57),
width: Constant.getActualY(context: context, y: 57),
),
SizedBox(
width: Constant.getActualX(context: context, x: 15),
),
Text(
'Setting',
style: Constant.title(context: context)
.copyWith(color: Colors.black),
),
],
),
),
),
SizedBox(
width: Constant.getActualX(context: context, x: 166.5),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 5,
maximumSize: Size(
Constant.getActualX(context: context, x: 350),
Constant.getActualY(context: context, y: 100)),
foregroundColor: Colors.grey,
side: const BorderSide(
width: 0.5,
color: Color.fromARGB(162, 204, 92, 146)),
backgroundColor: testPrintDisabled.value
? Colors.grey
: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50))),
onPressed: () {
if (testPrintDisabled.value) {
} else {
PrintTicket().printTest();
}
},
child: Padding(
padding: EdgeInsets.symmetric(
horizontal:
Constant.getActualX(context: context, x: 51),
vertical:
Constant.getActualY(context: context, y: 21)),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'assets/images/next.png',
height:
Constant.getActualY(context: context, y: 57),
width: Constant.getActualY(context: context, y: 57),
),
SizedBox(
width: Constant.getActualX(context: context, x: 15),
),
Text(
'Test Print',
style: Constant.title(context: context).copyWith(
color: testPrintDisabled.value
? Colors.white
: Colors.black),
),
],
),
),
),
SizedBox(
width: Constant.getActualX(context: context, x: 166.5),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 5,
maximumSize: Size(
Constant.getActualX(context: context, x: 350),
Constant.getActualY(context: context, y: 100)),
foregroundColor: Colors.grey,
side: const BorderSide(
width: 0.5,
color: Color.fromARGB(162, 204, 92, 146)),
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50))),
onPressed: () {
Navigator.of(context).pushNamed(queueRoute);
},
child: Padding(
padding: EdgeInsets.symmetric(
horizontal:
Constant.getActualX(context: context, x: 51),
vertical:
Constant.getActualY(context: context, y: 21)),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'assets/images/next.png',
height:
Constant.getActualY(context: context, y: 57),
width: Constant.getActualY(context: context, y: 57),
),
SizedBox(
width: Constant.getActualX(context: context, x: 15),
),
Text(
'Next',
style: Constant.title(context: context)
.copyWith(color: Colors.black),
),
],
),
),
),
],
),
),
)
],
),
));
}
}

View File

@@ -0,0 +1,63 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:ticket_booth/model/layanan.dart';
import 'package:ticket_booth/provider/dio_provider.dart';
import 'package:ticket_booth/repository/base_repository.dart';
import 'package:ticket_booth/repository/service_repository.dart';
abstract class ServiceListState extends Equatable {
final DateTime date;
const ServiceListState(this.date);
@override
List<Object?> get props => [date];
}
class ServiceListStateInit extends ServiceListState {
ServiceListStateInit() : super(DateTime.now());
}
class ServiceListStateLoading extends ServiceListState {
ServiceListStateLoading() : super(DateTime.now());
}
class ServiceListStateError extends ServiceListState {
final String message;
ServiceListStateError({
required this.message,
}) : super(DateTime.now());
}
class ServiceListStateDone extends ServiceListState {
final List<Layanan> model;
ServiceListStateDone({
required this.model,
}) : super(DateTime.now());
}
//notifier
class ServiceListNotifier extends StateNotifier<ServiceListState> {
final Ref ref;
ServiceListNotifier({
required this.ref,
}) : super(ServiceListStateInit());
void list({required hostIp}) async {
try {
state = ServiceListStateLoading();
final dio = ref.read(dioProvider);
final resp = await ServiceRepository(dio: dio).getData(hostIp: hostIp);
state = ServiceListStateDone(model: resp);
} catch (e) {
if (e is BaseRepositoryException) {
state = ServiceListStateError(message: e.message);
} else {
state = ServiceListStateError(message: e.toString());
}
}
}
}
//provider
final ServiceListProvider =
StateNotifierProvider<ServiceListNotifier, ServiceListState>(
(ref) => ServiceListNotifier(ref: ref));

View File

@@ -0,0 +1,71 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:ticket_booth/provider/dio_provider.dart';
import 'package:ticket_booth/repository/base_repository.dart';
import 'package:ticket_booth/repository/queue_repository.dart';
abstract class QueueState extends Equatable {
final DateTime date;
const QueueState(this.date);
@override
List<Object?> get props => [date];
}
class QueueStateInit extends QueueState {
QueueStateInit() : super(DateTime.now());
}
class QueueStateLoading extends QueueState {
QueueStateLoading() : super(DateTime.now());
}
class QueueStateError extends QueueState {
final String message;
QueueStateError({
required this.message,
}) : super(DateTime.now());
}
class QueueStateDone extends QueueState {
final Map<String, dynamic> response;
QueueStateDone({
required this.response,
}) : super(DateTime.now());
}
//notifier
class QueueNotifier extends StateNotifier<QueueState> {
final Ref ref;
QueueNotifier({
required this.ref,
}) : super(QueueStateInit());
void get(
{required service_id,
required booth_id,
required hostIp,
required branchID}) async {
try {
state = QueueStateLoading();
final dio = ref.read(dioProvider);
final resp = await QueueRepository(dio: dio).getData(
service_id: service_id,
booth_id: booth_id,
hostIp: hostIp,
branchID: branchID);
state = QueueStateDone(response: resp);
} catch (e) {
if (e is BaseRepositoryException) {
state = QueueStateError(message: e.message);
} else {
state = QueueStateError(message: e.toString());
}
}
}
}
//provider
final QueueProvider = StateNotifierProvider<QueueNotifier, QueueState>(
(ref) => QueueNotifier(ref: ref));

View File

@@ -0,0 +1,543 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_pos_printer_platform_image_3/flutter_pos_printer_platform_image_3.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:ticket_booth/app/print_ticket.dart';
import 'package:ticket_booth/app/route.dart';
import 'package:ticket_booth/model/booth.dart';
import 'package:ticket_booth/model/branch_model.dart';
import 'package:ticket_booth/model/printer_device.dart';
import 'package:ticket_booth/provider/all_service_provider.dart';
import 'package:intl/intl.dart';
import 'package:ticket_booth/screen/queue/queue_provider.dart';
import 'package:ticket_booth/screen/widgets/dialog_print.dart';
import 'package:ticket_booth/screen/widgets/pasien_online_dialog.dart';
import '../../app/constant.dart';
import '../../model/cabapility_model.dart';
import '../../model/layanan.dart';
import '../widgets/error_dialog.dart';
class QueueScreen extends HookConsumerWidget {
const QueueScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final isLoading = useState(true);
final lyn = useState<List<Layanan>>(List.empty());
final activeLayanan = useState<List<dynamic>>(List.empty());
final selectedPrinter =
useState<PrinterDev>(PrinterDev(deviceName: "a", id: 0));
final selectedBooth =
useState<Booth>(Booth(name: 'B001', id: 0, code: 'a'));
final hostIp = useState('N');
final hostqR = useState('N');
final headerDisplay = useState('N');
final headerTicket = useState('N');
final footerTicket = useState('N');
final footerDisplay = useState('N');
final isLoadingPrint = useState(false);
final errorMsg = useState('');
final splitedHost = useState('');
final tapAnimation = useState(0);
final selectedCapabilityProfile = useState(CapabilityProfileModel());
final selectedBranch = useState<BranchModel?>(null);
final selectedLayanan = useState<Layanan>(Layanan(
name: 'x', id: 0, priority: 0, isConsultDoctor: 'n', code: '0'));
final getNumberLoading = useState(false);
final btnSize = useState<Map<String, dynamic>>({'name': 'n'});
Future getData() async {
try {
final prefs = await SharedPreferences.getInstance();
var raw_data = prefs.getString('tb-westerindo') ?? 'a';
if (raw_data != 'a') {
List<Layanan> allLayanan = ref.read(allServiceProvider);
var data = json.decode(raw_data);
hostIp.value = data['hostIp'];
hostqR.value = data['hostQrIp'];
headerDisplay.value = data['headerDisplay'];
headerTicket.value = data['headerTicket'];
footerTicket.value = data['footerTicket'];
footerDisplay.value = data['footerDisplay'];
selectedPrinter.value = PrinterDev.fromJson(data['selectedPrinter']);
selectedBooth.value = Booth.fromJson(data['selectedBooth']);
selectedBranch.value = BranchModel.fromJson(data['selectedBranch']);
selectedCapabilityProfile.value =
CapabilityProfileModel.fromJson(data['selectedCapability']);
activeLayanan.value = data['activeLayanan'];
btnSize.value = data['buttonSize'];
print(btnSize.value);
var sp = hostIp.value.split(':');
splitedHost.value = sp[0] + ":" + sp[1];
List<Layanan> actvLayanan = [];
activeLayanan.value.forEach((e) {
allLayanan.forEach((f) {
if (e == f.id) {
actvLayanan.add(f);
}
});
});
actvLayanan.sort(((a, b) => a.priority.compareTo(b.priority)));
lyn.value = actvLayanan;
} else {
Navigator.of(context).popAndPushNamed(settingRoute);
}
isLoading.value = false;
} catch (e) {
print(e);
ErrorDialog(context, e.toString(), 'ERROR');
isLoading.value = false;
}
}
getDataApi(e) {
ref.read(QueueProvider.notifier).get(
branchID: selectedBranch.value?.mBranchID,
booth_id: selectedBooth.value.id,
service_id: e.id,
hostIp: hostIp.value);
}
printTicketDialog(String number) {
try {
// DialogPrint(context, number);
PrintTicket().printTicket(
boothId: selectedBooth.value.name.toString(),
header: headerTicket.value,
date: 'a',
time: 'a',
antrianNumb: number,
name: '',
StringqrCode: hostqR.value,
location: '',
footer: footerTicket.value,
DoctorName: selectedLayanan.value.doctorName,
isConsult: selectedLayanan.value.isConsultDoctor,
capabilityName: selectedCapabilityProfile.value.key ?? "default",
layananName: selectedLayanan.value.name);
} catch (e) {
errorMsg.value = e.toString();
ErrorDialog(context, errorMsg.value, 'ERROR');
}
}
ref.listen(QueueProvider, (previous, next) {
if (next is QueueStateLoading) {
isLoadingPrint.value = true;
} else if (next is QueueStateError) {
errorMsg.value = next.message;
ErrorDialog(context, errorMsg.value, 'ERROR');
getNumberLoading.value = false;
} else if (next is QueueStateDone) {
String number = next.response['data']['number'].toString();
String location = next.response['data']['location'].toString();
// print(location);
DialogPrint(
context, number, printTicketDialog, selectedLayanan.value.name);
try {
// PrintTicket().printTicket(
// boothId: selectedBooth.value.name.toString(),
// header: headerTicket.value,
// date: 'a',
// time: 'a',
// antrianNumb: number,
// name: '',
// StringqrCode: hostqR.value,
// location: location,
// footer: footerTicket.value,
// DoctorName: selectedLayanan.value.doctorName,
// isConsult: selectedLayanan.value.isConsultDoctor,
// capabilityName: selectedCapabilityProfile.value.key ?? "default",
// layananName: selectedLayanan.value.name);
} catch (e) {
errorMsg.value = e.toString();
ErrorDialog(context, errorMsg.value, 'ERROR');
}
Timer(Duration(seconds: 2), () {
getNumberLoading.value = false;
});
}
});
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
getData();
});
return () {};
}, []);
return Material(
child: Container(
height: Constant.getActualY(context: context, y: 982),
width: Constant.getActualX(context: context, x: 1512),
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: AssetImage("assets/images/new-bg.png"))),
child: isLoading.value
? const SpinKitCubeGrid(
color: Colors.blue,
)
: Column(
children: [
SizedBox(
height: Constant.getActualY(context: context, y: 150),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () {
// Navigator.of(context).popAndPushNamed(displayRoute);
},
child: Container(
width:
Constant.getActualX(context: context, x: 150),
height:
Constant.getActualY(context: context, y: 150),
margin: EdgeInsets.only(
left: Constant.getActualX(
context: context, x: 30)),
padding: const EdgeInsets.all(20),
child: Image.network(
'${splitedHost.value.toString()}/one-media/one-queue/logo-westerindo.png',
loadingBuilder:
(context, child, loadingProgress) {
if (loadingProgress == null) return child;
return const CircularProgressIndicator();
},
errorBuilder: (context, error, stackTrace) {
return Image.asset(
'assets/images/logo-westerindo.png',
);
},
)),
),
Text(
headerDisplay.value,
style: Constant.title(context: context),
),
Container(
alignment: Alignment.center,
margin: EdgeInsets.only(
right:
Constant.getActualX(context: context, x: 30)),
padding: const EdgeInsets.all(20),
width: Constant.getActualX(context: context, x: 150),
height: Constant.getActualY(context: context, y: 150),
child: Text(selectedBranch.value?.mBranchName ?? "",
textAlign: TextAlign.center,
style: Constant.label(context: context).copyWith(
fontWeight: FontWeight.bold,
)),
),
],
),
),
SizedBox(
height: Constant.getActualY(context: context, y: 682),
child: Row(
children: [
Container(
width: Constant.getActualX(
context: context,
x: double.parse(btnSize.value['rightLeftWidth'])),
// color: Colors.green,
),
Container(
// color: Colors.red,
width: Constant.getActualX(
context: context,
x: double.parse(btnSize.value['centerWidth'])),
child: GridView.count(
crossAxisCount: int.parse(btnSize.value['column']),
childAspectRatio:
double.parse(btnSize.value['childAspectRatio']),
mainAxisSpacing: Constant.getActualY(
context: context,
y: double.parse(
btnSize.value['mainAxisSpacing'])),
crossAxisSpacing: Constant.getActualY(
context: context,
y: double.parse(
btnSize.value['crossAxisSpacing'])),
children: lyn.value
.map((e) => ButtonLayanan(
getNumberLoading,
tapAnimation,
e,
selectedLayanan,
context,
getDataApi,
btnSize,
splitedHost))
.toList()),
),
Container(
width: Constant.getActualX(
context: context,
x: double.parse(btnSize.value['rightLeftWidth'])),
// color: Colors.green,
)
],
),
),
SizedBox(
height: Constant.getActualY(context: context, y: 150),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: Constant.getActualX(context: context, x: 200),
margin: EdgeInsets.only(
left:
Constant.getActualX(context: context, x: 30)),
padding: EdgeInsets.all(20),
child: Center(
child: Text(
selectedBooth.value.name,
style: Constant.subTitle(context: context),
),
)),
Text(
footerDisplay.value,
style: Constant.title(context: context).copyWith(
fontStyle: FontStyle.italic,
fontWeight: FontWeight.normal),
),
const DisplayClock(),
],
),
)
],
),
));
}
Container ButtonLayanan(
ValueNotifier<bool> getNumberLoading,
ValueNotifier<int> tapAnimation,
Layanan e,
ValueNotifier<Layanan> selectedLayanan,
BuildContext context,
Null getDataApi(dynamic e),
ValueNotifier<Map<String, dynamic>> btnSize,
ValueNotifier<String> splitedHost) {
return Container(
// color: Colors.amber,
child: Center(
child: InkWell(
onTap: getNumberLoading.value
? null
: () {
// DialogPrint(context, 'A0001');
// PasienOnlineDialog(context);
// return;
tapAnimation.value = e.id;
selectedLayanan.value = e;
getNumberLoading.value = true;
if (e.name == "Pasien Online" && e.id == 5) {
} else {
getDataApi(e);
}
Timer(Duration(milliseconds: 250), () {
tapAnimation.value = 0;
});
Timer(Duration(seconds: 2), () {
getNumberLoading.value = false;
print("tidak loading");
});
},
child: Ink(
// color: Colors.green,
height: Constant.getActualY(
context: context, y: double.parse(btnSize.value['btnHeight'])),
width: Constant.getActualX(
context: context, x: double.parse(btnSize.value['btnWidth'])),
child: Stack(
alignment: AlignmentDirectional.center,
children: [
Image.network(
'${splitedHost.value.toString()}/one-media/one-queue/${e.code}.png',
fit: BoxFit.cover,
color:
e.id == tapAnimation.value ? Colors.grey : Colors.white,
// color: Colors.white
// .withOpacity(e.id ==
// tapAnimation.value
// ? 0.7
// : 1),
colorBlendMode: BlendMode.modulate,
height: Constant.getActualY(
context: context,
y: e.id == tapAnimation.value ? 310 : 400),
width: Constant.getActualX(
context: context,
x: e.id == tapAnimation.value ? 280 : 300),
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return const CircularProgressIndicator();
},
errorBuilder: (context, error, stackTrace) {
return Image.asset(
'assets/images/cpone-default.png',
fit: BoxFit.cover,
color: e.id == tapAnimation.value
? Colors.grey
: Colors.white,
colorBlendMode: BlendMode.modulate,
height: Constant.getActualY(
context: context,
y: e.id == tapAnimation.value ? 310 : 400),
width: Constant.getActualX(
context: context,
x: e.id == tapAnimation.value ? 280 : 300),
);
},
),
Positioned(
top: 1,
child: Container(
margin: EdgeInsets.only(
top: Constant.getActualY(context: context, y: 10)),
padding: EdgeInsets.symmetric(
horizontal:
Constant.getActualX(context: context, x: 10)),
// color: Colors.red,
height: Constant.getActualY(context: context, y: 110),
width: Constant.getActualX(context: context, x: 280),
child: Center(
child: e.isConsultDoctor == 'N'
? Text(
e.name,
maxLines: 2,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: Constant.layananTitle(context: context)
.copyWith(color: Colors.white),
)
: Column(
children: [
Text(
e.name,
maxLines: 2,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: Constant.layananTitle(context: context)
.copyWith(color: Colors.white),
),
Text(
e.doctorName,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: Constant.layananTitle(context: context)
.copyWith(color: Colors.white),
)
],
),
),
),
)
],
),
),
),
),
);
}
}
class DisplayClock extends HookConsumerWidget {
const DisplayClock({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final strDate = useState("");
final strMonth = useState("");
final strYear = useState("");
final strHour = useState("");
final strMinute = useState("");
final strSeconds = useState("");
useEffect(() {
final tmr = Timer.periodic(const Duration(seconds: 1), (timer) {
final dt = DateFormat('yyyy-MMM-dd-HH-mm-ss').format(DateTime.now());
final splited = dt.split('-');
final date = splited[2].toString();
final month = splited[1].toString();
final year = splited[0].toString();
final hour = splited[3].toString();
final minute = splited[4].toString();
final second = splited[5].toString();
if (date != strDate.value) strDate.value = date;
if (month != strMonth.value) strMonth.value = month;
if (year != strYear.value) strYear.value = year;
if (hour != strHour.value) strHour.value = hour;
if (minute != strMinute.value) strMinute.value = minute;
if (second != strSeconds.value) strSeconds.value = second;
});
return () {
tmr.cancel();
};
}, []);
return Container(
width: Constant.getActualX(context: context, x: 200),
margin: EdgeInsets.only(
right: Constant.getActualX(context: context, x: 30)),
padding: EdgeInsets.all(0),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
strDate.value,
style: Constant.title(context: context).copyWith(
color: Colors.blue[900],
fontSize: Constant.getActualX(context: context, x: 64)),
),
SizedBox(
width: Constant.getActualX(context: context, x: 10),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
strMonth.value,
style: Constant.subTitle(context: context).copyWith(
color: Colors.blue[900],
fontSize:
Constant.getActualX(context: context, x: 25)),
),
Text(
strYear.value,
style: Constant.subTitle(context: context).copyWith(
color: Colors.blue[900],
fontSize:
Constant.getActualX(context: context, x: 25)),
)
],
)
],
),
Text(
'${strHour.value} . ${strMinute.value} . ${strSeconds.value}',
style: Constant.subTitle(context: context).copyWith(
color: Colors.blue[900],
fontSize: Constant.getActualX(context: context, x: 25)),
)
],
));
}
}

View File

@@ -0,0 +1,63 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:ticket_booth/model/booth.dart';
import 'package:ticket_booth/provider/dio_provider.dart';
import 'package:ticket_booth/repository/base_repository.dart';
import 'package:ticket_booth/repository/booth_repository.dart';
abstract class BoothListState extends Equatable {
final DateTime date;
const BoothListState(this.date);
@override
List<Object?> get props => [date];
}
class BoothListStateInit extends BoothListState {
BoothListStateInit() : super(DateTime.now());
}
class BoothListStateLoading extends BoothListState {
BoothListStateLoading() : super(DateTime.now());
}
class BoothListStateError extends BoothListState {
final String message;
BoothListStateError({
required this.message,
}) : super(DateTime.now());
}
class BoothListStateDone extends BoothListState {
final List<Booth> model;
BoothListStateDone({
required this.model,
}) : super(DateTime.now());
}
//notifier
class BoothListNotifier extends StateNotifier<BoothListState> {
final Ref ref;
BoothListNotifier({
required this.ref,
}) : super(BoothListStateInit());
void list({required hostIp}) async {
try {
state = BoothListStateLoading();
final dio = ref.read(dioProvider);
final resp = await BoothRepository(dio: dio).getData(hostIP: hostIp);
state = BoothListStateDone(model: resp);
} catch (e) {
if (e is BaseRepositoryException) {
state = BoothListStateError(message: e.message);
} else {
state = BoothListStateError(message: e.toString());
}
}
}
}
//provider
final BoothListProvider =
StateNotifierProvider<BoothListNotifier, BoothListState>(
(ref) => BoothListNotifier(ref: ref));

View File

@@ -0,0 +1,63 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:ticket_booth/model/branch_model.dart';
import 'package:ticket_booth/provider/dio_provider.dart';
import 'package:ticket_booth/repository/base_repository.dart';
import 'package:ticket_booth/repository/booth_repository.dart';
abstract class BranchListState extends Equatable {
final DateTime date;
const BranchListState(this.date);
@override
List<Object?> get props => [date];
}
class BranchListStateInit extends BranchListState {
BranchListStateInit() : super(DateTime.now());
}
class BranchListStateLoading extends BranchListState {
BranchListStateLoading() : super(DateTime.now());
}
class BranchListStateError extends BranchListState {
final String message;
BranchListStateError({
required this.message,
}) : super(DateTime.now());
}
class BranchListStateDone extends BranchListState {
final List<BranchModel> model;
BranchListStateDone({
required this.model,
}) : super(DateTime.now());
}
//notifier
class BranchListNotifier extends StateNotifier<BranchListState> {
final Ref ref;
BranchListNotifier({
required this.ref,
}) : super(BranchListStateInit());
void list({required hostIp}) async {
try {
state = BranchListStateLoading();
final dio = ref.read(dioProvider);
final resp = await BoothRepository(dio: dio).getBranch(hostIP: hostIp);
state = BranchListStateDone(model: resp);
} catch (e) {
if (e is BaseRepositoryException) {
state = BranchListStateError(message: e.message);
} else {
state = BranchListStateError(message: e.toString());
}
}
}
}
//provider
final BranchListProvider =
StateNotifierProvider<BranchListNotifier, BranchListState>(
(ref) => BranchListNotifier(ref: ref));

View File

@@ -0,0 +1,496 @@
import 'dart:convert';
import 'package:clippy_flutter/ticket.dart';
import 'package:esc_pos_utils/esc_pos_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_pos_printer_platform_image_3/flutter_pos_printer_platform_image_3.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:ticket_booth/app/print_ticket.dart';
import 'package:ticket_booth/model/branch_model.dart';
import 'package:ticket_booth/model/cabapility_model.dart';
import 'package:ticket_booth/screen/widgets/error_dialog.dart';
import '../../app/constant.dart';
import '../../model/booth.dart';
import '../../model/printer_device.dart';
class FormInput extends HookConsumerWidget {
FormInput(
{Key? key,
required this.selected_booth,
required this.pm,
required this.booth,
required this.selected_printer,
required this.prnt,
required this.hostIpController,
required this.hostQrController,
required this.headerDisplayController,
required this.footerDisplayController,
required this.headerTicketController,
required this.footerTicketController,
required this.isLoading,
required this.capabilityList,
required this.selectedCapability,
required this.getServiceBooth,
required this.branch,
required this.selected_branch})
: super(key: key);
final ValueNotifier<Booth> selected_booth;
final ValueNotifier<List<Booth>> booth;
final ValueNotifier<List<BranchModel>> branch;
final ValueNotifier<BranchModel> selected_branch;
final ValueNotifier<PrinterDev?> selected_printer;
final ValueNotifier<List<PrinterDev>> prnt;
final ValueNotifier<List<CapabilityProfileModel>> capabilityList;
final ValueNotifier<CapabilityProfileModel?> selectedCapability;
final TextEditingController hostIpController;
final TextEditingController hostQrController;
final TextEditingController headerDisplayController;
final TextEditingController footerDisplayController;
final TextEditingController headerTicketController;
final TextEditingController footerTicketController;
final PrinterManager pm;
final Function getServiceBooth;
final bool isLoading;
@override
Widget build(BuildContext context, WidgetRef ref) {
final hostIpError = useState(false);
final hostQrError = useState(false);
;
final usbStream =
useStream<USBStatus>(pm.stateUSB, initialData: USBStatus.none);
_connectDevice(PrinterDev selectedPrinter, PrinterType type,
{bool reconnect = true, String? ipAddress = null}) async {
try {
await pm.connect(
type: type,
model: UsbPrinterInput(
name: selectedPrinter.deviceName,
productId: selectedPrinter.productId,
vendorId: selectedPrinter.vendorId));
} catch (e) {
print(e);
}
}
return Row(
children: [
SizedBox(
width: Constant.getActualX(context: context, x: 625),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
child: Tooltip(
message: "Pilih salah satu cabang ",
child: Text(
'Cabang*',
style: Constant.label(context: context),
),
),
),
SizedBox(
height: Constant.getActualY(context: context, y: 60),
child: DropdownButtonFormField<BranchModel>(
style: Constant.normal(context: context)
.copyWith(color: Colors.black),
decoration: const InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(8),
),
),
),
menuMaxHeight: Constant.getActualY(context: context, y: 500),
hint: Text(selected_branch.value.mBranchName ?? ''),
items: branch.value
.map((e) => DropdownMenuItem<BranchModel>(
value: e, child: Text(e.mBranchName ?? '')))
.toList(),
onChanged: (BranchModel? e) {
// print(e);
selected_branch.value = e!;
},
),
),
SizedBox(
height: Constant.getActualY(context: context, y: 20),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Constant.getActualX(context: context, x: 170),
child: Tooltip(
message:
"Pilih salah satu capability profile yang akan digunakan",
child: Text(
'Printer*',
style: Constant.label(context: context),
),
),
),
SizedBox(
width: Constant.getActualX(context: context, x: 170),
child: Tooltip(
message:
"Pilih salah satu printer yang akan digunakan",
child: Text(
'Capability Profile*',
style: Constant.label(context: context),
),
),
),
SizedBox(
width: Constant.getActualX(context: context, x: 100),
),
SizedBox(
width: Constant.getActualX(context: context, x: 100),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Constant.getActualX(context: context, x: 170),
height: Constant.getActualY(context: context, y: 60),
child: DropdownButtonFormField<PrinterDev>(
value: selected_printer.value,
isExpanded: true,
style: Constant.normal(context: context)
.copyWith(color: Colors.black),
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(8),
),
),
),
menuMaxHeight:
Constant.getActualY(context: context, y: 500),
hint: Text(
selected_printer.value?.deviceName
.replaceAll(' ', '\u00A0') ??
'',
// softWrap: true,
overflow: TextOverflow.ellipsis,
),
items: prnt.value
.map((e) => DropdownMenuItem<PrinterDev>(
value: e,
child: Text(
e.deviceName.replaceAll(' ', '\u00A0'),
// softWrap: true,
overflow: TextOverflow.ellipsis,
)))
.toList(),
onChanged: (PrinterDev? e) {
selected_printer.value = e!;
},
)),
SizedBox(
width: Constant.getActualX(context: context, x: 170),
height: Constant.getActualY(context: context, y: 60),
child:
DropdownButtonFormField<CapabilityProfileModel>(
value: selectedCapability.value,
isExpanded: true,
style: Constant.normal(context: context)
.copyWith(color: Colors.black),
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(8),
),
),
),
menuMaxHeight:
Constant.getActualY(context: context, y: 500),
hint: Text(
selectedCapability.value?.key
?.replaceAll(' ', '\u00A0') ??
'',
// softWrap: true,
overflow: TextOverflow.ellipsis,
),
items: capabilityList.value
.map((e) =>
DropdownMenuItem<CapabilityProfileModel>(
value: e,
child: Text(
e.key!.replaceAll(' ', '\u00A0'),
// softWrap: true,
overflow: TextOverflow.ellipsis,
)))
.toList(),
onChanged: (CapabilityProfileModel? e) {
selectedCapability.value = e!;
},
)),
SizedBox(
// width: Constant.getActualX(context: context, x: 100),
height: Constant.getActualY(context: context, y: 60),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red),
onPressed: () {
try {
if (selected_printer.value != null) {
_connectDevice(
selected_printer.value!, PrinterType.usb);
} else {
throw Exception("Printer tidak dipilih");
}
} catch (e) {
ErrorDialog(context, e.toString(), 'ERROR');
print(e);
}
},
child: Tooltip(
message:
"Tombol untuk menyambungkan printer yang elah dipilih",
child: Text(
"Connect",
style: Constant.label(context: context)
.copyWith(color: Colors.white),
),
)),
),
SizedBox(
// width: Constant.getActualX(context: context, x: 100),
height: Constant.getActualY(context: context, y: 60),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green),
onPressed: () {
try {
PrintTicket().printTest();
} catch (e) {
ErrorDialog(context, e.toString(), 'ERROR');
print(e);
}
},
child: Tooltip(
message:
"Tombol untuk tes print, setelah terhubung akan mencetak teks tes print",
child: Text(
"Test Print",
style: Constant.label(context: context)
.copyWith(color: Colors.white),
),
)),
),
],
),
],
),
SizedBox(
height: Constant.getActualY(context: context, y: 20),
),
Tooltip(
message:
"Domain yang akan digunakan untuk mendapatkan daftar layanan dan nomor antrian",
child: Text(
'Host IP*',
style: Constant.label(context: context),
),
),
SizedBox(
height: Constant.getActualY(context: context, y: 60),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Constant.getActualX(context: context, x: 500),
child: TextField(
controller: hostIpController,
onChanged: (value) {
if (value.trim() == "") {
hostIpError.value = true;
}
if (value.trim() != "") {
hostIpError.value = false;
}
},
decoration: InputDecoration(
errorText:
hostIpError.value ? "Tidak boleh kosong" : null,
border: const OutlineInputBorder(),
focusedBorder: const OutlineInputBorder(
borderSide:
BorderSide(color: Colors.blue, width: 2)),
hintText: "http://devone.aplikasi.web.id",
hintStyle: Constant.label(context: context)),
),
),
SizedBox(
// width: Constant.getActualX(context: context, x: 200),
height: Constant.getActualY(context: context, y: 60),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green),
onPressed: hostIpController.text == ""
? null
: () {
getServiceBooth();
},
child: Tooltip(
message:
"Tombol untuk menampilkan daftar layanan apabila tidak muncul",
child: isLoading
? CircularProgressIndicator(
color: Colors.white,
)
: Text(
"Layanan ",
style: Constant.label(context: context)
.copyWith(color: Colors.white),
),
)),
),
],
),
),
SizedBox(
height: Constant.getActualY(context: context, y: 20),
),
Tooltip(
message:
"Teks yang akan di cetak dibagian atas tiket. Maksimal 2 baris",
child: Text(
'Header Ticket',
style: Constant.label(context: context),
),
),
SizedBox(
height: Constant.getActualY(context: context, y: 80),
child: TextField(
controller: headerTicketController,
maxLines: 2,
decoration: InputDecoration(
border: const OutlineInputBorder(),
hintText: "Masukkan header ticket",
hintStyle: Constant.label(context: context)),
),
),
],
),
),
SizedBox(
width: Constant.getActualX(context: context, x: 50),
),
SizedBox(
width: Constant.getActualX(context: context, x: 625),
child:
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Tooltip(
message: "Teks yang akan di tampilkan di display bagian atas",
child: Text(
'Header Display',
style: Constant.label(context: context),
),
),
SizedBox(
height: Constant.getActualY(context: context, y: 60),
child: TextField(
controller: headerDisplayController,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Masukkan header display",
hintStyle: Constant.label(context: context)),
),
),
SizedBox(
height: Constant.getActualY(context: context, y: 20),
),
Tooltip(
message: "Teks yang akan di tampilkan di bagian bawah display",
child: Text(
'Footer Display',
style: Constant.label(context: context),
),
),
SizedBox(
height: Constant.getActualY(context: context, y: 60),
child: TextField(
controller: footerDisplayController,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Masukkan footer display",
hintStyle: Constant.label(context: context)),
),
),
SizedBox(
height: Constant.getActualY(context: context, y: 20),
),
SizedBox(
child: Tooltip(
message: "Pilih salah satu booth ",
child: Text(
'Booth*',
style: Constant.label(context: context),
),
),
),
SizedBox(
height: Constant.getActualY(context: context, y: 60),
child: DropdownButtonFormField<Booth>(
style: Constant.normal(context: context)
.copyWith(color: Colors.black),
decoration: const InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(8),
),
),
),
menuMaxHeight: Constant.getActualY(context: context, y: 500),
hint: Text(selected_booth.value.name),
items: booth.value
.map((e) => DropdownMenuItem<Booth>(
value: e, child: Text(e.name)))
.toList(),
onChanged: (Booth? e) {
// print(e);
selected_booth.value = e!;
},
),
),
SizedBox(
height: Constant.getActualY(context: context, y: 20),
),
Tooltip(
message:
"Teks yang akan di cetak di bagian bawah tiket. Maksimal 2 baris",
child: Text(
'Footer Ticket',
style: Constant.label(context: context),
),
),
SizedBox(
height: Constant.getActualY(context: context, y: 80),
child: TextField(
maxLines: 2,
controller: footerTicketController,
decoration: InputDecoration(
border: const OutlineInputBorder(),
hintText: "Masukkan footer ticket",
hintStyle: Constant.label(context: context)),
),
),
]))
],
);
}
}

View File

@@ -0,0 +1,63 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:ticket_booth/model/layanan.dart';
import 'package:ticket_booth/provider/dio_provider.dart';
import 'package:ticket_booth/repository/base_repository.dart';
import 'package:ticket_booth/repository/service_repository.dart';
abstract class ServiceListState extends Equatable {
final DateTime date;
const ServiceListState(this.date);
@override
List<Object?> get props => [date];
}
class ServiceListStateInit extends ServiceListState {
ServiceListStateInit() : super(DateTime.now());
}
class ServiceListStateLoading extends ServiceListState {
ServiceListStateLoading() : super(DateTime.now());
}
class ServiceListStateError extends ServiceListState {
final String message;
ServiceListStateError({
required this.message,
}) : super(DateTime.now());
}
class ServiceListStateDone extends ServiceListState {
final List<Layanan> model;
ServiceListStateDone({
required this.model,
}) : super(DateTime.now());
}
//notifier
class ServiceListNotifier extends StateNotifier<ServiceListState> {
final Ref ref;
ServiceListNotifier({
required this.ref,
}) : super(ServiceListStateInit());
void list({required hostIp}) async {
try {
state = ServiceListStateLoading();
final dio = ref.read(dioProvider);
final resp = await ServiceRepository(dio: dio).getData(hostIp: hostIp);
state = ServiceListStateDone(model: resp);
} catch (e) {
if (e is BaseRepositoryException) {
state = ServiceListStateError(message: e.message);
} else {
state = ServiceListStateError(message: e.toString());
}
}
}
}
//provider
final ServiceListProvider =
StateNotifierProvider<ServiceListNotifier, ServiceListState>(
(ref) => ServiceListNotifier(ref: ref));

View File

@@ -0,0 +1,559 @@
import 'dart:convert';
import 'package:esc_pos_utils/esc_pos_utils.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_pos_printer_platform_image_3/flutter_pos_printer_platform_image_3.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:ticket_booth/app/print_ticket.dart';
import 'package:ticket_booth/app/route.dart';
import 'package:ticket_booth/model/booth.dart';
import 'package:ticket_booth/model/branch_model.dart';
import 'package:ticket_booth/model/cabapility_model.dart';
import 'package:ticket_booth/model/layanan.dart';
import 'package:ticket_booth/model/printer_device.dart';
import 'package:ticket_booth/provider/all_service_provider.dart';
import 'package:ticket_booth/screen/settings/booth_list_provider.dart';
import 'package:ticket_booth/screen/settings/branch_list_provider.dart';
import 'package:ticket_booth/screen/settings/service_list_provider.dart';
import '../../app/constant.dart';
import '../widgets/error_dialog.dart';
import 'form_input.dart';
class SettingScreen extends HookConsumerWidget {
const SettingScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final hostIpController = useTextEditingController(text: '');
final hostQrController = useTextEditingController(text: '');
final headerDisplayController = useTextEditingController(text: '');
final footerDisplayController = useTextEditingController(text: '');
final footerTicketController = useTextEditingController(text: '');
final headerTicketController = useTextEditingController(text: '');
final activeLayanan = useState<List>(List.empty());
final capabilityProfileList = useState<List<CapabilityProfileModel>>([]);
final selectedCapabilityProfile = useState<CapabilityProfileModel?>(null);
final isLoading = useState(false);
final data = useState('N');
final PrinterManager pm = PrinterManager.instance;
final dvcs = useState<List<PrinterDev>>([]);
var devices3 = <PrinterDev>[];
final selectedBtnSize = useState<Map<String, dynamic>>({
'name': '4',
'message': 'Tombol yang akan ditampilkan berjumlah 4 ',
'rightLeftWidth': "300",
'centerWidth': "912",
'childAspectRatio': '1.559',
'mainAxisSpacing': "10",
'crossAxisSpacing': '10',
'btnHeight': "330",
'btnWidth': '300',
'column': '2'
});
final List<Map<String, dynamic>> listBtnSize = [
{
'name': '10',
'message': 'Tombol yang akan ditampilkan berjumlah 10',
'rightLeftWidth': "40",
'centerWidth': "1432",
'childAspectRatio': '1',
'mainAxisSpacing': "20",
'crossAxisSpacing': '10',
'btnHeight': "290",
'btnWidth': '260',
'column': '5'
},
{
'name': '8',
'message': 'Tombol yang akan ditampilkan berjumlah 8 ',
'rightLeftWidth': "100",
'centerWidth': "1312",
'childAspectRatio': '1.1',
'mainAxisSpacing': "40",
'crossAxisSpacing': '10',
'btnHeight': "310",
'btnWidth': '280',
'column': '4'
},
{
'name': '6',
'message': 'Tombol yang akan ditampilkan berjumlah 6 ',
'rightLeftWidth': "200",
'centerWidth': "1112",
'childAspectRatio': '1.33',
'mainAxisSpacing': "40",
'crossAxisSpacing': '10',
'btnHeight': "310",
'btnWidth': '280',
'column': '3'
},
{
'name': '4',
'message': 'Tombol yang akan ditampilkan berjumlah 4 ',
'rightLeftWidth': "300",
'centerWidth': "912",
'childAspectRatio': '1.559',
'mainAxisSpacing': "10",
'crossAxisSpacing': '10',
'btnHeight': "330",
'btnWidth': '300',
'column': '2'
}
];
getServiceBooth() async {
activeLayanan.value = List.empty();
ref.read(BoothListProvider.notifier).list(hostIp: hostIpController.text);
ref
.read(ServiceListProvider.notifier)
.list(hostIp: hostIpController.text);
ref.read(BranchListProvider.notifier).list(hostIp: hostIpController.text);
}
_scan(PrinterType type) async {
// Find printers
pm.discovery(type: PrinterType.usb, isBle: false).listen((device) {
devices3.add(PrinterDev(
id: int.parse(device.productId ?? '0'),
deviceName: device.name,
address: device.address,
isBle: false,
vendorId: device.vendorId,
productId: device.productId,
));
});
}
full() async {
// await FullScreen.enterFullScreen(FullScreenMode.EMERSIVE_STICKY);
}
final lyn = useState<List<Layanan>>(List.empty());
final booth = useState<List<Booth>>(List.empty());
final branch = useState<List<BranchModel>>(List.empty());
final selected_printer = useState<PrinterDev?>(null);
final selected_booth =
useState<Booth>(Booth(name: 'Pilih salah satu', id: 0, code: 'b1'));
final selected_branch = useState<BranchModel>(
BranchModel(mBranchID: '0', mBranchName: 'Pilih salah satu cabang'));
Future delData() async {
// Obtain shared preferences.
try {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('tb-westerindo');
} catch (e) {
print(e);
}
}
Future getData() async {
try {
final prefs = await SharedPreferences.getInstance();
var raw_data = prefs.getString('tb-westerindo') ?? 'a';
data.value = raw_data;
print(raw_data);
if (raw_data != 'a') {
lyn.value = ref.read(allServiceProvider);
var data = json.decode(raw_data);
selected_booth.value = Booth.fromJson(data['selectedBooth']);
selected_branch.value = BranchModel.fromJson(data['selectedBranch']);
selected_printer.value = PrinterDev.fromJson(data['selectedPrinter']);
hostIpController.text = data['hostIp'];
hostQrController.text = data['hostQrIp'];
headerDisplayController.text = data['headerDisplay'];
footerDisplayController.text = data['footerDisplay'];
footerTicketController.text = data['footerTicket'];
headerTicketController.text = data['headerTicket'];
activeLayanan.value = data['activeLayanan'] ?? List.empty();
selectedBtnSize.value = data['buttonSize'];
selectedCapabilityProfile.value =
CapabilityProfileModel.fromJson(data['selectedCapability']);
if (activeLayanan.value.isNotEmpty) {
activeLayanan.value.forEach((e) {
lyn.value.forEach((f) {
if (e == f.id) {
f.value = true;
}
});
});
}
}
} catch (e) {
print(e);
ErrorDialog(context, e.toString(), 'ERROR');
}
}
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await getData();
var capabilityList = await CapabilityProfile.getAvailableProfiles();
List<CapabilityProfileModel> cl = List.empty(growable: true);
for (var i = 0; i < capabilityList.length; i++) {
var data = capabilityList[i];
final model = CapabilityProfileModel.fromJson(data);
cl.add(model);
}
capabilityProfileList.value = cl;
var stb = capabilityProfileList.value.firstWhere(
(element) => element.key == selectedCapabilityProfile.value?.key,
orElse: () => CapabilityProfileModel());
if (stb.key != null) {
selectedCapabilityProfile.value = stb;
}
_scan(PrinterType.usb);
dvcs.value = devices3;
if (data.value != 'a') {
ref
.read(BoothListProvider.notifier)
.list(hostIp: hostIpController.text);
ref
.read(BranchListProvider.notifier)
.list(hostIp: hostIpController.text);
}
});
full();
return () {};
}, []);
ref.listen(BoothListProvider, (previous, next) {
if (next is BoothListStateLoading) {
isLoading.value = true;
} else if (next is BoothListStateError) {
ErrorDialog(context, next.message, 'ERROR');
isLoading.value = false;
} else if (next is BoothListStateDone) {
booth.value = next.model;
isLoading.value = false;
}
});
ref.listen(ServiceListProvider, (previous, next) {
if (next is ServiceListStateLoading) {
isLoading.value = true;
} else if (next is ServiceListStateError) {
ErrorDialog(context, next.message, 'ERROR');
isLoading.value = false;
} else if (next is ServiceListStateDone) {
// ref.read(allServiceProvider.notifier).state = next.model;
// print(ref.read(allServiceProvider));
lyn.value = next.model;
isLoading.value = false;
}
});
ref.listen(BranchListProvider, (previous, next) {
if (next is BranchListStateLoading) {
isLoading.value = true;
} else if (next is BranchListStateError) {
ErrorDialog(context, next.message, 'ERROR');
isLoading.value = false;
} else if (next is BranchListStateDone) {
// ref.read(allServiceProvider.notifier).state = next.model;
// print(ref.read(allServiceProvider));
branch.value = next.model;
isLoading.value = false;
}
});
Future<bool> saveData() async {
try {
final prefs = await SharedPreferences.getInstance();
final Map<String, dynamic> data = {
"headerDisplay": headerDisplayController.text != ''
? headerDisplayController.text
: 'Selamat Datang Di Pramita',
"footerDisplay": footerDisplayController.text != ''
? footerDisplayController.text
: 'Sentuh Salah Satu Layanan',
"footerTicket": footerTicketController.text != ''
? footerTicketController.text
: 'SCAN QR CODE UNTUK MELIHAT STATUS ANTRIAN',
"headerTicket": headerTicketController.text != ''
? headerTicketController.text
: 'Labratorium Medis & Klinik PRAMITA',
"hostQrIp": hostQrController.text,
"hostIp": hostIpController.text,
"selectedPrinter": selected_printer.value,
"selectedBooth": selected_booth.value,
"selectedBranch": selected_branch.value,
"activeLayanan": activeLayanan.value,
'buttonSize': selectedBtnSize.value,
"selectedCapability": selectedCapabilityProfile.value
};
json.encode(data);
await prefs.setString('tb-westerindo', json.encode(data));
return true;
} catch (e) {
ErrorDialog(context, e.toString(), 'ERROR');
print(e);
return false;
}
}
Color getColor(Set<MaterialState> states) {
const Set<MaterialState> interactiveStates = <MaterialState>{
MaterialState.pressed,
MaterialState.hovered,
MaterialState.focused,
};
if (states.any(interactiveStates.contains)) {
return Colors.blue;
}
if (states.contains(MaterialState.selected)) {
return Colors.green;
}
return Colors.white;
}
return Material(
child: Container(
height: Constant.getActualY(context: context, y: 982),
width: Constant.getActualX(context: context, x: 1512),
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: AssetImage("assets/images/new-bg-icon.png"))),
child: Center(
child: Card(
color: Colors.white,
shadowColor: Colors.white,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10))),
elevation: 5,
child: SizedBox(
height: Constant.getActualY(context: context, y: 880),
width: Constant.getActualX(context: context, x: 1360),
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: Constant.getActualX(context: context, x: 30),
vertical: Constant.getActualY(context: context, y: 30)),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
// TextButton(
// onPressed: () {
// Navigator.of(context)
// .popAndPushNamed(displayRoute);
// // print(pm.currentStatusUSB);
// // print(pm.currentStatusUSB);
// },
// child: const Text('back')),
Text(
'Setting',
style: Constant.subTitle(context: context),
),
// TextButton(
// onPressed: () {
// delData();
// },
// child: const Text('Delete All')),
],
),
SizedBox(
height: Constant.getActualY(context: context, y: 30),
),
FormInput(
pm: pm,
selected_booth: selected_booth,
booth: booth,
selected_printer: selected_printer,
prnt: dvcs,
branch: branch,
selected_branch: selected_branch,
hostIpController: hostIpController,
hostQrController: hostQrController,
headerDisplayController: headerDisplayController,
footerDisplayController: footerDisplayController,
headerTicketController: headerTicketController,
getServiceBooth: getServiceBooth,
isLoading: isLoading.value,
selectedCapability: selectedCapabilityProfile,
capabilityList: capabilityProfileList,
footerTicketController: footerTicketController),
SizedBox(
height: Constant.getActualY(context: context, y: 20),
),
Container(
// color: Colors.red,
height: Constant.getActualY(context: context, y: 40),
child: Row(
children: [
Tooltip(
message: "Jumlah tombol pada display",
child: Text('Button display',
style: Constant.label(context: context)),
),
SizedBox(
width: Constant.getActualX(context: context, x: 10),
),
Row(
children: [
SizedBox(
width: Constant.getActualX(
context: context, x: 10),
),
Row(
children: listBtnSize
.map(
(e) => Row(
children: [
Checkbox(
fillColor: MaterialStateProperty
.resolveWith(getColor),
value: e['name'].toString() ==
selectedBtnSize
.value['name']
? true
: false,
onChanged: ((value) {
selectedBtnSize.value = e;
})),
SizedBox(
width: Constant.getActualX(
context: context, x: 3),
),
Tooltip(
message: e['message'].toString(),
child: Text(
e['name'].toString(),
style: Constant.normal(
context: context),
),
),
SizedBox(
width: Constant.getActualX(
context: context, x: 15),
),
],
),
)
.toList(),
),
],
),
],
),
),
SizedBox(
height: Constant.getActualY(context: context, y: 190),
child: GridView.count(
childAspectRatio:
Constant.getActualY(context: context, y: 150) /
Constant.getActualX(context: context, x: 15),
crossAxisCount: 3,
children: lyn.value
.map((e) => Row(
children: [
Transform.scale(
scale: 0.8,
child: CupertinoSwitch(
value: e.value,
onChanged: (val) {
if (val) {
var rw_data = json.encode(
activeLayanan.value);
List data =
json.decode(rw_data);
List actlyn = data;
actlyn.add(e.id);
activeLayanan.value = actlyn;
}
if (!val) {
var rw_data = json.encode(
activeLayanan.value);
List data =
json.decode(rw_data);
List actlyn = data;
actlyn.remove(e.id);
activeLayanan.value = actlyn;
}
e.value = val;
}),
),
Expanded(
child: e.isConsultDoctor == 'N'
? Text(
e.name,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: Constant.normal(
context: context),
)
: Text(
"${e.name} ${e.doctorName}",
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: Constant.normal(
context: context),
),
)
],
))
.toList()),
),
SizedBox(
height: Constant.getActualY(context: context, y: 20),
),
Center(
child: SizedBox(
height: Constant.getActualY(context: context, y: 60),
width: Constant.getActualX(context: context, x: 200),
child: ElevatedButton(
onPressed: () {
if (selected_booth.value != null &&
selected_booth.value.id > 0 &&
selected_printer.value != null &&
selectedCapabilityProfile.value?.key !=
null &&
selected_branch.value != null &&
selected_branch.value.mBranchID != '0' &&
hostIpController.text.isNotEmpty &&
activeLayanan.value.isNotEmpty) {
saveData();
ref.read(allServiceProvider.notifier).state =
lyn.value;
Navigator.of(context).pushNamed(queueRoute);
} else {
if (selected_booth.value == null ||
selected_printer.value == null ||
selectedCapabilityProfile.value == null ||
hostIpController.text.isEmpty) {
ErrorDialog(
context,
'Input bertanda * tidak boleh kosong',
'PERINGATAN');
} else if (activeLayanan.value.isEmpty) {
ErrorDialog(
context,
'Layanan belum ada yang aktif',
'PERINGATAN');
}
}
},
style: ElevatedButton.styleFrom(
elevation: 5, backgroundColor: Colors.blue),
child: Text(
'Save',
style: Constant.subTitle(context: context)
.copyWith(color: Colors.white),
),
)),
)
],
),
),
),
),
),
),
));
}
}

View File

@@ -0,0 +1,103 @@
import 'dart:async';
import 'dart:convert';
import 'package:animated_text_kit/animated_text_kit.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_pos_printer_platform_image_3/flutter_pos_printer_platform_image_3.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:ticket_booth/app/constant.dart';
import 'package:ticket_booth/app/route.dart';
import 'package:ticket_booth/model/printer_device.dart';
import '../widgets/error_dialog.dart';
class SplashScreen extends HookConsumerWidget {
const SplashScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final PrinterManager pm = PrinterManager.instance;
_connectDevice(PrinterDev selectedPrinter, PrinterType type,
{bool reconnect = true, String? ipAddress = null}) async {
try {
await pm.connect(
type: type,
model: UsbPrinterInput(
name: selectedPrinter.deviceName,
productId: selectedPrinter.productId,
vendorId: selectedPrinter.vendorId));
} catch (e) {
print(e);
}
}
Future getData() async {
try {
final prefs = await SharedPreferences.getInstance();
var raw_data = prefs.getString('tb-westerindo') ?? 'a';
if (raw_data != 'a') {
var data = json.decode(raw_data);
PrinterDev selectedPrinter =
PrinterDev.fromJson(data['selectedPrinter']);
_connectDevice(selectedPrinter, PrinterType.usb);
}
} catch (e) {
print(e);
ErrorDialog(context, e.toString(), 'ERROR');
}
}
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await getData();
Timer(const Duration(seconds: 3), () {
Navigator.of(context).pushNamed(displayRoute);
});
});
return () {};
}, []);
return Material(
child: Container(
height: Constant.getActualY(context: context, y: 982),
width: Constant.getActualX(context: context, x: 1512),
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: AssetImage("assets/images/new-bg-icon.png"))),
child: Column(
children: [
SizedBox(
height: Constant.getActualY(context: context, y: 600),
width: Constant.getActualX(context: context, x: 1512),
),
const SpinKitFadingCube(
color: Colors.redAccent,
size: 50.0,
),
SizedBox(
height: Constant.getActualY(context: context, y: 50),
),
DefaultTextStyle(
style: Constant.title(context: context)
.copyWith(fontStyle: FontStyle.italic, color: Colors.black),
child: AnimatedTextKit(animatedTexts: [
TyperAnimatedText('Loading ...',
speed: Duration(milliseconds: 300))
])),
SizedBox(
height: Constant.getActualY(context: context, y: 50),
),
Text(
Constant.versi,
style: Constant.subTitle(context: context)
.copyWith(fontStyle: FontStyle.italic, color: Colors.grey),
)
],
),
));
}
}

View File

@@ -0,0 +1,135 @@
import 'package:flutter/material.dart';
import 'package:qr_flutter/qr_flutter.dart';
import '../../app/constant.dart';
// ignore: non_constant_identifier_names
Future<String?> DialogPrint(BuildContext context, String noAntrian,
Function funcPrintTicket, String serviceName) {
return showDialog<String>(
context: context,
builder: (BuildContext context) => Dialog(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
width: Constant.getActualX(context: context, x: 700),
alignment: Alignment.center,
child: Text(
textAlign: TextAlign.center,
'CETAK TIKET ANTRIAN',
style: Constant.subTitle(context: context),
),
),
const SizedBox(
height: 10,
),
SizedBox(
width: Constant.getActualX(context: context, x: 700),
child: const Divider()),
Container(
width: Constant.getActualX(context: context, x: 700),
alignment: Alignment.center,
child: Text(
textAlign: TextAlign.center,
serviceName,
style: Constant.subTitle(context: context),
),
),
const SizedBox(
height: 10,
),
Container(
width: Constant.getActualX(context: context, x: 700),
alignment: Alignment.center,
child: Text(
textAlign: TextAlign.center,
noAntrian,
style: Constant.subTitle(context: context),
),
),
Container(
width: Constant.getActualX(context: context, x: 700),
alignment: Alignment.center,
child: QrImageView(
data: noAntrian,
version: QrVersions.auto,
size: 290,
// embeddedImage: AssetImage('assets/images/logo-westerindo.png'),
// embeddedImageStyle: ,
gapless: false,
),
),
SizedBox(
height: 10,
),
Container(
width: Constant.getActualX(context: context, x: 700),
alignment: Alignment.center,
child: ElevatedButton(
onPressed: () {
funcPrintTicket(noAntrian);
},
style: ElevatedButton.styleFrom(
elevation: 5, backgroundColor: Colors.blue),
child: Text(
'Cetak Tiket',
style: Constant.label(context: context)
.copyWith(color: Colors.white),
),
),
),
SizedBox(
height: 10,
),
Container(
width: Constant.getActualX(context: context, x: 700),
alignment: Alignment.center,
child: Text(
textAlign: TextAlign.center,
"Scan qr code atau tekan tombol 'cetak tiket' untuk mendapatkan tiket",
style: Constant.label(context: context),
),
),
const SizedBox(
height: 10,
),
Container(
width: Constant.getActualX(context: context, x: 700),
alignment: Alignment.center,
child: Text(
textAlign: TextAlign.center,
"Tutup dialog setelah mendapatkan tiket",
style: Constant.label(context: context),
),
),
SizedBox(
width: Constant.getActualX(context: context, x: 700),
child: const Divider()),
const SizedBox(
height: 10,
),
SizedBox(
width: Constant.getActualX(context: context, x: 700),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Tutup'),
),
],
),
),
],
),
),
),
);
}

View File

@@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import '../../app/constant.dart';
Future<String?> ErrorDialog(
BuildContext context, String errorMsg, String header) {
return showDialog<String>(
context: context,
builder: (BuildContext context) => Dialog(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
header,
style: Constant.subTitle(context: context),
),
const SizedBox(
height: 10,
),
SizedBox(
width: Constant.getActualX(context: context, x: 700),
child: const Divider()),
SizedBox(
height: Constant.getActualY(context: context, y: 500),
width: Constant.getActualX(context: context, x: 700),
child: ListView(
children: [Text(errorMsg)],
),
),
SizedBox(
width: Constant.getActualX(context: context, x: 700),
child: const Divider()),
const SizedBox(
height: 10,
),
SizedBox(
width: Constant.getActualX(context: context, x: 700),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Tutup'),
),
],
),
),
],
),
),
),
);
}

View File

@@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import '../../app/constant.dart';
// ignore: non_constant_identifier_names
Future<String?> PasienOnlineDialog(BuildContext context) {
return showDialog<String>(
context: context,
builder: (BuildContext context) => Dialog(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Pasien Online',
style: Constant.subTitle(context: context),
),
const SizedBox(
height: 10,
),
SizedBox(
width: Constant.getActualX(context: context, x: 700),
child: const Divider()),
SizedBox(
height: Constant.getActualY(context: context, y: 500),
width: Constant.getActualX(context: context, x: 700),
child: ListView(
children: [TextField()],
),
),
SizedBox(
width: Constant.getActualX(context: context, x: 700),
child: const Divider()),
const SizedBox(
height: 10,
),
SizedBox(
width: Constant.getActualX(context: context, x: 700),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Tutup'),
),
],
),
),
],
),
),
),
);
}