feat: customer auto complete

This commit is contained in:
efrilm 2025-08-04 14:08:13 +07:00
parent c7f473eb5f
commit a63328d953
2 changed files with 130 additions and 21 deletions

View File

@ -4,11 +4,13 @@ import 'dart:developer';
import 'package:enaklo_pos/core/components/dashed_divider.dart'; import 'package:enaklo_pos/core/components/dashed_divider.dart';
import 'package:enaklo_pos/core/components/flushbar.dart'; import 'package:enaklo_pos/core/components/flushbar.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart'; import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/data/models/response/customer_response_model.dart';
import 'package:enaklo_pos/presentation/home/bloc/order_form/order_form_bloc.dart'; import 'package:enaklo_pos/presentation/home/bloc/order_form/order_form_bloc.dart';
import 'package:enaklo_pos/presentation/home/dialog/save_dialog.dart'; import 'package:enaklo_pos/presentation/home/dialog/save_dialog.dart';
import 'package:enaklo_pos/presentation/home/models/order_type.dart'; import 'package:enaklo_pos/presentation/home/models/order_type.dart';
import 'package:enaklo_pos/presentation/home/models/product_quantity.dart'; import 'package:enaklo_pos/presentation/home/models/product_quantity.dart';
import 'package:enaklo_pos/presentation/home/widgets/confirm_payment_title.dart'; import 'package:enaklo_pos/presentation/home/widgets/confirm_payment_title.dart';
import 'package:enaklo_pos/presentation/home/widgets/customer_auto_complete_field.dart';
import 'package:enaklo_pos/presentation/success/pages/success_order_page.dart'; import 'package:enaklo_pos/presentation/success/pages/success_order_page.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@ -51,6 +53,7 @@ class _ConfirmPaymentPageState extends State<ConfirmPaymentPage> {
int uangPas = 0; int uangPas = 0;
int uangPas2 = 0; int uangPas2 = 0;
int uangPas3 = 0; int uangPas3 = 0;
Customer? selectedCustomer;
// int discountAmountValue = 0; // int discountAmountValue = 0;
int totalPriceFinal = 0; int totalPriceFinal = 0;
@ -541,27 +544,14 @@ class _ConfirmPaymentPageState extends State<ConfirmPaymentPage> {
), ),
), ),
), ),
child: Column( child: CustomerAutocomplete(
crossAxisAlignment: CrossAxisAlignment.start, controller: customerController,
children: [ selectedCustomer: selectedCustomer,
const Text( onSelected: (customer) {
'Pelanggan', setState(() {
style: TextStyle( selectedCustomer = customer;
color: AppColors.black, });
fontSize: 16, },
fontWeight: FontWeight.w600,
),
),
const SpaceHeight(6.0),
TextFormField(
controller: customerController,
decoration: InputDecoration(
hintText: 'Nama Pelanggan',
),
textCapitalization:
TextCapitalization.words,
),
],
), ),
), ),
Container( Container(

View File

@ -0,0 +1,119 @@
import 'package:enaklo_pos/core/components/spaces.dart';
import 'package:enaklo_pos/core/constants/colors.dart';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/data/models/response/customer_response_model.dart';
import 'package:enaklo_pos/presentation/customer/bloc/customer_loader/customer_loader_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class CustomerAutocomplete extends StatefulWidget {
final TextEditingController controller;
final Customer? selectedCustomer;
final ValueChanged<Customer?>? onSelected;
const CustomerAutocomplete({
super.key,
required this.controller,
this.selectedCustomer,
this.onSelected,
});
@override
State<CustomerAutocomplete> createState() => _CustomerAutocompleteState();
}
class _CustomerAutocompleteState extends State<CustomerAutocomplete> {
final FocusNode _focusNode = FocusNode();
@override
void initState() {
super.initState();
context.read<CustomerLoaderBloc>().add(CustomerLoaderEvent.getCustomer());
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Pelanggan',
style: TextStyle(
color: AppColors.black,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
const SpaceHeight(6.0),
BlocBuilder<CustomerLoaderBloc, CustomerLoaderState>(
builder: (context, state) {
return state.maybeWhen(
orElse: () => const SizedBox.shrink(),
loading: () => const Center(child: CircularProgressIndicator()),
loaded: (customers, totalCustomer) => RawAutocomplete<Customer>(
textEditingController: widget.controller,
focusNode: _focusNode,
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text.isEmpty) {
return const Iterable<Customer>.empty();
}
return customers.where((Customer customer) {
return customer.name?.toLowerCase().contains(
textEditingValue.text.toLowerCase(),
) ??
false;
});
},
displayStringForOption: (Customer option) => option.name ?? "",
onSelected: (Customer selection) {
widget.controller.text = selection.name ?? "";
if (widget.onSelected != null) {
widget.onSelected!(selection);
}
},
fieldViewBuilder:
(context, controller, focusNode, onFieldSubmitted) {
return TextFormField(
controller: controller,
focusNode: focusNode,
decoration: const InputDecoration(
hintText: 'Nama Pelanggan',
),
onChanged: (value) {
if (widget.onSelected != null) {
widget.onSelected!(null); // reset jika ketik manual
}
},
);
},
optionsViewBuilder: (context, onSelected, options) {
return Material(
elevation: 1,
borderRadius: BorderRadius.circular(8),
color: Colors.white,
child: SizedBox(
height: context.deviceHeight * 0.4,
child: ListView.separated(
padding: EdgeInsets.zero,
itemCount: options.length,
separatorBuilder: (_, __) => const Divider(height: 1),
itemBuilder: (BuildContext context, int index) {
final Customer option = options.elementAt(index);
return ListTile(
title: Text(option.name ?? ""),
onTap: () {
onSelected(option);
},
);
},
),
),
);
},
),
);
},
),
],
);
}
}