multiple asset picker added.

design_3.0_task_module_new
Sikander Saleem 7 months ago
parent 15e62bb004
commit b75c0b321d

@ -81,18 +81,20 @@ extension WidgetExtensions on Widget {
).toShadowContainer(context)
: this;
Widget toShadowContainer(BuildContext context, {bool showShadow = true,double borderRadius = 14, bool withShadow = true, Color? backgroundColor, double padding = 16, EdgeInsets? paddingObject}) => withShadow
? Container(
padding: paddingObject ?? EdgeInsets.all(padding),
width: double.infinity,
decoration: ShapeDecoration(
color: backgroundColor ?? AppColor.background(context),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(borderRadius)),
shadows: showShadow ? [boxShadowR14] : null,
),
child: this,
)
: this;
Widget toShadowContainer(BuildContext context,
{bool showShadow = true, double borderRadius = 14, bool withShadow = true, Color? backgroundColor, Color borderColor = Colors.transparent, double padding = 16, EdgeInsets? paddingObject}) =>
withShadow
? Container(
padding: paddingObject ?? EdgeInsets.all(padding),
width: double.infinity,
decoration: ShapeDecoration(
color: backgroundColor ?? AppColor.background(context),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(borderRadius), side: BorderSide(color: borderColor)),
shadows: showShadow ? [boxShadowR14] : null,
),
child: this,
)
: this;
Widget bottomSheetContainer(BuildContext context, {EdgeInsets? padding}) => Container(
clipBehavior: Clip.antiAlias,

@ -5,6 +5,7 @@ import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart';
import '../../../models/device/asset.dart';
@ -20,8 +21,9 @@ import '../../widgets/loaders/no_data_found.dart';
class SearchAssetPage extends StatefulWidget {
/// add on route
static const String id = "asset_search_page";
final bool multiSelection;
const SearchAssetPage({Key? key}) : super(key: key);
const SearchAssetPage({Key? key, this.multiSelection = false}) : super(key: key);
@override
State<SearchAssetPage> createState() => _SearchAssetPageState();
@ -36,6 +38,8 @@ class _SearchAssetPageState extends State<SearchAssetPage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
bool _isFirst = true;
List<num> selectedAssets = [];
@override
void initState() {
_searchController = TextEditingController();
@ -135,20 +139,67 @@ class _SearchAssetPageState extends State<SearchAssetPage> {
});
}
},
child: ListView.separated(
itemCount: _searchableList.length,
separatorBuilder: (listContext, itemIndex) => 8.height,
padding: const EdgeInsets.all(16),
itemBuilder: (listContext, itemIndex) {
return AssetItemListView(
device: _searchableList[itemIndex],
onPressed: (device) {
Navigator.of(context).pop();
Navigator.of(context).pop(device);
child: Column(
children: [
ListView.separated(
itemCount: _searchableList.length,
separatorBuilder: (listContext, itemIndex) => 8.height,
padding: const EdgeInsets.all(16),
itemBuilder: (listContext, itemIndex) {
bool isSelected = selectedAssets.contains(_searchableList[itemIndex].id!);
String title = isSelected ? "UnSelect" : "Select";
return AssetItemListView(
device: _searchableList[itemIndex],
isSelected: isSelected,
onPressed: (device) {
if (widget.multiSelection) {
if (selectedAssets.contains(device.id!)) {
selectedAssets.remove(device.id!);
} else {
selectedAssets.add(device.id!);
}
setState(() {});
} else {
Navigator.of(context).pop();
Navigator.of(context).pop(device);
}
},
selectButton: Text(title, style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context))),
);
},
selectButton: Text(context.translation.select, style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context))),
);
},
).expanded,
if (widget.multiSelection && selectedAssets.isNotEmpty)
Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: AppFilledButton(
label: "Clear",
buttonColor: AppColor.white60,
textColor: AppColor.black10,
loading: false,
onPressed: () async {
selectedAssets.clear();
setState(() {});
},
),
),
12.width,
Expanded(
child: AppFilledButton(
label: context.translation.select,
buttonColor: AppColor.primary10,
loading: false,
onPressed: () async {
Navigator.of(context).pop();
List<Asset> assets = _searchableList.where((asset) => selectedAssets.contains(asset.id)).toList();
Navigator.of(context).pop(assets);
},
),
),
],
).toShadowContainer(context),
],
),
),
)

@ -1,16 +1,13 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/service_requests_provider.dart';
import 'package:test_sa/dashboard_latest/dashboard_provider.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/device/asset.dart';
import 'package:test_sa/models/enums/user_types.dart';
import 'package:test_sa/models/helper_data_models/workorder/work_order_helper_models.dart';
import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/models/service_request/pending_service_request_model.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
@ -19,12 +16,10 @@ import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart';
import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart';
import 'package:test_sa/providers/service_request_providers/last_situation_provider.dart';
import 'package:test_sa/service_request_latest/views/components/action_button/footer_action_button.dart';
import 'package:test_sa/views/pages/user/requests/pending_requests_screen.dart';
import 'package:test_sa/views/widgets/bottom_sheets/pending_request_bottom_sheet.dart';
import 'package:test_sa/views/widgets/equipment/asset_picker.dart';
import 'package:test_sa/views/widgets/images/multi_image_picker.dart';
import '../../../../../../new_views/common_widgets/default_app_bar.dart';
import '../../../../../../new_views/common_widgets/default_app_bar.dart';
class CreateTaskView extends StatefulWidget {
static const String id = "/create-task";
@ -41,7 +36,8 @@ class _CreateTaskViewState extends State<CreateTaskView> with TickerProviderStat
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
//TODO need to replace with model attribute
Asset? _device;
// Asset? _device;
List<Asset> _deviceList = [];
PendingAssetServiceRequest? pendingAssetServiceRequest;
bool _isLocalUrl(String url) {
@ -65,21 +61,6 @@ class _CreateTaskViewState extends State<CreateTaskView> with TickerProviderStat
crossAxisAlignment: CrossAxisAlignment.start,
children: [
scanAssetButton(),
if (pendingAssetServiceRequest != null && pendingAssetServiceRequest!.details!.isNotEmpty) ...[
8.height,
Row(
children: [
const Icon(Icons.warning, color: Color(0xffEE404C), size: 14),
8.width,
Text(
"This asset already have ${pendingAssetServiceRequest!.details!.length} request pending",
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500, color: Color(0xff7D859A), decoration: TextDecoration.underline),
).expanded,
],
).onPress(() {
showPendingRequests();
}),
],
16.height,
//TODO replace with provided lookup..
SingleItemDropDownMenu<Lookup, LastSituationProvider>(
@ -138,45 +119,26 @@ class _CreateTaskViewState extends State<CreateTaskView> with TickerProviderStat
Widget scanAssetButton() {
return AssetPicker(
device: _device,
deviceList: _deviceList,
showLoading: checkPendingRequest,
borderColor: AppColor.black20,
buttonColor: AppColor.white936,
onPick: (asset) async {
pendingAssetServiceRequest = null;
_device = asset;
// _serviceRequest.device = asset;
await checkAssetForPendingServiceRequest(asset.id!.toInt());
if (pendingAssetServiceRequest != null && pendingAssetServiceRequest!.details!.isNotEmpty) {
showPendingRequestBottomSheet();
}
multiSelection: true,
onAssetRemove: (itemId) {
_deviceList.removeWhere((asset) => asset.id?.toInt() == itemId);
setState(() {});
},
onMultiAssetPick: (assetList) {
_deviceList.addAll(assetList);
final seenIds = <num?>{};
_deviceList = _deviceList.where((item) => seenIds.add(item.id)).toList();
setState(() {});
},
);
}
bool checkPendingRequest = false;
void showPendingRequests() {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => PendingServiceRequestScreen(pendingAssetServiceRequest!)));
}
void showPendingRequestBottomSheet() async {
bool view = (await showModalBottomSheet(
context: context,
isDismissible: false,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
clipBehavior: Clip.antiAliasWithSaveLayer,
builder: (BuildContext context) => PendingRequestBottomSheet(pendingAssetServiceRequest!, _device ?? Asset()),
)) as bool;
if (view) {
showPendingRequests();
}
}
Future<void> checkAssetForPendingServiceRequest(int assetId) async {
checkPendingRequest = true;
setState(() {});
@ -192,7 +154,6 @@ class _CreateTaskViewState extends State<CreateTaskView> with TickerProviderStat
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
// List<WorkOrderAttachments> attachement = [];
// for (var item in _deviceImages) {
// attachement.add(WorkOrderAttachments(id: 0, name: "${item.path.split("/").last}|${base64Encode(item.readAsBytesSync())}"));

@ -11,8 +11,9 @@ class AssetItemListView extends StatelessWidget {
final Asset device;
final Function(Asset) onPressed;
final Widget? selectButton;
final bool isSelected;
const AssetItemListView({Key? key,required this.device,required this.onPressed, this.selectButton}) : super(key: key);
const AssetItemListView({Key? key, required this.device, required this.onPressed, this.selectButton, this.isSelected = false}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -50,26 +51,27 @@ class AssetItemListView extends StatelessWidget {
children: [
"${context.translation.serialNo} : ${device.assetSerialNo}".bodyText(context).expanded,
4.width,
selectButton ?? Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
context.translation.viewDetails,
style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)),
selectButton ??
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
context.translation.viewDetails,
style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)),
),
4.width,
Icon(
Icons.arrow_forward,
color: AppColor.blueStatus(context),
size: 14,
)
],
),
4.width,
Icon(
Icons.arrow_forward,
color: AppColor.blueStatus(context),
size: 14,
)
],
),
],
)
],
).expanded
],
).toShadowContainer(context,padding: 12).onPress(() => onPressed(device));
).toShadowContainer(context, padding: 12, borderColor: isSelected ? AppColor.blueStatus(context) : Colors.transparent).onPress(() => onPressed(device));
}
}

@ -11,22 +11,44 @@ import '../../../new_views/app_style/app_color.dart';
class AssetPicker extends StatelessWidget {
final Function(Asset)? onPick;
final Function(List<Asset>)? onMultiAssetPick;
final Function(int)? onAssetRemove;
final Asset? device;
final List<Asset> deviceList;
final bool editable;
final bool showAssetInfo;
final Color? borderColor;
final Color? buttonColor;
final bool forPPM;
final bool showLoading;
final bool multiSelection;
const AssetPicker({Key? key, this.editable = true, this.device, this.onPick, this.borderColor, this.buttonColor, this.showAssetInfo = true, this.forPPM = false, this.showLoading = false})
: super(key: key);
const AssetPicker(
{Key? key,
this.editable = true,
this.device,
this.deviceList = const [],
this.onPick,
this.onMultiAssetPick,
this.onAssetRemove,
this.borderColor,
this.buttonColor,
this.showAssetInfo = true,
this.multiSelection = false,
this.forPPM = false,
this.showLoading = false})
: assert(
multiSelection == false || onMultiAssetPick != null,
'Cannot use multiple asset picker with single onPick Method\n'
'Use onMultiAssetPick method instead.',
),
super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: [
if (device == null)
if (device == null && deviceList.isEmpty)
Container(
height: 50,
alignment: Alignment.center,
@ -45,12 +67,24 @@ class AssetPicker extends StatelessWidget {
],
),
).onPress(() async {
Asset? device = await Navigator.of(context).push(MaterialPageRoute(
builder: (context) => AssetScanQr(
title: context.translation.assetScan,
))) as Asset?;
if (device != null) {
onPick!(device);
if (multiSelection) {
List<Asset>? device = await Navigator.of(context).push(MaterialPageRoute(
builder: (context) => AssetScanQr(
title: context.translation.assetScan,
multiSelection: multiSelection,
))) as List<Asset>?;
if (device?.isNotEmpty ?? false) {
onMultiAssetPick!(device!);
}
} else {
Asset? device = await Navigator.of(context).push(MaterialPageRoute(
builder: (context) => AssetScanQr(
title: context.translation.assetScan,
multiSelection: multiSelection,
))) as Asset?;
if (device != null) {
onPick!(device);
}
}
})
else
@ -71,71 +105,104 @@ class AssetPicker extends StatelessWidget {
],
),
).onPress(() async {
Asset? device = await Navigator.of(context).push(MaterialPageRoute(
builder: (context) => AssetScanQr(
title: context.translation.assetScan,
))) as Asset?;
if (device != null) {
onPick!(device);
if (multiSelection) {
List<Asset>? device = await Navigator.of(context).push(MaterialPageRoute(
builder: (context) => AssetScanQr(
title: context.translation.assetScan,
multiSelection: multiSelection,
))) as List<Asset>?;
if (device?.isNotEmpty ?? false) {
onMultiAssetPick!(device!);
}
} else {
Asset? device = await Navigator.of(context).push(MaterialPageRoute(
builder: (context) => AssetScanQr(
title: context.translation.assetScan,
multiSelection: multiSelection,
))) as Asset?;
if (device != null) {
onPick!(device);
}
}
}),
if (device != null && showAssetInfo)
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: showLoading ? Colors.white : const Color(0xffF4F6FC),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: const Color(0xff212936).withOpacity(.03),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Text(
device?.modelDefinition?.assetName?.cleanupWhitespace.capitalizeFirstOfEach ?? "",
style: TextStyle(
fontSize: 14.toScreenWidth,
fontWeight: FontWeight.w500,
fontStyle: FontStyle.normal,
color: Colors.black87,
decoration: TextDecoration.none,
),
).toShimmer(isShow: showLoading).expanded,
const Icon(
Icons.info,
color: Color(0xff7D859A),
size: 20,
),
],
),
8.height,
"${context.translation.assetNo}: ${device!.assetNumber}".bodyText2(context).toShimmer(isShow: showLoading),
2.height,
// "${context.translation.manufacture}: ${device.modelDefinition?.manufacturerName}".bodyText(context),
"${context.translation.model}: ${device!.modelDefinition?.modelName}".bodyText2(context).toShimmer(isShow: showLoading),
// "${context.translation.serialNumber}: ${device.assetNumber}".bodyText(context),
// const Divider().defaultStyle(context),
// "${context.translation.department}: ${device.department?.departmentName}".bodyText(context),
// "${context.translation.site}: ${device.site?.custName}".bodyText(context),
],
),
).onPress(() {
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
clipBehavior: Clip.antiAliasWithSaveLayer,
builder: (BuildContext context) => AssetDetailBottomSheet(device),
);
}).paddingOnly(top: 16),
if (device != null && showAssetInfo) _assetInfoView(device!, context).paddingOnly(top: 16),
if (deviceList.isNotEmpty && showAssetInfo)
ListView.separated(
shrinkWrap: true,
padding: EdgeInsets.only(top: 16),
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (cxt, index) => _assetInfoView(deviceList[index], context),
separatorBuilder: (cxt, index) => 12.height,
itemCount: deviceList.length)
],
);
}
Widget _assetInfoView(Asset device, BuildContext context) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: showLoading ? Colors.white : const Color(0xffF4F6FC),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: const Color(0xff212936).withOpacity(.03),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Text(
device.modelDefinition?.assetName?.cleanupWhitespace.capitalizeFirstOfEach ?? "",
style: TextStyle(
fontSize: 14.toScreenWidth,
fontWeight: FontWeight.w500,
fontStyle: FontStyle.normal,
color: Colors.black87,
decoration: TextDecoration.none,
),
).toShimmer(isShow: showLoading).expanded,
const Icon(
Icons.info,
color: Color(0xff7D859A),
size: 20,
).onPress(() {
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
clipBehavior: Clip.antiAliasWithSaveLayer,
builder: (BuildContext context) => AssetDetailBottomSheet(device),
);
}),
if (multiSelection) ...[
4.width,
const Icon(
Icons.delete_rounded,
color: Color(0xffF63939),
size: 20,
).onPress(() {
onAssetRemove!(device.id!.toInt());
}),
]
],
),
8.height,
"${context.translation.assetNo}: ${device!.assetNumber}".bodyText2(context).toShimmer(isShow: showLoading),
2.height,
// "${context.translation.manufacture}: ${device.modelDefinition?.manufacturerName}".bodyText(context),
"${context.translation.model}: ${device!.modelDefinition?.modelName}".bodyText2(context).toShimmer(isShow: showLoading),
// "${context.translation.serialNumber}: ${device.assetNumber}".bodyText(context),
// const Divider().defaultStyle(context),
// "${context.translation.department}: ${device.department?.departmentName}".bodyText(context),
// "${context.translation.site}: ${device.site?.custName}".bodyText(context),
],
),
);
}
}

@ -6,6 +6,7 @@ import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/models/device/asset.dart';
import '../../../controllers/providers/api/devices_provider.dart';
import '../../../models/device/asset_search.dart';
@ -16,8 +17,9 @@ import '../../pages/device_transfer/search_asset_page.dart';
class AssetScanQr extends StatefulWidget {
static const String id = "/asset-scan-qr";
const AssetScanQr({Key? key, required this.title}) : super(key: key);
const AssetScanQr({Key? key, required this.title, this.multiSelection = false}) : super(key: key);
final String title;
final bool multiSelection;
@override
_AssetScanQrState createState() => _AssetScanQrState();
@ -51,7 +53,7 @@ class _AssetScanQrState extends State<AssetScanQr> {
_pickManually() async {
_controller?.pauseCamera();
await Navigator.push(context, MaterialPageRoute(builder: (context) => const SearchAssetPage())).then((value) => _controller?.resumeCamera());
await Navigator.push(context, MaterialPageRoute(builder: (context) => SearchAssetPage(multiSelection: widget.multiSelection))).then((value) => _controller?.resumeCamera());
}
_getDevice(String result, {bool isQr = false}) async {
@ -81,7 +83,11 @@ class _AssetScanQrState extends State<AssetScanQr> {
_scanDone = true;
final result = await _getDevice(scanData.code!, isQr: true);
if (result.isNotEmpty) {
Navigator.of(context).pop(result[0]);
if (widget.multiSelection) {
Navigator.of(context).pop(<Asset>[result[0]]);
} else {
Navigator.of(context).pop(result[0]);
}
} else {
_scanDone = false;
}

Loading…
Cancel
Save