diff --git a/lib/core/function/app_function.dart b/lib/core/function/app_function.dart index 5c4bd37..f62ac66 100644 --- a/lib/core/function/app_function.dart +++ b/lib/core/function/app_function.dart @@ -1,5 +1,7 @@ import 'dart:developer'; +import 'dart:typed_data'; +import 'package:barcode_image/barcode_image.dart'; import 'package:enaklo_pos/core/extensions/string_ext.dart'; import 'package:enaklo_pos/core/utils/printer_service.dart'; import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart'; @@ -9,6 +11,8 @@ import 'package:enaklo_pos/data/datasources/settings_local_datasource.dart'; import 'package:enaklo_pos/data/models/response/order_response_model.dart'; import 'package:enaklo_pos/presentation/home/models/product_quantity.dart'; import 'package:flutter/material.dart'; +import 'package:barcode/barcode.dart'; +import 'package:image/image.dart' as img; Future onPrint( BuildContext context, { @@ -124,3 +128,21 @@ Future onPrintRecipt( } } } + +Future generateBarcodeAsUint8List(String data) async { + // 1. Buat barcode instance (code128, qrCode, dll) + final barcode = Barcode.code128(); + + // 2. Siapkan canvas image dari package `image` + final image = img.Image(width: 600, height: 200); + + // 3. Gambar barcode ke canvas menggunakan barcode_image + drawBarcode( + image, + barcode, + data, + ); + + // 4. Encode image ke Uint8List PNG + return Uint8List.fromList(img.encodePng(image)); +} diff --git a/lib/data/dataoutputs/print_dataoutputs.dart b/lib/data/dataoutputs/print_dataoutputs.dart index 59d2d9d..eca3177 100644 --- a/lib/data/dataoutputs/print_dataoutputs.dart +++ b/lib/data/dataoutputs/print_dataoutputs.dart @@ -1398,4 +1398,39 @@ class PrintDataoutputs { return bytes; } + + Future> printTicket( + int totalPrice, Uint8List imageQris, int paper) async { + List bytes = []; + + final profile = await CapabilityProfile.load(); + final generator = + Generator(paper == 58 ? PaperSize.mm58 : PaperSize.mm80, profile); + + final img.Image? orginalImage = img.decodeImage(imageQris); + bytes += generator.reset(); + + // final Uint8List bytesData = data.buffer.asUint8List(); + // final img.Image? orginalImage = img.decodeImage(bytesData); + // bytes += generator.reset(); + + bytes += generator.text('Scan Ticket', + styles: const PosStyles(bold: false, align: PosAlign.center)); + bytes += generator.feed(2); + if (orginalImage != null) { + final img.Image grayscalledImage = img.grayscale(orginalImage); + final img.Image resizedImage = + img.copyResize(grayscalledImage, width: 240); + bytes += generator.imageRaster(resizedImage, align: PosAlign.center); + bytes += generator.feed(1); + } + + bytes += generator.text('Price : ${totalPrice.currencyFormatRp}', + styles: const PosStyles(bold: false, align: PosAlign.center)); + + bytes += generator.feed(4); + bytes += generator.cut(); + + return bytes; + } } diff --git a/lib/main.dart b/lib/main.dart index f41f73a..2c0c671 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,6 +18,7 @@ import 'package:enaklo_pos/presentation/refund/bloc/refund_bloc.dart'; import 'package:enaklo_pos/presentation/report/blocs/profit_loss/profit_loss_bloc.dart'; import 'package:enaklo_pos/presentation/sales/blocs/order_loader/order_loader_bloc.dart'; import 'package:enaklo_pos/presentation/sales/blocs/payment_form/payment_form_bloc.dart'; +import 'package:enaklo_pos/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_bloc.dart'; import 'package:enaklo_pos/presentation/setting/bloc/upload_file/upload_file_bloc.dart'; import 'package:enaklo_pos/presentation/void/bloc/void_order_bloc.dart'; import 'package:flutter/material.dart'; @@ -279,6 +280,9 @@ class _MyAppState extends State { BlocProvider( create: (context) => CategoryLoaderBloc(CategoryRemoteDatasource()), ), + BlocProvider( + create: (context) => GetPrinterTicketBloc(), + ), ], child: MaterialApp( debugShowCheckedModeBanner: false, diff --git a/lib/presentation/home/pages/dashboard_page.dart b/lib/presentation/home/pages/dashboard_page.dart index ce95eed..9c95225 100644 --- a/lib/presentation/home/pages/dashboard_page.dart +++ b/lib/presentation/home/pages/dashboard_page.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'dart:developer'; import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:enaklo_pos/presentation/setting/pages/setting_page.dart'; import 'package:enaklo_pos/presentation/table/pages/table_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -15,7 +16,6 @@ import 'package:enaklo_pos/presentation/auth/login_page.dart'; import 'package:enaklo_pos/presentation/report/pages/report_page.dart'; import 'package:enaklo_pos/presentation/setting/bloc/sync_order/sync_order_bloc.dart'; import 'package:enaklo_pos/presentation/setting/pages/printer_configuration_page.dart'; -import 'package:enaklo_pos/presentation/setting/pages/settings_page.dart'; import '../../../core/assets/assets.gen.dart'; import '../../auth/bloc/logout/logout_bloc.dart'; @@ -61,7 +61,7 @@ class _DashboardPageState extends State { const ReportPage(), const PrinterConfigurationPage(), // SalesPage(), - const SettingsPage(), + const SettingPage(), ]; // ignore: unused_local_variable _connectivitySubscription = Connectivity() diff --git a/lib/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_bloc.dart b/lib/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_bloc.dart new file mode 100644 index 0000000..c3101b0 --- /dev/null +++ b/lib/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_bloc.dart @@ -0,0 +1,20 @@ +import 'package:bloc/bloc.dart'; +import 'package:enaklo_pos/data/datasources/product_local_datasource.dart'; +import 'package:enaklo_pos/data/models/response/print_model.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'get_printer_ticket_event.dart'; +part 'get_printer_ticket_state.dart'; +part 'get_printer_ticket_bloc.freezed.dart'; + +class GetPrinterTicketBloc + extends Bloc { + GetPrinterTicketBloc() : super(_Initial()) { + on<_Get>((event, emit) async { + emit(_Loading()); + final result = + await ProductLocalDatasource.instance.getPrinterByCode('ticket'); + emit(_Success(result)); + }); + } +} diff --git a/lib/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_bloc.freezed.dart b/lib/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_bloc.freezed.dart new file mode 100644 index 0000000..2f6949b --- /dev/null +++ b/lib/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_bloc.freezed.dart @@ -0,0 +1,609 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'get_printer_ticket_bloc.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +/// @nodoc +mixin _$GetPrinterTicketEvent { + @optionalTypeArgs + TResult when({ + required TResult Function() get, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? get, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? get, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(_Get value) get, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Get value)? get, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Get value)? get, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $GetPrinterTicketEventCopyWith<$Res> { + factory $GetPrinterTicketEventCopyWith(GetPrinterTicketEvent value, + $Res Function(GetPrinterTicketEvent) then) = + _$GetPrinterTicketEventCopyWithImpl<$Res, GetPrinterTicketEvent>; +} + +/// @nodoc +class _$GetPrinterTicketEventCopyWithImpl<$Res, + $Val extends GetPrinterTicketEvent> + implements $GetPrinterTicketEventCopyWith<$Res> { + _$GetPrinterTicketEventCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of GetPrinterTicketEvent + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc +abstract class _$$GetImplCopyWith<$Res> { + factory _$$GetImplCopyWith(_$GetImpl value, $Res Function(_$GetImpl) then) = + __$$GetImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$GetImplCopyWithImpl<$Res> + extends _$GetPrinterTicketEventCopyWithImpl<$Res, _$GetImpl> + implements _$$GetImplCopyWith<$Res> { + __$$GetImplCopyWithImpl(_$GetImpl _value, $Res Function(_$GetImpl) _then) + : super(_value, _then); + + /// Create a copy of GetPrinterTicketEvent + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$GetImpl implements _Get { + const _$GetImpl(); + + @override + String toString() { + return 'GetPrinterTicketEvent.get()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$GetImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() get, + }) { + return get(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? get, + }) { + return get?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? get, + required TResult orElse(), + }) { + if (get != null) { + return get(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Get value) get, + }) { + return get(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Get value)? get, + }) { + return get?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Get value)? get, + required TResult orElse(), + }) { + if (get != null) { + return get(this); + } + return orElse(); + } +} + +abstract class _Get implements GetPrinterTicketEvent { + const factory _Get() = _$GetImpl; +} + +/// @nodoc +mixin _$GetPrinterTicketState { + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(PrintModel? printer) success, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(PrintModel? printer)? success, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function(PrintModel? printer)? success, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(_Initial value) initial, + required TResult Function(_Loading value) loading, + required TResult Function(_Success value) success, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Initial value)? initial, + TResult? Function(_Loading value)? loading, + TResult? Function(_Success value)? success, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Initial value)? initial, + TResult Function(_Loading value)? loading, + TResult Function(_Success value)? success, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $GetPrinterTicketStateCopyWith<$Res> { + factory $GetPrinterTicketStateCopyWith(GetPrinterTicketState value, + $Res Function(GetPrinterTicketState) then) = + _$GetPrinterTicketStateCopyWithImpl<$Res, GetPrinterTicketState>; +} + +/// @nodoc +class _$GetPrinterTicketStateCopyWithImpl<$Res, + $Val extends GetPrinterTicketState> + implements $GetPrinterTicketStateCopyWith<$Res> { + _$GetPrinterTicketStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of GetPrinterTicketState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc +abstract class _$$InitialImplCopyWith<$Res> { + factory _$$InitialImplCopyWith( + _$InitialImpl value, $Res Function(_$InitialImpl) then) = + __$$InitialImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$InitialImplCopyWithImpl<$Res> + extends _$GetPrinterTicketStateCopyWithImpl<$Res, _$InitialImpl> + implements _$$InitialImplCopyWith<$Res> { + __$$InitialImplCopyWithImpl( + _$InitialImpl _value, $Res Function(_$InitialImpl) _then) + : super(_value, _then); + + /// Create a copy of GetPrinterTicketState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$InitialImpl implements _Initial { + const _$InitialImpl(); + + @override + String toString() { + return 'GetPrinterTicketState.initial()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$InitialImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(PrintModel? printer) success, + }) { + return initial(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(PrintModel? printer)? success, + }) { + return initial?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function(PrintModel? printer)? success, + required TResult orElse(), + }) { + if (initial != null) { + return initial(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Initial value) initial, + required TResult Function(_Loading value) loading, + required TResult Function(_Success value) success, + }) { + return initial(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Initial value)? initial, + TResult? Function(_Loading value)? loading, + TResult? Function(_Success value)? success, + }) { + return initial?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Initial value)? initial, + TResult Function(_Loading value)? loading, + TResult Function(_Success value)? success, + required TResult orElse(), + }) { + if (initial != null) { + return initial(this); + } + return orElse(); + } +} + +abstract class _Initial implements GetPrinterTicketState { + const factory _Initial() = _$InitialImpl; +} + +/// @nodoc +abstract class _$$LoadingImplCopyWith<$Res> { + factory _$$LoadingImplCopyWith( + _$LoadingImpl value, $Res Function(_$LoadingImpl) then) = + __$$LoadingImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$LoadingImplCopyWithImpl<$Res> + extends _$GetPrinterTicketStateCopyWithImpl<$Res, _$LoadingImpl> + implements _$$LoadingImplCopyWith<$Res> { + __$$LoadingImplCopyWithImpl( + _$LoadingImpl _value, $Res Function(_$LoadingImpl) _then) + : super(_value, _then); + + /// Create a copy of GetPrinterTicketState + /// with the given fields replaced by the non-null parameter values. +} + +/// @nodoc + +class _$LoadingImpl implements _Loading { + const _$LoadingImpl(); + + @override + String toString() { + return 'GetPrinterTicketState.loading()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$LoadingImpl); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(PrintModel? printer) success, + }) { + return loading(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(PrintModel? printer)? success, + }) { + return loading?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function(PrintModel? printer)? success, + required TResult orElse(), + }) { + if (loading != null) { + return loading(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Initial value) initial, + required TResult Function(_Loading value) loading, + required TResult Function(_Success value) success, + }) { + return loading(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Initial value)? initial, + TResult? Function(_Loading value)? loading, + TResult? Function(_Success value)? success, + }) { + return loading?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Initial value)? initial, + TResult Function(_Loading value)? loading, + TResult Function(_Success value)? success, + required TResult orElse(), + }) { + if (loading != null) { + return loading(this); + } + return orElse(); + } +} + +abstract class _Loading implements GetPrinterTicketState { + const factory _Loading() = _$LoadingImpl; +} + +/// @nodoc +abstract class _$$SuccessImplCopyWith<$Res> { + factory _$$SuccessImplCopyWith( + _$SuccessImpl value, $Res Function(_$SuccessImpl) then) = + __$$SuccessImplCopyWithImpl<$Res>; + @useResult + $Res call({PrintModel? printer}); +} + +/// @nodoc +class __$$SuccessImplCopyWithImpl<$Res> + extends _$GetPrinterTicketStateCopyWithImpl<$Res, _$SuccessImpl> + implements _$$SuccessImplCopyWith<$Res> { + __$$SuccessImplCopyWithImpl( + _$SuccessImpl _value, $Res Function(_$SuccessImpl) _then) + : super(_value, _then); + + /// Create a copy of GetPrinterTicketState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? printer = freezed, + }) { + return _then(_$SuccessImpl( + freezed == printer + ? _value.printer + : printer // ignore: cast_nullable_to_non_nullable + as PrintModel?, + )); + } +} + +/// @nodoc + +class _$SuccessImpl implements _Success { + const _$SuccessImpl(this.printer); + + @override + final PrintModel? printer; + + @override + String toString() { + return 'GetPrinterTicketState.success(printer: $printer)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SuccessImpl && + const DeepCollectionEquality().equals(other.printer, printer)); + } + + @override + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(printer)); + + /// Create a copy of GetPrinterTicketState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$SuccessImplCopyWith<_$SuccessImpl> get copyWith => + __$$SuccessImplCopyWithImpl<_$SuccessImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() loading, + required TResult Function(PrintModel? printer) success, + }) { + return success(printer); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? initial, + TResult? Function()? loading, + TResult? Function(PrintModel? printer)? success, + }) { + return success?.call(printer); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? loading, + TResult Function(PrintModel? printer)? success, + required TResult orElse(), + }) { + if (success != null) { + return success(printer); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Initial value) initial, + required TResult Function(_Loading value) loading, + required TResult Function(_Success value) success, + }) { + return success(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Initial value)? initial, + TResult? Function(_Loading value)? loading, + TResult? Function(_Success value)? success, + }) { + return success?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Initial value)? initial, + TResult Function(_Loading value)? loading, + TResult Function(_Success value)? success, + required TResult orElse(), + }) { + if (success != null) { + return success(this); + } + return orElse(); + } +} + +abstract class _Success implements GetPrinterTicketState { + const factory _Success(final PrintModel? printer) = _$SuccessImpl; + + PrintModel? get printer; + + /// Create a copy of GetPrinterTicketState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$SuccessImplCopyWith<_$SuccessImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_event.dart b/lib/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_event.dart new file mode 100644 index 0000000..4bbc8f9 --- /dev/null +++ b/lib/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_event.dart @@ -0,0 +1,6 @@ +part of 'get_printer_ticket_bloc.dart'; + +@freezed +class GetPrinterTicketEvent with _$GetPrinterTicketEvent { + const factory GetPrinterTicketEvent.get() = _Get; +} diff --git a/lib/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_state.dart b/lib/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_state.dart new file mode 100644 index 0000000..9df4697 --- /dev/null +++ b/lib/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_state.dart @@ -0,0 +1,8 @@ +part of 'get_printer_ticket_bloc.dart'; + +@freezed +class GetPrinterTicketState with _$GetPrinterTicketState { + const factory GetPrinterTicketState.initial() = _Initial; + const factory GetPrinterTicketState.loading() = _Loading; + const factory GetPrinterTicketState.success(PrintModel? printer) = _Success; +} diff --git a/lib/presentation/setting/pages/printer_page.dart b/lib/presentation/setting/pages/printer_page.dart new file mode 100644 index 0000000..9dd7272 --- /dev/null +++ b/lib/presentation/setting/pages/printer_page.dart @@ -0,0 +1,42 @@ +import 'package:enaklo_pos/presentation/home/widgets/custom_tab_bar.dart'; +import 'package:enaklo_pos/presentation/setting/widgets/bar_printer_page.dart'; +import 'package:enaklo_pos/presentation/setting/widgets/checker_printer_page.dart'; +import 'package:enaklo_pos/presentation/setting/widgets/kitchen_printer_page.dart'; +import 'package:enaklo_pos/presentation/setting/widgets/receipt_printer_page.dart'; +import 'package:enaklo_pos/presentation/setting/widgets/settings_title.dart'; +import 'package:enaklo_pos/presentation/setting/widgets/ticket_printer_page.dart'; +import 'package:flutter/material.dart'; + +class SettingPrinterPage extends StatelessWidget { + const SettingPrinterPage({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + SettingsTitle( + 'Printer', + subtitle: 'Kelola printer untuk cetak struk', + ), + Expanded( + child: CustomTabBarV2( + tabTitles: [ + 'Receipt Printer', + 'Checker Printer', + 'Kitchen Printer', + 'Bar Printer', + 'Tiket Printer', + ], + tabViews: [ + ReceiptPrinterPage(), + CheckerPrinterPage(), + KitchenPrinterPage(), + BarPrinterPage(), + TicketPrinterPage() + ], + ), + ), + ], + ); + } +} diff --git a/lib/presentation/setting/pages/setting_page.dart b/lib/presentation/setting/pages/setting_page.dart new file mode 100644 index 0000000..d835371 --- /dev/null +++ b/lib/presentation/setting/pages/setting_page.dart @@ -0,0 +1,112 @@ +import 'package:enaklo_pos/core/constants/colors.dart'; +import 'package:enaklo_pos/core/extensions/build_context_ext.dart'; +import 'package:enaklo_pos/presentation/setting/pages/printer_page.dart'; +import 'package:enaklo_pos/presentation/setting/pages/setting_tile.dart'; +import 'package:flutter/material.dart'; + +class SettingPage extends StatefulWidget { + const SettingPage({super.key}); + + @override + State createState() => _SettingPageState(); +} + +class _SettingPageState extends State { + int currentIndex = 0; + String? role; + + void indexValue(int index) { + currentIndex = index; + setState(() {}); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.background, + body: Row( + children: [ + Expanded( + flex: 2, + child: Align( + alignment: Alignment.topCenter, + child: Material( + color: AppColors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 12.0, + ), + width: double.infinity, + height: context.deviceHeight * 0.1, + decoration: const BoxDecoration( + color: AppColors.white, + border: Border( + bottom: BorderSide( + color: AppColors.background, + width: 1.0, + ), + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Pengaturan', + style: TextStyle( + color: AppColors.black, + fontSize: 20, + fontWeight: FontWeight.w600, + ), + ), + Text( + 'Kelola pengaturan aplikasi', + style: TextStyle( + color: AppColors.grey, + fontSize: 14, + ), + ), + ], + ), + ), + Expanded( + child: SingleChildScrollView( + child: Column( + children: [ + SettingTile( + index: 0, + currentIndex: currentIndex, + title: 'Printer', + subtitle: 'Kelola printer', + icon: Icons.print_outlined, + onTap: () => indexValue(0), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + Expanded( + flex: 4, + child: Align( + alignment: AlignmentDirectional.topStart, + child: IndexedStack( + index: currentIndex, + children: [ + SettingPrinterPage(), + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/setting/widgets/receipt_printer_page.dart b/lib/presentation/setting/widgets/receipt_printer_page.dart index 8561e6e..0e1d016 100644 --- a/lib/presentation/setting/widgets/receipt_printer_page.dart +++ b/lib/presentation/setting/widgets/receipt_printer_page.dart @@ -161,62 +161,71 @@ class _ReceiptPrinterPageState extends State { SpaceHeight(16), // button test print Button.outlined( - onPressed: () async { - if (addressController!.text.isNotEmpty && printNameController!.text.isNotEmpty) { - try { - // Get actual tax and service charge settings - final settingsLocalDatasource = SettingsLocalDatasource(); - final taxModel = await settingsLocalDatasource.getTax(); - final serviceChargeValue = await settingsLocalDatasource.getServiceCharge(); - - // Create a test print model - final testPrinter = PrintModel( - code: 'receipt', - name: printNameController!.text, - address: addressController!.text, - paper: paper, - type: selectedPrinter, - ); - - // Generate test print data - final testPrintData = await PrintDataoutputs.instance.printOrderV3( - [], // Empty product list for test - 0, - 0, - 'Test', - 0, - 0, - 0, - 0, - 0, - 1, - 'Test Cashier', - 'Test Customer', - int.parse(paper), - taxPercentage: taxModel.value, - serviceChargePercentage: serviceChargeValue, - ); - - // Print test - await PrinterService().printWithPrinter( - testPrinter, - testPrintData, - context, - ); - } catch (e) { - log("Error test printing: $e"); + onPressed: () async { + if (addressController!.text.isNotEmpty && + printNameController!.text.isNotEmpty) { + try { + // Get actual tax and service charge settings + final settingsLocalDatasource = + SettingsLocalDatasource(); + final taxModel = + await settingsLocalDatasource.getTax(); + final serviceChargeValue = + await settingsLocalDatasource + .getServiceCharge(); + + // Create a test print model + final testPrinter = PrintModel( + code: 'receipt', + name: printNameController!.text, + address: addressController!.text, + paper: paper, + type: selectedPrinter, + ); + + // Generate test print data + final testPrintData = await PrintDataoutputs + .instance + .printOrderV3( + [], // Empty product list for test + 0, + 0, + 'Test', + 0, + 0, + 0, + 0, + 0, + 1, + 'Test Cashier', + 'Test Customer', + int.parse(paper), + taxPercentage: taxModel.value, + serviceChargePercentage: serviceChargeValue, + ); + + // Print test + await PrinterService().printWithPrinter( + testPrinter, + testPrintData, + context, + ); + } catch (e) { + log("Error test printing: $e"); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Error test printing: $e')), + ); + } + } else { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Error test printing: $e')), + SnackBar( + content: Text( + 'Please fill in printer details first')), ); } - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Please fill in printer details first')), - ); - } - }, - label: 'Test Print' - ), + }, + label: 'Test Print'), SpaceHeight(8), // button save data == null diff --git a/lib/presentation/setting/widgets/ticket_printer_page.dart b/lib/presentation/setting/widgets/ticket_printer_page.dart new file mode 100644 index 0000000..bf80b29 --- /dev/null +++ b/lib/presentation/setting/widgets/ticket_printer_page.dart @@ -0,0 +1,310 @@ +import 'dart:developer'; + +import 'package:enaklo_pos/core/function/app_function.dart'; +import 'package:enaklo_pos/presentation/setting/bloc/get_printer_ticket/get_printer_ticket_bloc.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:enaklo_pos/core/components/components.dart'; +import 'package:enaklo_pos/core/constants/colors.dart'; +import 'package:enaklo_pos/core/extensions/build_context_ext.dart'; +import 'package:enaklo_pos/core/utils/printer_service.dart'; +import 'package:enaklo_pos/data/dataoutputs/print_dataoutputs.dart'; +import 'package:enaklo_pos/data/models/response/print_model.dart'; +import 'package:enaklo_pos/presentation/setting/bloc/create_printer/create_printer_bloc.dart'; +import 'package:enaklo_pos/presentation/setting/bloc/update_printer/update_printer_bloc.dart'; +import 'package:enaklo_pos/presentation/setting/widgets/dialog_search_printer.dart'; + +class TicketPrinterPage extends StatefulWidget { + const TicketPrinterPage({super.key}); + + @override + State createState() => _TicketPrinterPageState(); +} + +class _TicketPrinterPageState extends State { + String selectedPrinter = 'Bluetooth'; + TextEditingController? addressController; + TextEditingController? printNameController; + String paper = '58'; + bool isInitialized = false; + @override + void initState() { + super.initState(); + addressController = TextEditingController(); + printNameController = TextEditingController(); + context.read().add(GetPrinterTicketEvent.get()); + } + + @override + void dispose() { + addressController!.dispose(); + printNameController!.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: BlocBuilder( + builder: (context, state) { + return state.maybeWhen( + orElse: () { + return SizedBox.shrink(); + }, + loading: () { + return Center(child: CircularProgressIndicator()); + }, + success: (data) { + if (data != null && !isInitialized) { + addressController!.text = data.address; + printNameController!.text = data.name; + selectedPrinter = data.type; + paper = data.paper; + isInitialized = true; + } + return Container( + // width: 300, + width: context.deviceWidth, + padding: const EdgeInsets.all(16.0), + color: Colors.white, + child: ListView( + children: [ + const Text( + 'Tiket Printer', + style: TextStyle( + color: Colors.black, + fontSize: 20, + fontWeight: FontWeight.w600, + ), + ), + SpaceHeight(16), + + DropdownButtonFormField( + decoration: InputDecoration( + labelText: 'Printer Type', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + ), + ), + value: selectedPrinter, // nilai default + items: const [ + DropdownMenuItem( + value: 'Bluetooth', + child: Text('Bluetooth'), + ), + DropdownMenuItem( + value: 'Network', + child: Text('Network'), + ), + ], + onChanged: (value) { + selectedPrinter = value ?? 'Bluetooth'; + setState(() {}); + }, + ), + SpaceHeight(8), + //button search + selectedPrinter == 'Bluetooth' + ? Button.outlined( + onPressed: () { + showDialog( + context: context, + builder: (context) => DialogSearchPrinter( + onSelected: (value) { + addressController!.text = value; + setState(() {}); + }, + ), + ); + }, + label: 'Search') + : CustomTextField( + controller: addressController!, + label: 'Address', + showLabel: false, + ), + SpaceHeight(16), + // Textfield for name + CustomTextField( + controller: printNameController!, + label: 'Print Name', + showLabel: false, + ), + SpaceHeight(16), + // Textfield for width paper + DropdownButtonFormField( + decoration: InputDecoration( + labelText: 'Width Paper', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + ), + ), + value: paper, // nilai default + items: const [ + DropdownMenuItem( + value: '58', + child: Text('58 mm'), + ), + DropdownMenuItem( + value: '80', + child: Text('80 mm'), + ), + ], + onChanged: (value) { + paper = value ?? '58'; + log('Paper : $paper'); + }, + ), + + SpaceHeight(16), + // button test print + Button.outlined( + onPressed: () async { + if (addressController!.text.isNotEmpty && + printNameController!.text.isNotEmpty) { + try { + // Create a test print model + final testPrinter = PrintModel( + code: 'ticket', + name: printNameController!.text, + address: addressController!.text, + paper: paper, + type: selectedPrinter, + ); + final barcode = + await generateBarcodeAsUint8List('123'); + // Generate test print data + final testPrintData = + await PrintDataoutputs.instance.printTicket( + 10000, + barcode, + int.parse(paper), + ); + + // Print test + await PrinterService().printWithPrinter( + testPrinter, + testPrintData, + context, + ); + } catch (e) { + log("Error test printing: $e"); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Error test printing: $e')), + ); + } + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'Please fill in printer details first')), + ); + } + }, + label: 'Test Print'), + SpaceHeight(8), + // button save + data == null + ? BlocConsumer( + listener: (context, state) { + state.maybeWhen( + orElse: () {}, + success: (message) { + final snackBar = SnackBar( + content: Text(message), + backgroundColor: AppColors.primary, + ); + ScaffoldMessenger.of(context) + .showSnackBar(snackBar); + context + .read() + .add(GetPrinterTicketEvent.get()); + }, + ); + }, + builder: (context, state) { + return state.maybeWhen( + orElse: () { + return Button.filled( + onPressed: () { + final printData = PrintModel( + code: 'ticket', + name: printNameController!.text, + address: addressController!.text, + paper: paper, + type: selectedPrinter, + ); + + context.read().add( + CreatePrinterEvent.createPrinter( + printData), + ); + }, + label: 'Save', + ); + }, + loading: () { + return CircularProgressIndicator(); + }, + ); + }, + ) + : BlocConsumer( + listener: (context, state) { + state.maybeWhen( + orElse: () {}, + success: (message) { + final snackBar = SnackBar( + content: Text(message), + backgroundColor: AppColors.primary, + ); + ScaffoldMessenger.of(context) + .showSnackBar(snackBar); + context + .read() + .add(GetPrinterTicketEvent.get()); + }, + ); + }, + builder: (context, state) { + return state.maybeWhen( + orElse: () { + return Button.filled( + onPressed: () { + final printData = PrintModel( + id: data.id, + code: 'ticket', + name: printNameController!.text, + address: addressController!.text, + paper: paper, + type: selectedPrinter, + ); + log("Print Data : ${printData.toMap()}"); + + context.read().add( + UpdatePrinterEvent.updatePrinter( + printData, + ), + ); + }, + label: 'Save', + ); + }, + loading: () { + return CircularProgressIndicator(); + }, + ); + }, + ), + SpaceHeight(16), + ], + ), + ); + }, + ); + }, + )); + } +} diff --git a/pubspec.lock b/pubspec.lock index aaf63b6..3efa4dc 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -63,13 +63,21 @@ packages: source: hosted version: "1.3.0" barcode: - dependency: transitive + dependency: "direct main" description: name: barcode - sha256: ab180ce22c6555d77d45f0178a523669db67f95856e3378259ef2ffeb43e6003 + sha256: "7b6729c37e3b7f34233e2318d866e8c48ddb46c1f7ad01ff7bb2a8de1da2b9f4" url: "https://pub.dev" source: hosted - version: "2.2.8" + version: "2.2.9" + barcode_image: + dependency: "direct main" + description: + name: barcode_image + sha256: b93f6ec7fa3d29373cff404ea99fb2afe895d2b4f36704e563f861e7a0818483 + url: "https://pub.dev" + source: hosted + version: "2.0.3" bidi: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d8874e6..4f4e6cc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -64,6 +64,8 @@ dependencies: another_flushbar: ^1.12.30 dropdown_search: ^5.0.6 fl_chart: ^1.0.0 + barcode: ^2.2.9 + barcode_image: ^2.0.3 # imin_printer: ^0.6.10 dev_dependencies: