This commit is contained in:
jantunesmessias 2025-02-07 16:50:45 -03:00
parent 16f43bbab8
commit 906fbcaf24
4 changed files with 141 additions and 108 deletions

View File

@ -52,6 +52,7 @@ class _AccessHistoryState extends State<AccessHistoryScreen> {
_model = createModel(context, () => AcessHistoryPageModel());
_accessFuture = fetchAccessHistoryService();
_scrollController = ScrollController()
..addListener(() {
if (_scrollController.position.atEdge &&

View File

@ -4,21 +4,24 @@ part of 'vehicles_on_the_property.dart';
// ignore: must_be_immutable
class VehicleHistoryScreen extends StatefulWidget {
VehicleHistoryScreen(this.model, {super.key});
late VehicleModel model;
@override
State<VehicleHistoryScreen> createState() => _VehicleHistoryScreenState();
}
class _VehicleHistoryScreenState extends State<VehicleHistoryScreen>
with Remotable {
@override
void initState() {
super.initState();
_initializeScrollController();
_future = _fetch();
}
@override
@ -69,8 +72,8 @@ class _VehicleHistoryScreenState extends State<VehicleHistoryScreen>
Widget _buildHistoryList(BuildContext context, double limitedBodyTextSize) {
return Expanded(
child: FutureBuilder<List<Widget?>>(
future: _future,
child: FutureBuilder<List<dynamic>>(
future: _fetch(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
@ -79,17 +82,13 @@ class _VehicleHistoryScreenState extends State<VehicleHistoryScreen>
} else {
return ListView.builder(
shrinkWrap: true,
addAutomaticKeepAlives: true,
restorationId: '',
physics: const BouncingScrollPhysics(),
controller: _scrollController,
itemCount: _wrap.length + 1,
itemBuilder: (context, index) {
if (index == 0) {
return _buildHeader(context, limitedBodyTextSize);
} else {
Widget? item = _wrap[index - 1];
return _wrap[index - 1] ?? SizedBox.shrink();
}
},
itemCount: snapshot.data!.length ,
itemBuilder: (context, index) => _generateItems(context, snapshot.data![index], index),
);
}
},
@ -97,24 +96,6 @@ class _VehicleHistoryScreenState extends State<VehicleHistoryScreen>
);
}
Widget _buildHeader(BuildContext context, double limitedBodyTextSize) {
log('amountRegister: ${widget.model.amountRegister}');
return Padding(
padding: const EdgeInsets.only(right: 30, top: 10),
child: Text(
(widget.model.amountRegister == '0' ||
widget.model.amountRegister == null)
? ''
: "${FFLocalizations.of(context).getVariableText(ptText: "Quantidade de Veículos: ", enText: "Amount of Vehicles: ")}${widget.model.amountRegister}/${widget.count}",
textAlign: TextAlign.right,
style: TextStyle(
fontFamily: 'Nunito',
fontSize: limitedBodyTextSize,
),
),
);
}
Widget _buildLoadingIndicator(BuildContext context) {
return Container(
padding: const EdgeInsets.only(top: 15, bottom: 15),
@ -127,56 +108,59 @@ class _VehicleHistoryScreenState extends State<VehicleHistoryScreen>
),
);
}
}
// Mixin for Fetch Visits
mixin Remotable on State<VehicleHistoryScreen> {
late Future<List<Widget?>> _future;
List<Widget?> _wrap = [];
late ScrollController _scrollController;
double offset = 0.0;
List<dynamic> _wrap = [];
int _pageNumber = 1;
bool _hasData = true;
bool _loading = true;
int count = 0;
Future<List<Widget?>> _fetch() async {
if (!widget._hasData || !widget._loading) return [];
setState(() => widget._loading = true);
Future<List<dynamic>> _fetch() async {
if (!_hasData || !_loading) return _wrap;
print('hasHasData');
setState(() => _loading = true);
try {
var response = await PhpGroup.getVehiclesByProperty.call(widget._pageNumber.toString());
final List<dynamic> vehicles = response.jsonBody['vehicles'] as List<dynamic>? ?? [];
safeSetState(() => widget.count = response.jsonBody['total_rows'] ?? 0);
var response = await PhpGroup.getVehiclesByProperty.call(_pageNumber.toString());
final List<dynamic> vehicles = response.jsonBody['vehicles'] as List<dynamic>? ?? [];
safeSetState(() => count = response.jsonBody['total_rows'] ?? 0);
if (vehicles.isNotEmpty) {
setState(() {
widget._hasData = true;
widget._loading = false;
_hasData = true;
_loading = false;
});
return _generateItems(context, vehicles);
print('vehicles.isNotEmpty');
_wrap.addAll(vehicles);
return _wrap;
}
_showNoMoreDataSnackBar(context);
setState(() {
widget._hasData = false;
widget._loading = false;
_hasData = false;
_loading = false;
});
return [];
print('hasEmpty: ${_wrap.length}');
return _wrap;
} catch (e, s) {
DialogUtil.errorDefault(context);
LogUtil.requestAPIFailed("proccessRequest.php", "", "Consulta de Veículo", e, s);
setState(() {
widget._hasData = false;
widget._loading = false;
_hasData = false;
_loading = false;
});
return [];
print('hasError');
return _wrap;
}
}
@ -189,43 +173,56 @@ mixin Remotable on State<VehicleHistoryScreen> {
showSnackbar(context, message, true);
}
Future<List<Widget?>> _generateItems(BuildContext context, List<dynamic> uItem) async {
if (!widget._hasData) return [];
Widget _buildHeader(BuildContext context) {
double limitedBodyTextSize = LimitedFontSizeUtil.getBodyFontSize(context);
log('amountRegister: ${widget.model.amountRegister}');
return Padding(
padding: const EdgeInsets.only(right: 30, top: 10),
child: Text(
(widget.model.amountRegister == '0' ||
widget.model.amountRegister == null)
? ''
: "${FFLocalizations.of(context).getVariableText(ptText: "Quantidade de Veículos: ", enText: "Amount of Vehicles: ")}${widget.model.amountRegister}/$count",
textAlign: TextAlign.right,
style: TextStyle(
fontFamily: 'Nunito',
fontSize: limitedBodyTextSize,
),
),
);
}
List<Widget?> items = [];
for (var uItem in uItem) {
final bool? isOwner = uItem['isOwnerVehicle'];
final IconData? iconData = isOwner == true ? Symbols.no_crash : Symbols.directions_car;
Widget? _generateItems(BuildContext context, dynamic snapshot, int index) {
if (index == 0) return _buildHeader(context);
final bool? isOwner = snapshot['isOwnerVehicle'];
final IconData? iconData = isOwner == true ? Symbols.garage : Symbols.directions_car;
final FreCardIcon? cardIcon = isOwner != null
? FreCardIcon(height: 50, width: 100, icon: Icon(iconData, size: 80, opticalSize: 10))
: null;
final String? tag = uItem['tag'];
final String? tag = snapshot['tag'];
final bool containTag = tag.isNotNullAndEmpty;
final Map<String, String> labelsHashMap = _generateLabelsHashMap(context, uItem, tag, containTag);
final Map<String, String> labelsHashMap = _generateLabelsHashMap(context, snapshot, tag, containTag);
final statusHashMapList = await _generateStatusHashMapList(uItem);
final List<Map<String, Color>> statusHashMapList = _generateStatusHashMapList(snapshot);
Future<void> onTapCardItemAction() async {
await _handleCardItemTap(context, cardIcon, uItem);
_handleCardItemTap(context, cardIcon, snapshot);
}
final statusLinkedHashMap = statusHashMapList.map((map) => LinkedHashMap<String, Color>.from(map)).toList();
final length = statusLinkedHashMap.expand((e) => [e.length]);
items.add(CardItemTemplateComponentWidget(
return CardItemTemplateComponentWidget(
icon: cardIcon,
labelsHashMap: labelsHashMap,
statusHashMap: statusHashMapList,
onTapCardItemAction: onTapCardItemAction,
itemWidthFactor: length == 1 ? 0.25 : 0.50,
));
);
}
widget._wrap.addAll(items);
return items;
}
Map<String, String> _generateLabelsHashMap(BuildContext context,
Map<String, dynamic> uItem, String? tag, bool containTag) {
@ -242,17 +239,17 @@ mixin Remotable on State<VehicleHistoryScreen> {
};
}
Future<List<Map<String, Color>>> _generateStatusHashMapList(
Map<String, dynamic> uItem) async {
List<Map<String, Color>> _generateStatusHashMapList(
Map<String, dynamic> uItem) {
final statusHashMap =
await widget.model.generateStatusColorMap(uItem, false);
widget.model.generateStatusColorMap(uItem, false);
return statusHashMap != null ? [statusHashMap] : [];
}
Future<void> _handleCardItemTap(BuildContext context, FreCardIcon? cardIcon,
Map<String, dynamic> uItem) async {
try {
final dialogContent = await widget.model.buildVehicleDetails(
final dialogContent = widget.model.buildVehicleDetails(
icon: cardIcon,
item: uItem,
context: context,
@ -266,9 +263,8 @@ mixin Remotable on State<VehicleHistoryScreen> {
Dialog(alignment: Alignment.center, child: dialogContent),
).whenComplete(() {
safeSetState(() {
widget._pageNumber = 1;
widget._wrap = [];
widget._future = _fetch();
_pageNumber = 1;
_fetch();
});
});
} catch (e, s) {
@ -276,31 +272,34 @@ mixin Remotable on State<VehicleHistoryScreen> {
LogUtil.requestAPIFailed(
"proccessRequest.php", "", "Consulta de Veículos", e, s);
safeSetState(() {
widget._hasData = false;
widget._loading = false;
_hasData = false;
_loading = false;
});
}
}
late ScrollController _scrollController;
void _initializeScrollController() {
_scrollController = ScrollController()
_scrollController = ScrollController(keepScrollOffset: true, initialScrollOffset: offset)
..addListener(() {
print('ScrollController');
if(!widget._hasData) return;
if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent) {
if(!_hasData) return;
if
// (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent)
(_scrollController.position.atEdge && _scrollController.position.pixels != 0)
{
print('ScrollController -> loadMore');
offset = _scrollController.offset;
_loadMore();
}
});
}
void _loadMore() {
if (widget._hasData) {
widget._pageNumber++;
widget._loading = true;
widget._future = _fetch();
void _loadMore() async {
if (_hasData) {
_pageNumber+=1;
_loading = true;
_fetch();
}
}
}

View File

@ -71,6 +71,7 @@ class VehicleModel extends FlutterFlowModel<VehiclePage>
Future<void> initAsync() async {
amountRegister =
await StorageHelper().get(LocalsStorageKey.vehicleAmountRegister.key);
autoApproval= await StorageHelper().get(LocalsStorageKey.vehicleAutoApproval.key);
}
bool isFormValid(BuildContext context, GlobalKey<FormState> formKey) {
@ -89,6 +90,8 @@ mixin class _BaseVehiclePage {
bool isEditing = false;
ApiCallResponse? vehicleResponse;
String? amountRegister = '0';
late final String? autoApproval;
VoidCallback? onUpdateVehicle;
VoidCallback? onRegisterVehicle;
@ -237,10 +240,10 @@ mixin _VehicleUpdateScreenModel on _BaseVehiclePage {
/// [_VehicleHistoryScreenModel] is a mixin that contains the business logic of the vehicle history page.
mixin _VehicleHistoryScreenModel on _BaseVehiclePage {
Future<Map<String, Color>?>? generateStatusColorMap(
dynamic uItem, bool isDetail) async {
final autoApproval =
await StorageHelper().get(LocalsStorageKey.vehicleAutoApproval.key);
Map<String, Color>? generateStatusColorMap(dynamic uItem, bool isDetail) {
if (autoApproval.toBoolean == true) return null;
final theme = FlutterFlowTheme.of(context);
final localization = FFLocalizations.of(context);
@ -296,7 +299,7 @@ mixin _VehicleHistoryScreenModel on _BaseVehiclePage {
return {};
}
Future<List<FFButtonWidget>?> generateActionButtons(dynamic item) async {
List<FFButtonWidget> generateActionButtons(dynamic item) {
final Color iconButtonColor = FlutterFlowTheme.of(context).primaryText;
final FFButtonOptions buttonOptions = FFButtonOptions(
height: 40,
@ -339,7 +342,7 @@ mixin _VehicleHistoryScreenModel on _BaseVehiclePage {
ptText: 'Você tem certeza que deseja cancelar essa solicitação?',
enText: 'Are you sure you want to delete this request?',
),
() async => await processCancelRequest(item['status'], item));
() async => processCancelRequest(item['status'], item));
}
final deleteText = FFLocalizations.of(context)
@ -356,7 +359,7 @@ mixin _VehicleHistoryScreenModel on _BaseVehiclePage {
ptText: 'Você tem certeza que deseja excluir esse veículo?',
enText: 'Are you sure you want to delete this vehicle?',
),
() async => await processDeleteRequest(item),
() async => processDeleteRequest(item),
);
}
@ -401,7 +404,7 @@ mixin _VehicleHistoryScreenModel on _BaseVehiclePage {
];
}
Future<void> processDeleteRequest(dynamic item) async {
void processDeleteRequest(dynamic item) async {
log('processDeleteRequest -> item[$item]');
return await PhpGroup.deleteVehicle
.call(
@ -448,7 +451,7 @@ mixin _VehicleHistoryScreenModel on _BaseVehiclePage {
});
}
Future<void> processCancelRequest(String status, dynamic item) async {
void processCancelRequest(String status, dynamic item) async {
late final ApiCallResponse value;
try {
switch (status) {
@ -500,7 +503,7 @@ mixin _VehicleHistoryScreenModel on _BaseVehiclePage {
}
}
Future<ApiCallResponse> processCancelDeleteRequest(dynamic item) async {
Future<ApiCallResponse> processCancelDeleteRequest(dynamic item) async{
return await PhpGroup.deleteVehicle.call(
vehicleId: item['vehicleId'],
licensePlate: item['licensePlate'],
@ -509,7 +512,7 @@ mixin _VehicleHistoryScreenModel on _BaseVehiclePage {
);
}
Future<ApiCallResponse> processCancelUpdateRequest(dynamic item) async {
Future<ApiCallResponse> processCancelUpdateRequest(dynamic item) async {
return await PhpGroup.deleteVehicle.call(
vehicleId: item['vehicleId'],
licensePlate: item['licensePlate'],
@ -527,7 +530,7 @@ mixin _VehicleHistoryScreenModel on _BaseVehiclePage {
);
}
Future<Map<String, String>> generateLabelsHashMap(dynamic item) async {
Map<String, String> generateLabelsHashMap(dynamic item) {
return {
if (item['model'] != null && item['model'] != '')
'${FFLocalizations.of(context).getVariableText(ptText: "Modelo", enText: "Model")}:':
@ -547,15 +550,15 @@ mixin _VehicleHistoryScreenModel on _BaseVehiclePage {
};
}
Future<Widget> buildVehicleDetails({
DetailsComponentWidget buildVehicleDetails({
required dynamic item,
required BuildContext context,
required VehicleModel model,
required FreCardIcon? icon,
}) async {
final status = await generateStatusColorMap(item, true);
final buttons = await generateActionButtons(item);
final labels = await generateLabelsHashMap(item);
}) {
final status = generateStatusColorMap(item, true);
final buttons = generateActionButtons(item);
final labels = generateLabelsHashMap(item);
return DetailsComponentWidget(
icon: icon,
buttons: buttons,

30
scripts/httpie.sh Executable file
View File

@ -0,0 +1,30 @@
#!/bin/bash
# Define the base URL for the API endpoint
BASE_URL="https://freaccess.com.br/freaccess/processRequest.php"
# Define common parameters
DEV_UUID="6b7b81849c115a8a"
USER_UUID="678aa05b0c2154.50583237"
CLI_ID="7"
ACTIVITY="getVehiclesByProperty"
PAGE_SIZE="10"
# Function to perform the HTTP request
perform_request() {
local page=$1
http --form POST "$BASE_URL" \
devUUID="$DEV_UUID" \
userUUID="$USER_UUID" \
cliID="$CLI_ID" \
atividade="$ACTIVITY" \
page="$page" \
pageSize="$PAGE_SIZE" \
--check-status \
--ignore-stdin \
--timeout=10
}
# Perform requests for pages 1 and 2
perform_request 1
perform_request 2