You've already forked flutter-rp-example
First commit
This commit is contained in:
334
lib/widgets/scan_flow/delivery_confirmed.dart
Normal file
334
lib/widgets/scan_flow/delivery_confirmed.dart
Normal file
@@ -0,0 +1,334 @@
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'package:dart_core_sdk/gcs-api.pbgrpc.dart';
|
||||
import 'package:dart_core_sdk/handlingunit.pb.dart';
|
||||
import 'package:dart_core_sdk/trackingInput.pbgrpc.dart';
|
||||
import 'package:dart_core_sdk/transportShared.pb.dart';
|
||||
import 'package:dart_core_sdk/transportShared.pbenum.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sampleapp/globals.dart';
|
||||
import 'package:sampleapp/services/gcs.dart';
|
||||
import 'package:sampleapp/widgets/components/reflex_alert.dart';
|
||||
import 'package:sampleapp/widgets/components/reflex_button.dart';
|
||||
import 'package:sampleapp/widgets/components/reflex_circular_progress.dart';
|
||||
import 'package:sampleapp/widgets/components/reflex_dropdown_button_form_field.dart';
|
||||
import 'package:sampleapp/widgets/components/reflex_signature.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:syncfusion_flutter_signaturepad/signaturepad.dart';
|
||||
import '../../locator.dart';
|
||||
import '../components/reflex_hu_info.dart';
|
||||
import 'dart:ui';
|
||||
import 'package:badges/badges.dart' as bd;
|
||||
import 'package:dart_core_sdk/shared.pb.dart' as rp;
|
||||
|
||||
class DeliveryConfirmed extends StatefulWidget {
|
||||
final bool isDeliveryValid;
|
||||
final Handlingunit handlingUnit;
|
||||
final String projectID;
|
||||
final void Function() onNextDelivery;
|
||||
|
||||
const DeliveryConfirmed(
|
||||
{super.key,
|
||||
required this.isDeliveryValid,
|
||||
required this.handlingUnit,
|
||||
required this.projectID,
|
||||
required this.onNextDelivery});
|
||||
|
||||
@override
|
||||
_DeliveryConfirmedState createState() => _DeliveryConfirmedState();
|
||||
}
|
||||
|
||||
class _DeliveryConfirmedState extends State<DeliveryConfirmed> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
final GlobalKey<SfSignaturePadState> signaturePadKey = GlobalKey();
|
||||
|
||||
var _selectedAnomalyReason = "";
|
||||
|
||||
final ImagePicker _picker = ImagePicker();
|
||||
final List<File> _imageFileList = [];
|
||||
|
||||
Future<bool> _callTrackingServiceNotified(
|
||||
TrackingEventCode code, String reason) async {
|
||||
TrackingEvent event = TrackingEvent(
|
||||
code: code,
|
||||
date: rp.DateTime(dateTime: DateTime.now().toUtc().toIso8601String(), authorTimeZone: "UTC"),
|
||||
reason: reason);
|
||||
bool notified = false;
|
||||
await locator
|
||||
.get<TrackingInputAPIClient>()
|
||||
.notified(
|
||||
TrackingNotifiedRequest(
|
||||
header: rp.RequestProjectHeader(
|
||||
projectID: widget.projectID,
|
||||
),
|
||||
iD: rp.EntityID(
|
||||
refID: widget.handlingUnit.iD.refID,
|
||||
),
|
||||
payload: TrackingNotifiedPayload(
|
||||
events: [event],
|
||||
)
|
||||
)
|
||||
).then((value) => notified = true);
|
||||
return notified;
|
||||
}
|
||||
|
||||
String _buildFileName(String fileName) {
|
||||
return "${fileName}_${widget.handlingUnit.iD.refID}_${DateTime.now().toUtc().toIso8601String().split('.')[0].replaceAll("-", "_").replaceAll(":", "_")}.png";
|
||||
}
|
||||
|
||||
void _takePhoto() async {
|
||||
XFile? pickedFile = await _picker.pickImage(
|
||||
source: ImageSource.camera, maxHeight: 720, maxWidth: 1280);
|
||||
if (pickedFile == null) return;
|
||||
setState(() => _imageFileList.add(File(pickedFile.path)));
|
||||
}
|
||||
|
||||
Future<void> _uploadPhotos(GetBucketSTSResult stsinfo, String resourceName) async {
|
||||
GcsService gcsService = locator.get<GcsService>();
|
||||
|
||||
for (var i = 0; i < _imageFileList.length; i++) {
|
||||
final String fileName = _buildFileName("photo_$i");
|
||||
await gcsService
|
||||
.uploadImage(
|
||||
stsinfo.accessToken,
|
||||
stsinfo.bucketName,
|
||||
resourceName,
|
||||
"${widget.handlingUnit.iD.refID}/photos/$fileName",
|
||||
_imageFileList[i])
|
||||
.catchError((e) => throw e);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handlePostSuccesfulDelivery() async {
|
||||
// Call the TrackingService to notify the delivery
|
||||
var notified = await _callTrackingServiceNotified(
|
||||
TrackingEventCode.TRACKING_EVENT_IFTSTA_21, "");
|
||||
if (!notified) {
|
||||
throw Exception(AppLocalizations.of(context)!.cannotNotifyDelivery);
|
||||
}
|
||||
|
||||
// Upload Signature
|
||||
// First get a token STS
|
||||
GetBucketSTSResult? stsinfo;
|
||||
|
||||
GcsApiClient gcsApiService = locator.get<GcsApiClient>();
|
||||
|
||||
await gcsApiService.getBucketSTS(GetBucketSTSRequest(projectID: widget.projectID)).then((v) => {
|
||||
stsinfo = v,
|
||||
});
|
||||
// Look for the project id in the resourcelist
|
||||
final String resourceName = stsinfo!.resources.firstWhere(
|
||||
(element) => element.endsWith("${widget.projectID}/"),
|
||||
orElse: () => "");
|
||||
if (resourceName == "") {
|
||||
throw Exception(
|
||||
AppLocalizations.of(context)!.missingSendingFilesRights);
|
||||
}
|
||||
|
||||
// Turn the signature into a png file
|
||||
final String fileName = _buildFileName("signature");
|
||||
final image = await signaturePadKey.currentState!.toImage(pixelRatio: 3.0);
|
||||
final signatureByteData =
|
||||
await image.toByteData(format: ImageByteFormat.png);
|
||||
final Uint8List signatureBytes = signatureByteData!.buffer.asUint8List(
|
||||
signatureByteData.offsetInBytes, signatureByteData.lengthInBytes);
|
||||
|
||||
final String path = (await getTemporaryDirectory()).path;
|
||||
final String tmpfileName = "$path/signature.png";
|
||||
final File signatureFile = File(tmpfileName);
|
||||
await signatureFile.writeAsBytes(signatureBytes);
|
||||
|
||||
// Upload the signature to GCS
|
||||
GcsService gcsService = locator.get<GcsService>();
|
||||
await gcsService
|
||||
.uploadImage(
|
||||
stsinfo!.accessToken,
|
||||
stsinfo!.bucketName,
|
||||
resourceName,
|
||||
"${widget.handlingUnit.iD.refID}/signatures/$fileName",
|
||||
signatureFile)
|
||||
.catchError((e) => throw e);
|
||||
|
||||
// And the photos
|
||||
await _uploadPhotos(stsinfo!, resourceName);
|
||||
}
|
||||
|
||||
Future<bool> _handlePostAnomalyDelivery() async {
|
||||
GetBucketSTSResult? stsinfo;
|
||||
|
||||
GcsApiClient gcsApiClient = locator.get<GcsApiClient>();
|
||||
|
||||
await gcsApiClient.getBucketSTS(GetBucketSTSRequest(projectID: widget.projectID)).then((v) => {
|
||||
stsinfo = v,
|
||||
});
|
||||
// Look for the project id in the resourcelist
|
||||
final String resourceName = stsinfo!.resources.firstWhere(
|
||||
(element) => element.endsWith("${widget.projectID}/"),
|
||||
orElse: () => "");
|
||||
if (resourceName == "") {
|
||||
throw Exception(
|
||||
AppLocalizations.of(context)!.missingSendingFilesRights);
|
||||
}
|
||||
|
||||
await _uploadPhotos(stsinfo!, resourceName);
|
||||
|
||||
var notified = await _callTrackingServiceNotified(
|
||||
TrackingEventCode.TRACKING_EVENT_IFTSTA_56, _selectedAnomalyReason);
|
||||
if (!notified) {
|
||||
throw Exception(AppLocalizations.of(context)!.cannotNotifyDelivery);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void _handleOnNextDelivery() async {
|
||||
try {
|
||||
showDialog(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
const Center(child: ReflexCircularProgress()));
|
||||
if (widget.isDeliveryValid) {
|
||||
await _handlePostSuccesfulDelivery();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.deliveryOk),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
await _handlePostAnomalyDelivery();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.deliveryWithAnomaly),
|
||||
),
|
||||
);
|
||||
}
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
widget.onNextDelivery();
|
||||
} catch (e) {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(AppLocalizations.of(context)!.error),
|
||||
content: Text(e.toString()),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(AppLocalizations.of(context)!.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () =>
|
||||
{Navigator.of(context).pop(), widget.onNextDelivery()},
|
||||
child: Text(AppLocalizations.of(context)!.nextDelivery),
|
||||
),
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildSignatureBox() {
|
||||
return ReflexSignature(signaturePadKey: signaturePadKey);
|
||||
}
|
||||
|
||||
Widget _buildAnomalyInput() {
|
||||
final List<String> _anomalyReasons = [
|
||||
AppLocalizations.of(context)!.anomalyBadHandlingUnit,
|
||||
AppLocalizations.of(context)!.anomalyCustomerRefused,
|
||||
AppLocalizations.of(context)!.anomalyCustomerUnavailable,
|
||||
AppLocalizations.of(context)!.anomalyOther
|
||||
];
|
||||
|
||||
_selectedAnomalyReason = _anomalyReasons[0];
|
||||
|
||||
return ReflexDropdownButtonFormField(
|
||||
label: AppLocalizations.of(context)!.anomalyReason,
|
||||
value: _anomalyReasons[0],
|
||||
items: _anomalyReasons.map<DropdownMenuItem<String>>((String reason) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: reason,
|
||||
child: Text(reason),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (String? value) {
|
||||
setState(() {
|
||||
_selectedAnomalyReason = value!;
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final dateNowLocale = AppLocalizations.of(context)!.dateOn(DateTime.now(), DateTime.now());
|
||||
return Scaffold(
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(15.0),
|
||||
child: Column(
|
||||
children: [
|
||||
ReflexHUInfo(
|
||||
hu: widget.handlingUnit,
|
||||
showArticles: false,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ReflexAlert(
|
||||
bigIcon: true,
|
||||
title: widget.isDeliveryValid
|
||||
? AppLocalizations.of(context)!.deliveryOk
|
||||
: AppLocalizations.of(context)!.deliveryWithAnomaly,
|
||||
text: dateNowLocale,
|
||||
icon: widget.isDeliveryValid ? Icons.check : Icons.error,
|
||||
color: widget.isDeliveryValid
|
||||
? Globals.RP_SUCCESS_COLOR
|
||||
: Globals.RP_DANGER_COLOR,
|
||||
)),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
Flexible(
|
||||
child: widget.isDeliveryValid
|
||||
? _buildSignatureBox()
|
||||
: _buildAnomalyInput()),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: bd.Badge(
|
||||
badgeStyle: const bd.BadgeStyle(
|
||||
badgeColor: Globals.RP_PRIMARY_COLOR,
|
||||
),
|
||||
badgeContent: Text(
|
||||
_imageFileList.length.toString(),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
child: ReflexButton(
|
||||
isFullWidth: true,
|
||||
color: Globals.RP_LIGHT_COLOR,
|
||||
textColor: Colors.black,
|
||||
text: AppLocalizations.of(context)!.addPhoto,
|
||||
onPressed: _takePhoto,
|
||||
))),
|
||||
const SizedBox(width: 15),
|
||||
Expanded(
|
||||
child: ReflexButton(
|
||||
text: AppLocalizations.of(context)!.nextDelivery,
|
||||
onPressed: _handleOnNextDelivery,
|
||||
)),
|
||||
],
|
||||
),
|
||||
],
|
||||
)));
|
||||
}
|
||||
}
|
||||
122
lib/widgets/scan_flow/scan_barcode_screen.dart
Normal file
122
lib/widgets/scan_flow/scan_barcode_screen.dart
Normal file
@@ -0,0 +1,122 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:dart_core_sdk/handlingunit.pb.dart';
|
||||
import 'package:dart_core_sdk/handlingunitQuery.pbgrpc.dart';
|
||||
import 'package:dart_core_sdk/shared.pb.dart';
|
||||
import 'package:sampleapp/widgets/components/reflex_button.dart';
|
||||
import 'package:barcode_scan2/barcode_scan2.dart';
|
||||
import 'package:sampleapp/widgets/components/reflex_circular_progress.dart';
|
||||
import 'package:sampleapp/widgets/components/reflex_text_form_field.dart';
|
||||
import '../../locator.dart';
|
||||
import '../components/reflex_alert.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
class ScanBarcode extends StatefulWidget {
|
||||
final String projectID;
|
||||
|
||||
final void Function(String refID, Handlingunit? handlingUnitResult) onBarcodeScanned;
|
||||
|
||||
const ScanBarcode(
|
||||
{super.key, required this.projectID, required this.onBarcodeScanned});
|
||||
|
||||
@override
|
||||
_ScanBarcodeState createState() => _ScanBarcodeState();
|
||||
}
|
||||
|
||||
class _ScanBarcodeState extends State<ScanBarcode> {
|
||||
final _handlingUnitIDController = TextEditingController();
|
||||
|
||||
bool _isButtonDisabled() {
|
||||
return _handlingUnitIDController.text.isEmpty;
|
||||
}
|
||||
|
||||
void _onHandlingUnitLookup() async {
|
||||
showDialog(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
builder: (context) => const Center(child: ReflexCircularProgress()));
|
||||
await locator
|
||||
.get<HandlingunitQueryClient>()
|
||||
.getByIds(HandlingunitByIdQuery(
|
||||
header: QueryProjectHeader(
|
||||
projectID: widget.projectID,
|
||||
),
|
||||
iDs: [EntityID(
|
||||
refID: _handlingUnitIDController.text,
|
||||
)],
|
||||
))
|
||||
.then((res) => {
|
||||
widget.onBarcodeScanned(_handlingUnitIDController.text, res.objects.isNotEmpty ? res.objects.first : null),
|
||||
Navigator.of(context, rootNavigator: true).pop()
|
||||
})
|
||||
.catchError((e) => {
|
||||
Navigator.of(context, rootNavigator: true).pop(),
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(AppLocalizations.of(context)!.error),
|
||||
content: Text(e.toString()),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.ok))
|
||||
],
|
||||
))
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(15.0),
|
||||
child: Form(
|
||||
child: Column(
|
||||
children: [
|
||||
ReflexTextFormField(
|
||||
controller: _handlingUnitIDController,
|
||||
label: AppLocalizations.of(context)!.handlingUnitIdentifier,
|
||||
onChanged: (v) {
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
// Separator
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.or,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// Scan button
|
||||
ReflexButton(
|
||||
text: AppLocalizations.of(context)!.scanBarcode,
|
||||
isFullWidth: true,
|
||||
onPressed: () async {
|
||||
var result = await BarcodeScanner.scan();
|
||||
if (result.rawContent.isNotEmpty) {
|
||||
setState(() {
|
||||
_handlingUnitIDController.text = result.rawContent.trim();
|
||||
});
|
||||
_onHandlingUnitLookup();
|
||||
}
|
||||
}),
|
||||
ReflexAlert(
|
||||
text:
|
||||
AppLocalizations.of(context)!.selectHandlingUnit,
|
||||
icon: Icons.info_outline_rounded,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: ReflexButton(
|
||||
text: AppLocalizations.of(context)!.search,
|
||||
onPressed:
|
||||
_isButtonDisabled() ? null : _onHandlingUnitLookup,
|
||||
))
|
||||
],
|
||||
))));
|
||||
}
|
||||
}
|
||||
101
lib/widgets/scan_flow/scan_result_screen.dart
Normal file
101
lib/widgets/scan_flow/scan_result_screen.dart
Normal file
@@ -0,0 +1,101 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:dart_core_sdk/handlingunit.pb.dart';
|
||||
import 'package:sampleapp/widgets/components/reflex_button.dart';
|
||||
import 'package:sampleapp/widgets/components/reflex_hu_info.dart';
|
||||
|
||||
import '../../globals.dart';
|
||||
import '../components/reflex_alert.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
class ScanResult extends StatefulWidget {
|
||||
final String refID;
|
||||
final Handlingunit? handlingUnit;
|
||||
final String projectID;
|
||||
final void Function(bool isDeliveryValid) onDeliveryConfirmed;
|
||||
|
||||
const ScanResult(
|
||||
{super.key,
|
||||
required this.projectID,
|
||||
required this.onDeliveryConfirmed,
|
||||
this.handlingUnit, required this.refID});
|
||||
|
||||
@override
|
||||
_ScanResultState createState() => _ScanResultState();
|
||||
}
|
||||
|
||||
class _ScanResultState extends State<ScanResult> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void _onAnomalyPressed() async {
|
||||
widget.onDeliveryConfirmed(false);
|
||||
}
|
||||
|
||||
void _onValidPressed() async {
|
||||
widget.onDeliveryConfirmed(true);
|
||||
}
|
||||
|
||||
Widget _buildNoHu() {
|
||||
return Column(children: [
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context)!.handlingUnitIdentifier,
|
||||
),
|
||||
controller: TextEditingController(text: widget.refID),
|
||||
readOnly: true,
|
||||
),
|
||||
ReflexAlert(
|
||||
icon: Icons.access_alarm_rounded,
|
||||
text:
|
||||
"${AppLocalizations.of(context)!.noHandlingUnitFound}\n${AppLocalizations.of(context)!.pleaseTryAgain}.",
|
||||
color: Globals.RP_DANGER_COLOR),
|
||||
ReflexButton(
|
||||
text: AppLocalizations.of(context)!.tryAgain,
|
||||
onPressed: () => Navigator.pop(context),
|
||||
isFullWidth: true,
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(15.0),
|
||||
child: widget.handlingUnit == null
|
||||
? _buildNoHu()
|
||||
: Column(children: [
|
||||
Expanded(
|
||||
child: ReflexHUInfo(
|
||||
hu: widget.handlingUnit!,
|
||||
showArticles: true,
|
||||
)),
|
||||
SizedBox(height: 10),
|
||||
ReflexAlert(
|
||||
text:
|
||||
AppLocalizations.of(context)!.pleaseIndicateAnomaly,
|
||||
icon: Icons.info_outline_rounded),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ReflexButton(
|
||||
text: AppLocalizations.of(context)!.anomaly,
|
||||
color: Globals.RP_DANGER_COLOR,
|
||||
onPressed: () => _onAnomalyPressed(),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: ReflexButton(
|
||||
text: AppLocalizations.of(context)!.validDelivery,
|
||||
color: Globals.RP_SUCCESS_COLOR,
|
||||
onPressed: () => _onValidPressed(),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
])));
|
||||
}
|
||||
}
|
||||
119
lib/widgets/scan_flow/select_context_screen.dart
Normal file
119
lib/widgets/scan_flow/select_context_screen.dart
Normal file
@@ -0,0 +1,119 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:dart_core_sdk/proj.pbgrpc.dart';
|
||||
import 'package:sampleapp/globals.dart';
|
||||
import 'package:sampleapp/widgets/components/reflex_alert.dart';
|
||||
import 'package:sampleapp/widgets/components/reflex_button.dart';
|
||||
import 'package:sampleapp/widgets/components/reflex_circular_progress.dart';
|
||||
import 'package:sampleapp/widgets/components/reflex_dropdown_button_form_field.dart';
|
||||
import '../../locator.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
class SelectContext extends StatefulWidget {
|
||||
final void Function(String projectID) onContextSelected;
|
||||
|
||||
const SelectContext({super.key, required this.onContextSelected});
|
||||
|
||||
@override
|
||||
_SelectContextState createState() => _SelectContextState();
|
||||
}
|
||||
|
||||
class _SelectContextState extends State<SelectContext> {
|
||||
final projectClient = locator.get<ProjectServiceClient>();
|
||||
|
||||
List<MyContext> contexts = [];
|
||||
List<Project> projectList = [];
|
||||
Project? selectedProject;
|
||||
bool isLoading = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadContexts();
|
||||
}
|
||||
|
||||
Future<void> _loadContexts() async {
|
||||
projectClient.getMyUIContext(GetMyContextRequest()).then((value) {
|
||||
contexts = value.myContexts;
|
||||
projectList = contexts.first.projects.map((idName) => Project(iD: idName.iD, name: idName.name)).toList();
|
||||
projectList.sort((a, b) => a.name.compareTo(b.name));
|
||||
selectedProject = projectList.isNotEmpty ? projectList.first : null;
|
||||
}).whenComplete(() => setState(() {
|
||||
isLoading = false;
|
||||
}));
|
||||
}
|
||||
|
||||
Widget _buildContextDropdowns() {
|
||||
return ListView(shrinkWrap: true, children: [
|
||||
ReflexDropdownButtonFormField<String>(
|
||||
label: AppLocalizations.of(context)!.organization,
|
||||
value: contexts.isNotEmpty ? contexts[0].organisation.iD : '',
|
||||
items: contexts.map<DropdownMenuItem<String>>((MyContext c) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: c.organisation.iD,
|
||||
child: Text(c.organisation.name),
|
||||
);
|
||||
}).toList(),
|
||||
// On change, update the project list
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
projectList = contexts.firstWhere((element) => element.organisation.iD == value).projects.map((idName) => Project(iD: idName.iD, name: idName.name)).toList();
|
||||
projectList.sort((a, b) => a.name.compareTo(b.name));
|
||||
selectedProject = projectList.isNotEmpty ? projectList.first : null;
|
||||
});
|
||||
},
|
||||
),
|
||||
ReflexDropdownButtonFormField<String>(
|
||||
label: AppLocalizations.of(context)!.project,
|
||||
value: projectList.isNotEmpty ? projectList[0].iD : '',
|
||||
items: projectList.map<DropdownMenuItem<String>>((Project p) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: p.iD,
|
||||
child: Text(p.name),
|
||||
);
|
||||
}).toList(),
|
||||
// On change, update the selected project
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
selectedProject =
|
||||
projectList.firstWhere((element) => element.iD == value);
|
||||
});
|
||||
},
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
bool _isButtonDisabled() {
|
||||
return selectedProject == null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(15.0),
|
||||
child: Column(
|
||||
children: [
|
||||
!isLoading
|
||||
? _buildContextDropdowns()
|
||||
: const ReflexCircularProgress(),
|
||||
const SizedBox(height: 20),
|
||||
ReflexAlert(
|
||||
icon: Icons.info_outline_rounded,
|
||||
text:
|
||||
AppLocalizations.of(context)!.selectContext
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: ReflexButton(
|
||||
text: AppLocalizations.of(context)!.start,
|
||||
onPressed: _isButtonDisabled()
|
||||
? null
|
||||
: () {
|
||||
widget.onContextSelected(selectedProject!.iD);
|
||||
},
|
||||
))
|
||||
],
|
||||
)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user