Compare commits
79 Commits
10bf8f523c
...
fb5f755d70
Author | SHA1 | Date |
---|---|---|
|
fb5f755d70 | |
|
40ef82cb85 | |
|
203632bbdf | |
|
a7f1ea418b | |
|
aa1bd55818 | |
|
575cfc5a83 | |
|
d03b3fbdfe | |
|
c0061eabe0 | |
|
00e5f416b9 | |
|
a8220f7743 | |
|
7d63d91ffd | |
|
1b785ab995 | |
|
8c91335ba1 | |
|
6b437e70e6 | |
|
a94f0dd977 | |
|
a9a62a4f9e | |
|
d585eaace5 | |
|
b89b4b8a2c | |
|
3bbe005e80 | |
|
9df0a15374 | |
|
d465869bc9 | |
|
f570d9db23 | |
|
ce6426ee9c | |
|
6f12eaa8ea | |
|
afe8d0d509 | |
|
5ffd4427d3 | |
|
272f50ed61 | |
|
7d6f7ecbc8 | |
|
fb026fc22f | |
|
1f04e7e5ff | |
|
85f961076f | |
|
6ac6e15009 | |
|
e21d5b5e53 | |
|
6b724b3821 | |
|
5b7ed35499 | |
|
66b6b6d884 | |
|
e08a049000 | |
|
3a9772558f | |
|
b9f355cdbf | |
|
75e4a98b17 | |
|
37f624401f | |
|
5845538518 | |
|
c7483d4676 | |
|
d42c6301a8 | |
|
fcf6af93b9 | |
|
49154d9945 | |
|
0b38538d2b | |
|
9edd350f13 | |
|
a46181d2f4 | |
|
70e81c0e34 | |
|
b1a09d999c | |
|
7b0297a491 | |
|
a4b7ee3cd0 | |
|
b0e4e86391 | |
|
05b3a612ba | |
|
f193baebe7 | |
|
906fbcaf24 | |
|
16f43bbab8 | |
|
b2df549066 | |
|
2e22045eea | |
|
8e035e00aa | |
|
61e3effa59 | |
|
56cb5427a2 | |
|
f64d65425f | |
|
73ea7d5641 | |
|
a07edd0c21 | |
|
1cc481a83e | |
|
268c4c897b | |
|
bc16df866a | |
|
742312e888 | |
|
1d4041f92b | |
|
a79b165635 | |
|
7dd7ed7a32 | |
|
3b7a33801f | |
|
e5bd18dec6 | |
|
f33081a773 | |
|
fb91e62a25 | |
|
3378c3b7a4 | |
|
083f2d200d |
|
@ -1 +1 @@
|
|||
gradle 7.6.1
|
||||
gradle 8.10.2
|
||||
|
|
|
@ -7,9 +7,11 @@ import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hub/components/atomic_components/shared_components_atoms/submit_button.dart';
|
||||
import 'package:hub/components/molecular_components/throw_exception/throw_exception_widget.dart';
|
||||
import 'package:hub/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart';
|
||||
import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart';
|
||||
import 'package:hub/components/templates_components/details_component/details_component_widget.dart';
|
||||
import 'package:hub/features/backend/api_requests/index.dart';
|
||||
import 'package:hub/features/local/index.dart';
|
||||
import 'package:hub/features/menu/index.dart';
|
||||
|
@ -20,6 +22,8 @@ import 'package:hub/features/storage/index.dart';
|
|||
import 'package:hub/flutter_flow/index.dart' as ff;
|
||||
import 'package:hub/flutter_flow/index.dart';
|
||||
import 'package:hub/main.dart';
|
||||
import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:flutter_web_plugins/url_strategy.dart';
|
||||
|
||||
|
@ -27,7 +31,6 @@ import 'app_test.dart';
|
|||
import 'fuzzer/fuzzer.dart';
|
||||
|
||||
import 'package:patrol_finders/patrol_finders.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
export 'package:flutter_test/flutter_test.dart';
|
||||
export 'package:patrol/patrol.dart';
|
||||
|
||||
|
@ -56,6 +59,7 @@ part 'storage_test.dart';
|
|||
part 'utils_test.dart';
|
||||
|
||||
part 'welcome_test.dart';
|
||||
part 'vehicle_test.dart';
|
||||
|
||||
late PatrolTester $;
|
||||
|
||||
|
@ -83,5 +87,9 @@ void main() {
|
|||
|
||||
LocalsTest.setLocal();
|
||||
LocalsTest.unlinkLocal();
|
||||
LocalsTest.attachLocal();
|
||||
|
||||
VehicleTest.vehiclePage();
|
||||
VehicleTest.historyScreen();
|
||||
VehicleTest.registerScreen();
|
||||
VehicleTest.updateScreen();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,320 @@
|
|||
part of 'app_test.dart';
|
||||
|
||||
class VehicleTest {
|
||||
static Future<void> _initVehicleModule() async {
|
||||
final vehicleParam = <String, dynamic>{
|
||||
'display': 'VISIVEL',
|
||||
'expirationDate': '',
|
||||
'startDate': '',
|
||||
'quantity': 0,
|
||||
};
|
||||
final vehicleManagerParam = <String, dynamic>{
|
||||
'display': 'VISIVEL',
|
||||
'expirationDate': '',
|
||||
'startDate': '',
|
||||
'quantity': 0,
|
||||
};
|
||||
await LicenseRepositoryImpl()
|
||||
.setModule(LicenseKeys.vehicles.value, vehicleParam);
|
||||
await LicenseRepositoryImpl()
|
||||
.setModule(LicenseKeys.vehiclesManager.value, vehicleManagerParam);
|
||||
}
|
||||
|
||||
static Future<void> vehiclePage() async {
|
||||
patrolWidgetTest(
|
||||
'Vehicle Page',
|
||||
(PatrolTester tester) async {
|
||||
$ = tester;
|
||||
$.tester.printToConsole('Vehicle Page');
|
||||
final PatrolFinder throwsException = $(Dialog).$(ThrowExceptionWidget);
|
||||
|
||||
await _loggedWithMultiLocalsAccount();
|
||||
await _initVehicleModule();
|
||||
await $.pumpAndSettle();
|
||||
await $.pumpWidgetAndSettle(const App());
|
||||
|
||||
ff.navigatorKey.currentContext!.go('/vehiclesOnThePropertyPage');
|
||||
|
||||
final String title = MenuEntry.entries //
|
||||
.where((entry) => entry.key == 'FRE-HUB-VEHICLES') //
|
||||
.map((entry) => entry.name)
|
||||
.first;
|
||||
|
||||
final PatrolFinder appBar = await $(AppBar) //
|
||||
.waitUntilExists();
|
||||
final PatrolFinder titleAppBar = await appBar //
|
||||
.$(title)
|
||||
.waitUntilVisible();
|
||||
expect(titleAppBar, findsOneWidget);
|
||||
|
||||
final PatrolFinder tab1 = await $(#TabView_Tab1) //
|
||||
.waitUntilExists();
|
||||
final PatrolFinder tab2 = await $(#TabView_Tab2) //
|
||||
.waitUntilExists();
|
||||
|
||||
await tab2.tap();
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
await tab1.tap();
|
||||
|
||||
final PatrolFinder listViewFinder = await $(VehicleHistoryScreen) //
|
||||
.$(ListView)
|
||||
.waitUntilVisible();
|
||||
|
||||
expect(listViewFinder, findsOneWidget);
|
||||
|
||||
final PatrolFinder entriesFinder = await $(listViewFinder)
|
||||
.$(CardItemTemplateComponentWidget)
|
||||
.waitUntilVisible();
|
||||
|
||||
expect(entriesFinder, findsWidgets);
|
||||
final int entriesCount = entriesFinder.evaluate().length;
|
||||
await $.pumpAndSettle();
|
||||
|
||||
if (entriesCount > 0)
|
||||
for (int i = 0; i < entriesCount; i++) {
|
||||
await $(entriesFinder.at(i)).scrollTo();
|
||||
|
||||
await $(entriesFinder.at(i))
|
||||
.waitUntilVisible(timeout: const Duration(seconds: 1))
|
||||
.tap(
|
||||
settleTimeout: const Duration(seconds: 1),
|
||||
settlePolicy: SettlePolicy.noSettle,
|
||||
);
|
||||
|
||||
await $.pumpAndSettle(duration: Duration(milliseconds: 500));
|
||||
final PatrolFinder detailsFinder =
|
||||
await $(DetailsComponentWidget).waitUntilVisible();
|
||||
expect(detailsFinder, findsOneWidget);
|
||||
|
||||
await _navigateBackUsingSystemGesture();
|
||||
|
||||
// await $.native.pressBack().then((_) => $.pumpAndSettle());
|
||||
}
|
||||
},
|
||||
);
|
||||
patrolWidgetTest(
|
||||
'License',
|
||||
(PatrolTester tester) async {
|
||||
$ = tester;
|
||||
$.tester.printToConsole('Vehicle Page');
|
||||
|
||||
await _loggedWithMultiLocalsAccount();
|
||||
await _initVehicleModule();
|
||||
await $.pumpAndSettle();
|
||||
|
||||
await $.pumpWidgetAndSettle(const App());
|
||||
|
||||
ff.navigatorKey.currentContext!.go('/vehiclesOnThePropertyPage');
|
||||
final String title = MenuEntry.entries //
|
||||
.where((entry) => entry.key == 'FRE-HUB-VEHICLES') //
|
||||
.map((entry) => entry.name)
|
||||
.first;
|
||||
|
||||
final PatrolFinder appBar = await $(AppBar) //
|
||||
.waitUntilExists();
|
||||
final PatrolFinder titleAppBar = await appBar //
|
||||
.$(title)
|
||||
.waitUntilVisible();
|
||||
expect(titleAppBar, findsOneWidget);
|
||||
|
||||
final PatrolFinder tab1 = await $(#TabView_Tab1) //
|
||||
.waitUntilExists();
|
||||
final PatrolFinder tab2 = await $(#TabView_Tab2) //
|
||||
.waitUntilExists();
|
||||
|
||||
await tab2.tap();
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
await tab1.tap();
|
||||
|
||||
final PatrolFinder listViewFinder = await $(VehicleHistoryScreen) //
|
||||
.$(ListView)
|
||||
.waitUntilVisible();
|
||||
|
||||
expect(listViewFinder, findsOneWidget);
|
||||
|
||||
final PatrolFinder entriesFinder = await $(listViewFinder)
|
||||
.$(CardItemTemplateComponentWidget)
|
||||
.waitUntilVisible();
|
||||
|
||||
expect(entriesFinder, findsWidgets);
|
||||
await $.pumpAndSettle();
|
||||
await Future.delayed(const Duration(milliseconds: 1000));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static Future<void> historyScreen() async {
|
||||
patrolWidgetTest(
|
||||
'historyScreen',
|
||||
(PatrolTester tester) async {
|
||||
$ = tester;
|
||||
$.tester.printToConsole('Vehicle Page');
|
||||
final PatrolFinder throwsException = $(Dialog).$(ThrowExceptionWidget);
|
||||
|
||||
await _loggedWithMultiLocalsAccount();
|
||||
await _initVehicleModule();
|
||||
await $.pumpAndSettle();
|
||||
await $.pumpWidgetAndSettle(const App());
|
||||
|
||||
ff.navigatorKey.currentContext!.go('/vehiclesOnThePropertyPage');
|
||||
|
||||
final String title = MenuEntry.entries //
|
||||
.where((entry) => entry.key == 'FRE-HUB-VEHICLES') //
|
||||
.map((entry) => entry.name)
|
||||
.first;
|
||||
|
||||
final PatrolFinder appBar = await $(AppBar) //
|
||||
.waitUntilExists();
|
||||
final PatrolFinder titleAppBar = await appBar //
|
||||
.$(title)
|
||||
.waitUntilVisible();
|
||||
expect(titleAppBar, findsOneWidget);
|
||||
|
||||
final PatrolFinder listViewFinder = await $(VehicleHistoryScreen) //
|
||||
.$(ListView)
|
||||
.waitUntilVisible();
|
||||
|
||||
expect(listViewFinder, findsOneWidget);
|
||||
|
||||
final PatrolFinder entriesFinder = await $(listViewFinder)
|
||||
.$(CardItemTemplateComponentWidget)
|
||||
.waitUntilVisible();
|
||||
|
||||
expect(entriesFinder, findsWidgets);
|
||||
await $.pumpAndSettle();
|
||||
await $(entriesFinder.first)
|
||||
.waitUntilVisible(timeout: const Duration(seconds: 1))
|
||||
.tap(
|
||||
settleTimeout: const Duration(seconds: 1),
|
||||
settlePolicy: SettlePolicy.noSettle,
|
||||
);
|
||||
|
||||
await $.pumpAndSettle(duration: Duration(milliseconds: 500));
|
||||
final PatrolFinder detailsFinder =
|
||||
await $(DetailsComponentWidget).waitUntilVisible();
|
||||
expect(detailsFinder, findsOneWidget);
|
||||
|
||||
await _navigateBackUsingSystemGesture();
|
||||
|
||||
/// Iterable Test
|
||||
// final int entriesCount = entriesFinder.evaluate().length;
|
||||
// for (int i = 0; i < entriesCount; i++) {
|
||||
// // await $(entriesFinder.at(i)).scrollTo();
|
||||
|
||||
// await $(entriesFinder.at(i))
|
||||
// .waitUntilVisible(timeout: const Duration(seconds: 1))
|
||||
// .tap(
|
||||
// settleTimeout: const Duration(seconds: 1),
|
||||
// settlePolicy: SettlePolicy.noSettle,
|
||||
// );
|
||||
|
||||
// await $.pumpAndSettle(duration: Duration(milliseconds: 500));
|
||||
// final PatrolFinder detailsFinder =
|
||||
// await $(DetailsComponentWidget).waitUntilVisible();
|
||||
// expect(detailsFinder, findsOneWidget);
|
||||
|
||||
// await _navigateBackUsingSystemGesture();
|
||||
|
||||
// // await $.native.pressBack().then((_) => $.pumpAndSettle());
|
||||
// }
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static Future<void> registerScreen() async {
|
||||
patrolWidgetTest(
|
||||
'registerScreen',
|
||||
(PatrolTester tester) async {
|
||||
$ = tester;
|
||||
$.tester.printToConsole('Vehicle Register Page');
|
||||
|
||||
await _loggedWithMultiLocalsAccount();
|
||||
await _initVehicleModule();
|
||||
await $.pumpAndSettle();
|
||||
await $.pumpWidgetAndSettle(const App());
|
||||
|
||||
ff.navigatorKey.currentContext!.go('/vehiclesOnThePropertyPage');
|
||||
|
||||
final PatrolFinder tab2 = await $(#TabView_Tab2) //
|
||||
.waitUntilExists();
|
||||
|
||||
await tab2.tap();
|
||||
|
||||
final PatrolFinder licensePlateField =
|
||||
await $(TextField).at(0).waitUntilVisible();
|
||||
final PatrolFinder modelField =
|
||||
await $(TextField).at(1).waitUntilVisible();
|
||||
final PatrolFinder colorField =
|
||||
await $(TextField).at(2).waitUntilVisible();
|
||||
final PatrolFinder submitButton =
|
||||
await $(SubmitButtonUtil).waitUntilVisible();
|
||||
|
||||
await licensePlateField.enterText('ABC1234');
|
||||
await modelField.enterText('Voyage');
|
||||
await colorField.enterText('Black');
|
||||
await submitButton.tap();
|
||||
|
||||
await $.pumpAndSettle();
|
||||
final PatrolFinder successDialog = await $(Dialog).waitUntilVisible();
|
||||
expect(successDialog, findsOneWidget);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static Future<void> updateScreen() async {
|
||||
patrolWidgetTest(
|
||||
'updateScreen',
|
||||
(PatrolTester tester) async {
|
||||
$ = tester;
|
||||
$.tester.printToConsole('Vehicle Update Page');
|
||||
|
||||
await _loggedWithMultiLocalsAccount();
|
||||
await _initVehicleModule();
|
||||
await $.pumpAndSettle();
|
||||
await $.pumpWidgetAndSettle(const App());
|
||||
|
||||
ff.navigatorKey.currentContext!.go('/vehiclesOnThePropertyPage');
|
||||
|
||||
final PatrolFinder tab1 = await $(#TabView_Tab1) //
|
||||
.waitUntilExists();
|
||||
|
||||
await tab1.tap();
|
||||
|
||||
final PatrolFinder listViewFinder = await $(VehicleHistoryScreen) //
|
||||
.$(ListView)
|
||||
.waitUntilVisible();
|
||||
|
||||
expect(listViewFinder, findsOneWidget);
|
||||
|
||||
final PatrolFinder entriesFinder = await $(listViewFinder)
|
||||
.$(CardItemTemplateComponentWidget)
|
||||
.waitUntilVisible();
|
||||
|
||||
expect(entriesFinder, findsWidgets);
|
||||
await $(entriesFinder.at(0)).tap();
|
||||
|
||||
final PatrolFinder editButton =
|
||||
await $(FFButtonWidget).$('Edit').waitUntilVisible();
|
||||
await editButton.tap();
|
||||
|
||||
final PatrolFinder licensePlateField =
|
||||
await $(TextField).at(0).waitUntilVisible();
|
||||
final PatrolFinder modelField =
|
||||
await $(TextField).at(1).waitUntilVisible();
|
||||
final PatrolFinder colorField =
|
||||
await $(TextField).at(2).waitUntilVisible();
|
||||
final PatrolFinder submitButton =
|
||||
await $(SubmitButtonUtil).waitUntilVisible();
|
||||
|
||||
await licensePlateField.enterText('XYZ5678');
|
||||
await modelField.enterText('Fiesta');
|
||||
await colorField.enterText('Red');
|
||||
await submitButton.tap();
|
||||
|
||||
await $.pumpAndSettle();
|
||||
final PatrolFinder successDialog = await $(Dialog).waitUntilVisible();
|
||||
expect(successDialog, findsOneWidget);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -5,6 +5,15 @@ import 'package:google_fonts/google_fonts.dart';
|
|||
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||
import 'package:hub/shared/utils/limited_text_size.dart';
|
||||
|
||||
class UpperCaseTextFormatter extends TextInputFormatter {
|
||||
@override
|
||||
TextEditingValue formatEditUpdate(
|
||||
TextEditingValue oldValue, TextEditingValue newValue) {
|
||||
return TextEditingValue(
|
||||
text: newValue.text.toUpperCase(), selection: newValue.selection);
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class CustomInputUtil extends StatefulWidget {
|
||||
final TextEditingController? controller;
|
||||
|
@ -20,22 +29,25 @@ class CustomInputUtil extends StatefulWidget {
|
|||
final String? Function(String?)? validator;
|
||||
final bool haveMaxLength;
|
||||
final void Function(String)? onChanged;
|
||||
final List<TextInputFormatter>? inputFormatters;
|
||||
|
||||
CustomInputUtil(
|
||||
{super.key,
|
||||
this.controller,
|
||||
required this.labelText,
|
||||
required this.hintText,
|
||||
required this.suffixIcon,
|
||||
this.autoFocus = false,
|
||||
required this.focusNode,
|
||||
this.onChanged,
|
||||
this.textInputAction = TextInputAction.next,
|
||||
this.keyboardType = TextInputType.text,
|
||||
this.maxLength = 80,
|
||||
this.validator,
|
||||
this.obscureText,
|
||||
required this.haveMaxLength});
|
||||
CustomInputUtil({
|
||||
super.key,
|
||||
this.controller,
|
||||
required this.labelText,
|
||||
required this.hintText,
|
||||
required this.suffixIcon,
|
||||
this.autoFocus = false,
|
||||
required this.focusNode,
|
||||
this.onChanged,
|
||||
this.textInputAction = TextInputAction.next,
|
||||
this.keyboardType = TextInputType.text,
|
||||
this.maxLength = 80,
|
||||
this.validator,
|
||||
this.obscureText,
|
||||
this.inputFormatters,
|
||||
required this.haveMaxLength,
|
||||
});
|
||||
|
||||
@override
|
||||
State<CustomInputUtil> createState() => _CustomInputUtilState();
|
||||
|
@ -152,6 +164,7 @@ class _CustomInputUtilState extends State<CustomInputUtil> {
|
|||
keyboardType: widget.keyboardType,
|
||||
inputFormatters: [
|
||||
LengthLimitingTextInputFormatter(widget.maxLength),
|
||||
if (widget.inputFormatters != null) ...widget.inputFormatters!
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
|
@ -10,7 +10,7 @@ class TabViewUtil extends StatelessWidget {
|
|||
String labelTab1;
|
||||
String labelTab2;
|
||||
final TabController controller;
|
||||
final Function(bool) onEditingChanged;
|
||||
final Function([bool]) onEditingChanged;
|
||||
Widget widget1;
|
||||
Widget widget2;
|
||||
|
||||
|
@ -49,15 +49,17 @@ class TabViewUtil extends StatelessWidget {
|
|||
padding: const EdgeInsets.all(4.0),
|
||||
tabs: [
|
||||
Tab(
|
||||
key: ValueKey('TabView_Tab1'),
|
||||
text: labelTab1,
|
||||
),
|
||||
Tab(
|
||||
key: ValueKey('TabView_Tab2'),
|
||||
text: labelTab2,
|
||||
),
|
||||
],
|
||||
controller: controller,
|
||||
onTap: (i) async {
|
||||
if (i == 1) onEditingChanged(false);
|
||||
onEditingChanged();
|
||||
[() async {}, () async {}][i]();
|
||||
},
|
||||
),
|
||||
|
|
|
@ -209,6 +209,7 @@ class _BottomArrowLinkedLocalsComponentWidgetState
|
|||
return CardItemTemplateComponentWidget(
|
||||
key: ValueKey<String>(local['CLI_NOME']),
|
||||
imagePath: _imagePath(local),
|
||||
icon: null,
|
||||
labelsHashMap: _labelsHashMap(local),
|
||||
statusHashMap: [_statusHashMap(local)],
|
||||
onTapCardItemAction: () async {
|
||||
|
|
|
@ -12,18 +12,74 @@ import 'card_item_template_component_model.dart';
|
|||
|
||||
export 'card_item_template_component_model.dart';
|
||||
|
||||
class FreCardIcon extends StatelessWidget {
|
||||
final double height;
|
||||
final double width;
|
||||
final Icon icon;
|
||||
|
||||
const FreCardIcon({
|
||||
super.key,
|
||||
required this.height,
|
||||
required this.width,
|
||||
required this.icon,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: height,
|
||||
width: width,
|
||||
child: icon,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FreCardPin extends StatelessWidget {
|
||||
final double height;
|
||||
final double width;
|
||||
final Color color;
|
||||
|
||||
const FreCardPin({
|
||||
super.key,
|
||||
required this.height,
|
||||
required this.width,
|
||||
required this.color,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
height: height,
|
||||
width: width,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CardItemTemplateComponentWidget extends StatefulWidget {
|
||||
const CardItemTemplateComponentWidget({
|
||||
super.key,
|
||||
required this.labelsHashMap,
|
||||
required this.statusHashMap,
|
||||
required this.imagePath,
|
||||
this.imagePath,
|
||||
this.icon,
|
||||
this.pin,
|
||||
this.itemWidthFactor = 0.25,
|
||||
required this.onTapCardItemAction,
|
||||
});
|
||||
|
||||
final Map<String, String>? labelsHashMap;
|
||||
final List<Map<String, Color>?> statusHashMap;
|
||||
final String? imagePath;
|
||||
final FreCardIcon? icon;
|
||||
final FreCardPin? pin;
|
||||
final double itemWidthFactor;
|
||||
final Future Function()? onTapCardItemAction;
|
||||
|
||||
@override
|
||||
|
@ -125,9 +181,24 @@ class _CardItemTemplateComponentWidgetState
|
|||
);
|
||||
}
|
||||
|
||||
Widget _generateIcon() {
|
||||
return Column(
|
||||
children: [
|
||||
widget.icon!,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _generatePin() {
|
||||
return widget.pin!;
|
||||
}
|
||||
|
||||
List<Widget> _generateStatus() {
|
||||
double limitedBodyTextSize = LimitedFontSizeUtil.getBodyFontSize(context);
|
||||
|
||||
return statusLinkedHashMap.expand((statusLinked) {
|
||||
log('statusHashMap: ${statusLinked.length}');
|
||||
|
||||
return statusLinked.entries.map((entry) {
|
||||
final text = entry.key;
|
||||
final color = entry.value;
|
||||
|
@ -136,7 +207,7 @@ class _CardItemTemplateComponentWidgetState
|
|||
message: text,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(5),
|
||||
width: MediaQuery.of(context).size.width * 0.25,
|
||||
width: MediaQuery.of(context).size.width * widget.itemWidthFactor,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
|
@ -162,38 +233,50 @@ class _CardItemTemplateComponentWidgetState
|
|||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
if (constraints.maxWidth > 360) {
|
||||
return Row(
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
..._generateLabels(),
|
||||
SizedBox(height: 3),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 4,
|
||||
children: _generateStatus(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [if (widget.pin != null) _generatePin()]),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
..._generateLabels(),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 4,
|
||||
children: _generateStatus(),
|
||||
),
|
||||
]
|
||||
.addToEnd(const SizedBox(height: 5))
|
||||
.divide(const SizedBox(height: 3))
|
||||
.addToStart(const SizedBox(height: 5)),
|
||||
),
|
||||
]
|
||||
.addToEnd(const SizedBox(height: 5))
|
||||
.divide(const SizedBox(height: 1))
|
||||
.addToStart(const SizedBox(height: 5)),
|
||||
),
|
||||
),
|
||||
if (widget.icon != null) _generateIcon(),
|
||||
if (widget.imagePath != null) _generateImage()
|
||||
]
|
||||
.addToEnd(const SizedBox(width: 10))
|
||||
.addToStart(const SizedBox(width: 10)),
|
||||
),
|
||||
if (widget.imagePath != null) _generateImage(),
|
||||
]
|
||||
.addToEnd(const SizedBox(width: 10))
|
||||
.addToStart(const SizedBox(width: 10)),
|
||||
].addToStart(SizedBox(height: 5)).addToEnd(SizedBox(height: 5)),
|
||||
);
|
||||
} else {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (widget.imagePath != null) _generateImage(),
|
||||
if (widget.icon != null) _generateIcon(),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Column(
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:collection';
|
|||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart';
|
||||
import 'package:hub/components/templates_components/details_component/details_component_model.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_util.dart';
|
||||
|
@ -14,6 +15,7 @@ class DetailsComponentWidget extends StatefulWidget {
|
|||
required this.labelsHashMap,
|
||||
required this.statusHashMap,
|
||||
this.imagePath,
|
||||
this.icon,
|
||||
this.onTapCardItemAction,
|
||||
required this.buttons,
|
||||
});
|
||||
|
@ -23,6 +25,7 @@ class DetailsComponentWidget extends StatefulWidget {
|
|||
final String? imagePath;
|
||||
final Future Function()? onTapCardItemAction;
|
||||
final List<Widget>? buttons;
|
||||
final FreCardIcon? icon;
|
||||
|
||||
@override
|
||||
State<DetailsComponentWidget> createState() => _DetailsComponentWidgetState();
|
||||
|
@ -97,6 +100,16 @@ class _DetailsComponentWidgetState extends State<DetailsComponentWidget> {
|
|||
useOldImageOnUrlChange: true,
|
||||
),
|
||||
),
|
||||
if (widget.icon != null && widget.icon != '')
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width * 0.3,
|
||||
height: MediaQuery.of(context).size.width * 0.3,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: widget.icon!,
|
||||
),
|
||||
SizedBox(height: MediaQuery.of(context).size.height * 0.03),
|
||||
Row(
|
||||
children: statusLinkedHashMap.expand((linkedHashMap) {
|
||||
|
@ -111,6 +124,7 @@ class _DetailsComponentWidgetState extends State<DetailsComponentWidget> {
|
|||
autofocus: false,
|
||||
canRequestFocus: false,
|
||||
readOnly: true,
|
||||
initialValue: item.key,
|
||||
obscureText: false,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
|
@ -122,7 +136,7 @@ class _DetailsComponentWidgetState extends State<DetailsComponentWidget> {
|
|||
),
|
||||
filled: true,
|
||||
fillColor: item.value,
|
||||
labelText: item.key,
|
||||
// labelText: item.key,
|
||||
labelStyle: FlutterFlowTheme.of(context)
|
||||
.labelMedium
|
||||
.override(
|
||||
|
@ -161,14 +175,16 @@ class _DetailsComponentWidgetState extends State<DetailsComponentWidget> {
|
|||
),
|
||||
),
|
||||
style: FlutterFlowTheme.of(context)
|
||||
.bodyMedium
|
||||
.labelMedium
|
||||
.override(
|
||||
fontFamily: FlutterFlowTheme.of(context)
|
||||
.bodyMediumFamily,
|
||||
.labelMediumFamily,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: FlutterFlowTheme.of(context).info,
|
||||
letterSpacing: 0.0,
|
||||
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||
FlutterFlowTheme.of(context).bodyMediumFamily,
|
||||
FlutterFlowTheme.of(context)
|
||||
.labelMediumFamily,
|
||||
),
|
||||
fontSize: limitedBodyFontSize,
|
||||
),
|
||||
|
|
|
@ -34,6 +34,7 @@ class RegisiterVistorTemplateComponentWidget extends StatefulWidget {
|
|||
class _RegisiterVistorTemplateComponentWidgetState
|
||||
extends State<RegisiterVistorTemplateComponentWidget> {
|
||||
late RegisiterVistorTemplateComponentModel _model;
|
||||
final bool _isLoading = false;
|
||||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
bool _isVisitorRegistered = false;
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
class DeadCode {
|
||||
final String? desc;
|
||||
|
||||
const DeadCode([this.desc = '']);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export 'anotations.dart';
|
|
@ -11,7 +11,7 @@ import 'package:hub/features/storage/index.dart';
|
|||
|
||||
import 'package:hub/shared/utils/log_util.dart';
|
||||
import 'package:hub/shared/utils/validator_util.dart';
|
||||
import 'package:hub/shared/widgets/widgets.dart';
|
||||
import 'package:hub/shared/widgets.dart';
|
||||
|
||||
import '/flutter_flow/flutter_flow_util.dart';
|
||||
import 'api_manager.dart';
|
||||
|
@ -75,6 +75,187 @@ class FreAccessWSGlobal extends Api {
|
|||
@override
|
||||
GetLicense getLicense = GetLicense();
|
||||
static GetProvSchedules getProvSchedules = GetProvSchedules();
|
||||
static RegisterVehicle registerVehicle = RegisterVehicle();
|
||||
static UpdateVehicle updateVehicle = UpdateVehicle();
|
||||
static DeleteVehicle deleteVehicle = DeleteVehicle();
|
||||
static CancelDeleteVehicle cancelDelete = CancelDeleteVehicle();
|
||||
static GetPDF getPDF = GetPDF();
|
||||
static GetCategories getCategories = GetCategories();
|
||||
static GetDocuments getDocuments = GetDocuments();
|
||||
}
|
||||
|
||||
class CancelDeleteVehicle {
|
||||
Future<ApiCallResponse> call({
|
||||
required final int vehicleId,
|
||||
required final String licensePlate,
|
||||
required final String model,
|
||||
required final String color,
|
||||
}) async {
|
||||
final String baseUrl = FreAccessWSGlobal.getBaseUrl();
|
||||
final String devUUID =
|
||||
(await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? '';
|
||||
final String userUUID =
|
||||
(await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? '';
|
||||
final String cliID =
|
||||
(await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? '';
|
||||
const String atividade = 'cancelDeleteVehicleRequest';
|
||||
|
||||
return await ApiManager.instance.makeApiCall(
|
||||
callName: atividade,
|
||||
apiUrl: '$baseUrl/processRequest.php',
|
||||
callType: ApiCallType.POST,
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
params: {
|
||||
'devUUID': devUUID,
|
||||
'userUUID': userUUID,
|
||||
'cliID': cliID,
|
||||
'atividade': atividade,
|
||||
'vehicleId': vehicleId,
|
||||
'licensePlate': licensePlate,
|
||||
'model': model,
|
||||
'color': color
|
||||
},
|
||||
bodyType: BodyType.X_WWW_FORM_URL_ENCODED,
|
||||
returnBody: true,
|
||||
encodeBodyUtf8: false,
|
||||
decodeUtf8: false,
|
||||
cache: false,
|
||||
isStreamingApi: false,
|
||||
alwaysAllowBody: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DeleteVehicle {
|
||||
Future<ApiCallResponse> call({
|
||||
required final int vehicleId,
|
||||
required final String licensePlate,
|
||||
required final String model,
|
||||
required final String color,
|
||||
}) async {
|
||||
final String baseUrl = FreAccessWSGlobal.getBaseUrl();
|
||||
final String devUUID =
|
||||
(await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? '';
|
||||
final String userUUID =
|
||||
(await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? '';
|
||||
final String cliID =
|
||||
(await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? '';
|
||||
const String atividade = 'deleteVehicle';
|
||||
|
||||
return await ApiManager.instance.makeApiCall(
|
||||
callName: 'deleteVehicle',
|
||||
apiUrl: '$baseUrl/processRequest.php',
|
||||
callType: ApiCallType.POST,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
params: {
|
||||
'devUUID': devUUID,
|
||||
'userUUID': userUUID,
|
||||
'cliID': cliID,
|
||||
'atividade': atividade,
|
||||
'vehicleId': vehicleId,
|
||||
'licensePlate': licensePlate,
|
||||
'model': model,
|
||||
'color': color
|
||||
},
|
||||
bodyType: BodyType.X_WWW_FORM_URL_ENCODED,
|
||||
returnBody: true,
|
||||
encodeBodyUtf8: false,
|
||||
decodeUtf8: false,
|
||||
cache: false,
|
||||
isStreamingApi: false,
|
||||
alwaysAllowBody: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RegisterVehicle {
|
||||
Future<ApiCallResponse> call({
|
||||
final String? licensePlate,
|
||||
final String? color,
|
||||
final String? model,
|
||||
}) async {
|
||||
final String baseUrl = FreAccessWSGlobal.getBaseUrl();
|
||||
|
||||
final String devUUID =
|
||||
(await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? '';
|
||||
final String userUUID =
|
||||
(await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? '';
|
||||
final String cliID =
|
||||
(await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? '';
|
||||
const String atividade = 'insertVehicle';
|
||||
|
||||
return await ApiManager.instance.makeApiCall(
|
||||
callName: 'registerVehicle',
|
||||
apiUrl: '$baseUrl/processRequest.php',
|
||||
callType: ApiCallType.POST,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
params: {
|
||||
'devUUID': devUUID,
|
||||
'userUUID': userUUID,
|
||||
'cliID': cliID,
|
||||
'atividade': atividade,
|
||||
'licensePlate': licensePlate,
|
||||
'color': color,
|
||||
'model': model,
|
||||
},
|
||||
bodyType: BodyType.X_WWW_FORM_URL_ENCODED,
|
||||
returnBody: true,
|
||||
encodeBodyUtf8: false,
|
||||
decodeUtf8: false,
|
||||
cache: false,
|
||||
isStreamingApi: false,
|
||||
alwaysAllowBody: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UpdateVehicle {
|
||||
Future<ApiCallResponse> call({
|
||||
required final int vehicleId,
|
||||
final String? licensePlate,
|
||||
final String? color,
|
||||
final String? model,
|
||||
}) async {
|
||||
final String baseUrl = FreAccessWSGlobal.getBaseUrl();
|
||||
final String devUUID =
|
||||
(await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? '';
|
||||
final String userUUID =
|
||||
(await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? '';
|
||||
final String cliID =
|
||||
(await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? '';
|
||||
const String atividade = 'updateVehicleToInsertRequest';
|
||||
|
||||
return await ApiManager.instance.makeApiCall(
|
||||
callName: 'updateVehicle',
|
||||
apiUrl: '$baseUrl/processRequest.php',
|
||||
callType: ApiCallType.POST,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
params: {
|
||||
'devUUID': devUUID,
|
||||
'userUUID': userUUID,
|
||||
'cliID': cliID,
|
||||
'atividade': atividade,
|
||||
'licensePlate': licensePlate,
|
||||
'color': color,
|
||||
'model': model,
|
||||
'vehicleId': vehicleId
|
||||
},
|
||||
bodyType: BodyType.X_WWW_FORM_URL_ENCODED,
|
||||
returnBody: true,
|
||||
encodeBodyUtf8: false,
|
||||
decodeUtf8: false,
|
||||
cache: false,
|
||||
isStreamingApi: false,
|
||||
alwaysAllowBody: false,
|
||||
);
|
||||
}
|
||||
|
||||
static GetCategories getCategories = GetCategories();
|
||||
static GetDocuments getDocuments = GetDocuments();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:core';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
|
@ -496,11 +495,11 @@ class ApiManager {
|
|||
result = ApiCallResponse(null, {}, -1, exception: e);
|
||||
}
|
||||
|
||||
log('API Call: $callName');
|
||||
log('URL: $apiUrl');
|
||||
log('Headers: $headers');
|
||||
log('Params: $params');
|
||||
log('Response: ${result.jsonBody}');
|
||||
print('API Call: $callName');
|
||||
print('URL: $apiUrl');
|
||||
print('Headers: $headers');
|
||||
print('Params: $params');
|
||||
print('Response: ${result.jsonBody}');
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:easy_debounce/easy_debounce.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hub/components/templates_components/details_component/details_component_widget.dart';
|
||||
import 'package:hub/features/backend/index.dart';
|
||||
import 'package:hub/flutter_flow/index.dart';
|
||||
import 'package:hub/shared/extensions/index.dart';
|
||||
import 'package:hub/shared/utils/index.dart';
|
||||
import 'package:hub/shared/widgets/widgets.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:hub/shared/extensions.dart';
|
||||
import 'package:hub/shared/utils.dart';
|
||||
import 'package:hub/shared/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_rx_bloc/flutter_rx_bloc.dart';
|
||||
|
||||
import 'package:rx_bloc/rx_bloc.dart';
|
||||
import 'package:rxdart/rxdart.dart' as rx;
|
||||
|
||||
|
@ -134,7 +131,7 @@ class DocumentManagerScreen extends StatelessScreen {
|
|||
children: [
|
||||
Expanded(
|
||||
child: EnhancedListView<Document, SearchField, Category, Query>(
|
||||
key: model.vehicleScreenManager,
|
||||
key: model.enhancedListViewKey,
|
||||
controller: controller,
|
||||
repository: repository,
|
||||
),
|
||||
|
@ -167,15 +164,14 @@ class _DocumentViewerScreenState extends ScreenState<DocumentViewerScreen> {
|
|||
super.initState();
|
||||
}
|
||||
|
||||
backAction() => widget.bloc.events.unselectDocument();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final String title = widget.doc.$1.description;
|
||||
final theme = FlutterFlowTheme.of(context);
|
||||
final locale = FFLocalizations.of(context);
|
||||
final Color color = widget.doc.$1.category.color;
|
||||
;
|
||||
|
||||
backAction() => widget.bloc.events.unselectDocument();
|
||||
infoAction() => DetailsComponentWidget(
|
||||
buttons: [],
|
||||
statusHashMap: [
|
||||
|
@ -230,6 +226,7 @@ class _DocumentViewerScreenState extends ScreenState<DocumentViewerScreen> {
|
|||
return ReadView(
|
||||
title: widget.doc.$1.description,
|
||||
url: widget.doc.$2.toString(),
|
||||
onError: backAction,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -243,28 +240,25 @@ class DocumentModel extends FlutterFlowModel<DocumentPage> {
|
|||
DocumentModel(this.bloc);
|
||||
|
||||
late EnhancedListViewKey<Document, SearchField, Category, Query>
|
||||
vehicleScreenManager;
|
||||
late DocumentKey vehicleScreenViewer;
|
||||
late PagingController<int, Document> _pagingController;
|
||||
enhancedListViewKey;
|
||||
late EnhancedCarouselViewKey<Category> carouselViewKey;
|
||||
|
||||
late bool categoryIsSelected;
|
||||
late DocumentKey vehicleScreenViewer;
|
||||
|
||||
/// ------------
|
||||
|
||||
@override
|
||||
void initState(BuildContext context) {
|
||||
vehicleScreenManager =
|
||||
enhancedListViewKey =
|
||||
EnhancedListViewKey<Document, SearchField, Category, Query>();
|
||||
carouselViewKey = EnhancedCarouselViewKey<Category>();
|
||||
vehicleScreenViewer = DocumentKey();
|
||||
_pagingController = PagingController<int, Document>(firstPageKey: 1);
|
||||
|
||||
categoryIsSelected = false;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pagingController.dispose();
|
||||
vehicleScreenManager.currentState?.dispose();
|
||||
enhancedListViewKey.currentState?.dispose();
|
||||
carouselViewKey.currentState?.dispose();
|
||||
vehicleScreenViewer.currentState?.dispose();
|
||||
}
|
||||
|
||||
|
@ -338,14 +332,17 @@ class DocumentModel extends FlutterFlowModel<DocumentPage> {
|
|||
Widget itemFooterBuilder<T extends Category>(
|
||||
Future<List<T?>> Function() fetchData) =>
|
||||
Builder(builder: (context) {
|
||||
CategoryComponent categoryItemBuilder<T>(T? item) {
|
||||
return CategoryComponent(category: item! as Category);
|
||||
CategoryComponent categoryItemBuilder<T>(T? item, bool isSelected) {
|
||||
return CategoryComponent(
|
||||
category: item! as Category, isSelected: isSelected);
|
||||
}
|
||||
|
||||
return EnhancedCarouselView<T>(
|
||||
key: carouselViewKey,
|
||||
dataProvider: fetchData,
|
||||
itemBuilder: categoryItemBuilder,
|
||||
filter: filterByCategory<T>,
|
||||
showIndicator: true,
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -458,18 +455,14 @@ class DocumentModel extends FlutterFlowModel<DocumentPage> {
|
|||
/// [Filter]
|
||||
|
||||
void filterBySearchBar<T>(T query, BuildContext context) {
|
||||
final key = vehicleScreenManager.currentState;
|
||||
return key?.filterBodyItems(query);
|
||||
final enhancedListViewState = enhancedListViewKey.currentState;
|
||||
return enhancedListViewState?.filterBodyItems(query);
|
||||
}
|
||||
|
||||
void filterByCategory<T>(T query, BuildContext context) {
|
||||
final key = vehicleScreenManager.currentState;
|
||||
void filterByCategory<T>(T? query, BuildContext context) {
|
||||
final enhancedListViewState = enhancedListViewKey.currentState;
|
||||
|
||||
categoryIsSelected
|
||||
? key?.filterBodyItems(null)
|
||||
: key?.filterBodyItems(query);
|
||||
|
||||
categoryIsSelected = !categoryIsSelected;
|
||||
enhancedListViewState?.filterBodyItems(query);
|
||||
}
|
||||
|
||||
/// [Exception]
|
||||
|
@ -662,64 +655,71 @@ class DocumentComponent extends StatelessComponent {
|
|||
final description = document.description;
|
||||
final title = document.category.title;
|
||||
const double size = 20;
|
||||
final date = ValidatorUtil.toLocalDateTime(
|
||||
'yyyy-MM-dd',
|
||||
document.updatedAt,
|
||||
);
|
||||
|
||||
return InkWell(
|
||||
onTap: () => onPressed(document, context),
|
||||
enableFeedback: true,
|
||||
overlayColor: WidgetStateProperty.all<Color>(primaryColor),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: SizedBox(
|
||||
height: boxHeight,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
spacing: size,
|
||||
children: [
|
||||
// const SizedBox(width: 10),
|
||||
Icon(icon, color: color),
|
||||
return Tooltip(
|
||||
message: description,
|
||||
child: InkWell(
|
||||
onTap: () => onPressed(document, context),
|
||||
enableFeedback: true,
|
||||
overlayColor: WidgetStateProperty.all<Color>(primaryColor),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: SizedBox(
|
||||
height: boxHeight,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
spacing: size,
|
||||
children: [
|
||||
// const SizedBox(width: 10),
|
||||
Icon(icon, color: color),
|
||||
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Tooltip(
|
||||
message: description,
|
||||
child: AutoText(
|
||||
description,
|
||||
style: textStyleMajor,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Tooltip(
|
||||
message: description,
|
||||
child: AutoText(
|
||||
description,
|
||||
style: textStyleMajor,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
AutoText(
|
||||
ValidatorUtil.toLocalDateTime(
|
||||
'yyyy-MM-dd',
|
||||
document.updatedAt,
|
||||
Tooltip(
|
||||
message: date,
|
||||
child: AutoText(
|
||||
date,
|
||||
style: textStyleMinor,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
style: textStyleMinor,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
_buildTooltip(title, color, context, constraints),
|
||||
],
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
_buildTooltip(title, color, context, constraints),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// const SizedBox(width: 10),
|
||||
Center(
|
||||
child: Icon(
|
||||
Icons.arrow_right,
|
||||
color: primaryText,
|
||||
// const SizedBox(width: 10),
|
||||
Center(
|
||||
child: Icon(
|
||||
Icons.arrow_right,
|
||||
color: primaryText,
|
||||
),
|
||||
),
|
||||
),
|
||||
] //
|
||||
.addToStart(space)
|
||||
.addToEnd(space),
|
||||
] //
|
||||
.addToStart(space)
|
||||
.addToEnd(space),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -740,15 +740,19 @@ class DocumentComponent extends StatelessComponent {
|
|||
|
||||
class CategoryComponent extends StatelessComponent {
|
||||
final Category category;
|
||||
final bool isSelected;
|
||||
|
||||
const CategoryComponent({
|
||||
super.key,
|
||||
required this.category,
|
||||
this.isSelected = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final backgroundTheme = FlutterFlowTheme.of(context).primaryBackground;
|
||||
final textTheme = FlutterFlowTheme.of(context).primaryText;
|
||||
final color = isSelected ? category.color.highlight : category.color;
|
||||
return ColoredBox(
|
||||
color: backgroundTheme,
|
||||
child: Padding(
|
||||
|
@ -758,7 +762,7 @@ class CategoryComponent extends StatelessComponent {
|
|||
Container(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
decoration: BoxDecoration(
|
||||
color: category.color,
|
||||
color: color,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
|
@ -771,7 +775,7 @@ class CategoryComponent extends StatelessComponent {
|
|||
Text(
|
||||
category.title,
|
||||
style: TextStyle(
|
||||
color: category.color,
|
||||
color: textTheme,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
|
|
|
@ -46,12 +46,15 @@ class _AccessHistoryState extends State<AccessHistoryScreen> {
|
|||
selectedTypeSubject.listen((value) {});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_model = createModel(context, () => AcessHistoryPageModel());
|
||||
_accessFuture = fetchAccessHistoryService();
|
||||
|
||||
|
||||
_scrollController = ScrollController()
|
||||
..addListener(() {
|
||||
if (_scrollController.position.atEdge &&
|
||||
|
|
|
@ -34,20 +34,13 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource {
|
|||
try {
|
||||
final GetLocalsCall callback = FreAccessWSGlobal.getLocalsCall;
|
||||
var response = await callback.call();
|
||||
final bool? isError = response.jsonBody['error'];
|
||||
if (response.jsonBody == null) return;
|
||||
|
||||
final bool? isError = response.jsonBody['error'];
|
||||
if (isError == true) {
|
||||
LocalUtil.handleError(context, response.jsonBody['error_msg']);
|
||||
return;
|
||||
}
|
||||
if (response.jsonBody == null) {
|
||||
// final String errorMsg = FFLocalizations.of(context).getVariableText(
|
||||
// enText: 'Verify your connection',
|
||||
// ptText: 'Verifique sua conexão',
|
||||
// );
|
||||
// await DialogUtil.error(context, errorMsg).whenComplete(() async => await selectLocal(context, response));
|
||||
return;
|
||||
}
|
||||
|
||||
final List<dynamic> locals = response.jsonBody['locais'] ?? [];
|
||||
final bool isEmpty = locals.isEmpty;
|
||||
|
|
|
@ -80,7 +80,6 @@ class LocalsRepositoryImpl implements LocalsRepository {
|
|||
}
|
||||
} else {
|
||||
log('_handleLocal -> Local selected');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -115,6 +115,9 @@ class LocalUtil {
|
|||
.set(LocalsStorageKey.whatsapp.key, jsonBody['whatsapp'] ?? false);
|
||||
await StorageHelper().set(
|
||||
LocalsStorageKey.provisional.key, jsonBody['provisional'] ?? false);
|
||||
await StorageHelper().set(LocalsStorageKey.vehicleAutoApproval.key,
|
||||
jsonBody['vehicleAutoApproval'] ?? false);
|
||||
|
||||
await StorageHelper().set(
|
||||
LocalsStorageKey.pets.key,
|
||||
jsonBody['pet'] ?? false,
|
||||
|
@ -141,6 +144,13 @@ class LocalUtil {
|
|||
jsonBody['petAmountRegister']?.toString().isEmpty ?? true
|
||||
? '0'
|
||||
: jsonBody['petAmountRegister'].toString());
|
||||
|
||||
await StorageHelper().set(
|
||||
LocalsStorageKey.vehicleAmountRegister.key,
|
||||
jsonBody['vehicleAmountRegister']?.toString().isEmpty ?? true
|
||||
? '0'
|
||||
: jsonBody['vehicleAmountRegister'].toString());
|
||||
|
||||
await StorageHelper().set(ProfileStorageKey.userName.key,
|
||||
jsonBody['visitado']['VDO_NOME'] ?? '');
|
||||
await StorageHelper().set(ProfileStorageKey.userEmail.key,
|
||||
|
|
|
@ -17,10 +17,6 @@ abstract class MenuLocalDataSource {
|
|||
|
||||
Future<void> handleMenu(EnumMenuItem item, EnumDisplay display, MenuEntry opt,
|
||||
List<MenuItem?> entries);
|
||||
|
||||
Future<bool> processStartDate(String startDate, MenuEntry entry);
|
||||
|
||||
Future<bool> processExpirationDate(String expirationDate, MenuEntry entry);
|
||||
}
|
||||
|
||||
class MenuLocalDataSourceImpl implements MenuLocalDataSource {
|
||||
|
@ -92,30 +88,4 @@ class MenuLocalDataSourceImpl implements MenuLocalDataSource {
|
|||
log('Error processing display for module ${opt.key}: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> processStartDate(String startDate, MenuEntry opt) async {
|
||||
try {
|
||||
if (startDate.isEmpty) return true;
|
||||
final start = DateTime.tryParse(startDate);
|
||||
if (start == null) return false;
|
||||
return DateTime.now().isAfter(start);
|
||||
} catch (e) {
|
||||
log('Error processing start date for module ${opt.key}: $e');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> processExpirationDate(
|
||||
String expirationDate, MenuEntry opt) async {
|
||||
try {
|
||||
if (expirationDate.isEmpty) return false;
|
||||
final expiration = DateTime.tryParse(expirationDate);
|
||||
return expiration != null && DateTime.now().isAfter(expiration);
|
||||
} catch (e) {
|
||||
log('Error processing expiration date for module ${opt.key}: $e');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:hub/features/menu/index.dart';
|
|||
import 'package:hub/features/module/index.dart';
|
||||
import 'package:hub/features/storage/index.dart';
|
||||
import 'package:hub/flutter_flow/custom_functions.dart';
|
||||
import 'package:hub/shared/utils/datetime_util.dart';
|
||||
|
||||
class MenuRepositoryImpl implements MenuRepository {
|
||||
final MenuLocalDataSource menuDataSource = MenuLocalDataSourceImpl();
|
||||
|
@ -25,10 +26,9 @@ class MenuRepositoryImpl implements MenuRepository {
|
|||
final display = EnumDisplay.fromString(licenseMap['display']);
|
||||
final startDate = licenseMap['startDate'] ?? '';
|
||||
final expirationDate = licenseMap['expirationDate'] ?? '';
|
||||
final isStarted =
|
||||
await menuDataSource.processStartDate(startDate, entry);
|
||||
final isStarted = await DateTimeUtil.processStartDate(startDate);
|
||||
final isExpired =
|
||||
await menuDataSource.processExpirationDate(expirationDate, entry);
|
||||
await DateTimeUtil.processExpirationDate(expirationDate);
|
||||
if (isStarted && !isExpired) {
|
||||
await menuDataSource.handleMenu(menuItem, display, entry, entries);
|
||||
}
|
||||
|
|
|
@ -76,6 +76,16 @@ class MenuEntry implements BaseModule {
|
|||
route: '/residentsOnThePropertyPage',
|
||||
types: [MenuEntryType.Property],
|
||||
),
|
||||
// MenuEntry(
|
||||
// key: 'FRE-HUB-VEHICLES-MANAGER',
|
||||
// icon: Icons.directions_car,
|
||||
// name: FFLocalizations.of(navigatorKey.currentContext!).getVariableText(
|
||||
// ptText: 'Veículos',
|
||||
// enText: 'Vehicles',
|
||||
// ),
|
||||
// route: '/vehiclesOnThePropertyPage',
|
||||
// types: [MenuEntryType.Property],
|
||||
// ),
|
||||
MenuEntry(
|
||||
key: 'FRE-HUB-VEHICLES',
|
||||
icon: Icons.directions_car,
|
||||
|
|
|
@ -94,7 +94,7 @@ class LicenseLocalDataSourceImpl implements LicenseLocalDataSource {
|
|||
Future<bool> isNewVersion() async {
|
||||
final String? reponse =
|
||||
await StorageHelper().get(LocalsStorageKey.isNewVersion.key);
|
||||
final bool isNewVersion = reponse.toBoolean();
|
||||
final bool isNewVersion = reponse.toBoolean;
|
||||
return isNewVersion;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ enum LicenseKeys {
|
|||
access('FRE-HUB-ACCESS'),
|
||||
openedVisits('FRE-HUB-OPENED-VISITS'),
|
||||
vehicles('FRE-HUB-VEHICLES'),
|
||||
vehiclesManager('FRE-HUB-VEHICLES-MANAGER'),
|
||||
residents('FRE-HUB-RESIDENTS'),
|
||||
about('FRE-HUB-ABOUT-SYSTEM'),
|
||||
pets('FRE-HUB-PETS'),
|
||||
|
@ -63,7 +64,7 @@ class License {
|
|||
static Future<String> _precessWpp() async {
|
||||
final bool whatsapp = await StorageHelper()
|
||||
.get(LocalsStorageKey.whatsapp.key)
|
||||
.then((v) => v.toBoolean());
|
||||
.then((v) => v.toBoolean);
|
||||
if (whatsapp)
|
||||
return ModuleStatus.active.key;
|
||||
else
|
||||
|
@ -73,7 +74,7 @@ class License {
|
|||
static Future<String> _processProvisional() async {
|
||||
final bool provisional = await StorageHelper()
|
||||
.get(LocalsStorageKey.provisional.key)
|
||||
.then((v) => v.toBoolean());
|
||||
.then((v) => v.toBoolean);
|
||||
if (provisional)
|
||||
return ModuleStatus.active.key;
|
||||
else
|
||||
|
@ -83,7 +84,7 @@ class License {
|
|||
static Future<String> _processPets() async {
|
||||
final bool pets = await StorageHelper()
|
||||
.get(LocalsStorageKey.pets.key)
|
||||
.then((v) => v.toBoolean());
|
||||
.then((v) => v.toBoolean);
|
||||
if (pets)
|
||||
return ModuleStatus.active.key;
|
||||
else
|
||||
|
@ -163,6 +164,13 @@ class License {
|
|||
startDate: '',
|
||||
quantity: 0,
|
||||
),
|
||||
Module(
|
||||
key: LicenseKeys.vehicles.value,
|
||||
display: ModuleStatus.inactive.key,
|
||||
expirationDate: '',
|
||||
startDate: '',
|
||||
quantity: 0,
|
||||
),
|
||||
Module(
|
||||
key: LicenseKeys.residents.value,
|
||||
display: isNewVersionWithModule
|
||||
|
|
|
@ -34,7 +34,9 @@ enum LocalsStorageKey implements DatabaseStorageKey {
|
|||
panic('fre_panic'),
|
||||
person('fre_person'),
|
||||
requestOSNotification('fre_requestOSnotification'),
|
||||
isNewVersion('fre_isNewVersion');
|
||||
isNewVersion('fre_isNewVersion'),
|
||||
vehicleAutoApproval('fre_vehicleAutoApproval'),
|
||||
vehicleAmountRegister('fre_vehicleAmountRegister');
|
||||
|
||||
final String key;
|
||||
|
||||
|
|
|
@ -529,7 +529,7 @@ void setAppLanguage(BuildContext context, String language) =>
|
|||
void setDarkModeSetting(BuildContext context, ThemeMode themeMode) =>
|
||||
App.of(context).setThemeMode(themeMode);
|
||||
|
||||
void showSnackbar(
|
||||
void showSnackbarMessenger(
|
||||
BuildContext context,
|
||||
String message,
|
||||
bool error, {
|
||||
|
@ -537,38 +537,47 @@ void showSnackbar(
|
|||
int duration = 4,
|
||||
}) {
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Row(
|
||||
children: [
|
||||
if (loading)
|
||||
Padding(
|
||||
padding: const EdgeInsetsDirectional.only(end: 10.0),
|
||||
child: SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(
|
||||
color: FlutterFlowTheme.of(context).info,
|
||||
),
|
||||
ScaffoldMessenger.of(context)
|
||||
.showSnackBar(showSnackbar(context, message, error));
|
||||
}
|
||||
|
||||
SnackBar showSnackbar(
|
||||
BuildContext context,
|
||||
String message,
|
||||
bool error, {
|
||||
bool loading = false,
|
||||
int duration = 4,
|
||||
}) {
|
||||
return SnackBar(
|
||||
content: Row(
|
||||
children: [
|
||||
if (loading)
|
||||
Padding(
|
||||
padding: const EdgeInsetsDirectional.only(end: 10.0),
|
||||
child: SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(
|
||||
color: FlutterFlowTheme.of(context).info,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
message,
|
||||
style: TextStyle(
|
||||
color: FlutterFlowTheme.of(context).info,
|
||||
fontSize: LimitedFontSizeUtil.getBodyFontSize(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
duration: Duration(seconds: duration),
|
||||
backgroundColor: error
|
||||
? FlutterFlowTheme.of(context).error
|
||||
: FlutterFlowTheme.of(context).success,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
Text(
|
||||
message,
|
||||
style: TextStyle(
|
||||
color: FlutterFlowTheme.of(context).info,
|
||||
fontSize: LimitedFontSizeUtil.getBodyFontSize(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
duration: Duration(seconds: duration),
|
||||
backgroundColor: error
|
||||
? FlutterFlowTheme.of(context).error
|
||||
: FlutterFlowTheme.of(context).success,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -198,7 +198,7 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) {
|
|||
FFRoute(
|
||||
name: 'vehiclesOnThePropertyPage',
|
||||
path: '/vehiclesOnThePropertyPage',
|
||||
builder: (context, params) => const VehicleOnTheProperty()),
|
||||
builder: (context, params) => const VehiclePage()),
|
||||
FFRoute(
|
||||
name: 'receptionPage',
|
||||
path: '/receptionPage',
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:app_tracking_transparency/app_tracking_transparency.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
|
@ -13,23 +14,33 @@ import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
|||
import 'package:hub/flutter_flow/flutter_flow_util.dart';
|
||||
import 'package:hub/flutter_flow/nav/nav.dart';
|
||||
|
||||
Future<void> initializeApp() async {
|
||||
Future<void> initializeBindings() async {
|
||||
// await _initializeTracking();
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await _initializeTracking();
|
||||
await _initializeAppTrackingTransparency();
|
||||
await StorageHelper().init();
|
||||
|
||||
await _initializeFirebase();
|
||||
await _initializeNotificationService();
|
||||
_initializeUrlStrategy();
|
||||
_initializeSystemSettings();
|
||||
await _initializeUrlStrategy();
|
||||
await _initializeSystemSettings();
|
||||
await _initializeFlutterFlow();
|
||||
await _initializeNav();
|
||||
}
|
||||
|
||||
Future<void> _initializeTracking() async {
|
||||
log('Requesting tracking authorization...');
|
||||
await AppTrackingTransparency.requestTrackingAuthorization();
|
||||
log('Tracking authorization requested');
|
||||
Future<void> _initializeAppTrackingTransparency() async {
|
||||
if (Platform.isIOS) {
|
||||
final status = await AppTrackingTransparency.trackingAuthorizationStatus;
|
||||
if (status == TrackingStatus.notDetermined) {
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
final request =
|
||||
await AppTrackingTransparency.requestTrackingAuthorization();
|
||||
if (request == TrackingStatus.authorized) {
|
||||
log('Tracking authorized');
|
||||
} else {
|
||||
log('Tracking not authorized');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _initializeFirebase() async {
|
||||
|
@ -44,19 +55,21 @@ Future<void> _initializeNotificationService() async {
|
|||
log('Notification Service initialized');
|
||||
}
|
||||
|
||||
void _initializeUrlStrategy() {
|
||||
log('Initializing URL Strategy...');
|
||||
Future<void> _initializeUrlStrategy() async {
|
||||
print('Initializing URL Strategy...');
|
||||
setUrlStrategy(PathUrlStrategy());
|
||||
log('URL Strategy initialized');
|
||||
}
|
||||
|
||||
Future<void> _initializeSystemSettings() async {
|
||||
log('Initializing System Settings...');
|
||||
final crashlyticsInstance = FirebaseCrashlytics.instance;
|
||||
print('Initializing System Settings...');
|
||||
|
||||
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||
final crashlyticsInstance = FirebaseCrashlytics.instance;
|
||||
|
||||
if (kDebugMode) {
|
||||
log('Debug mode');
|
||||
print('Debug mode');
|
||||
await crashlyticsInstance.setCrashlyticsCollectionEnabled(false);
|
||||
} else {
|
||||
log('Release mode');
|
||||
|
||||
|
@ -72,10 +85,22 @@ Future<void> _initializeSystemSettings() async {
|
|||
// }
|
||||
|
||||
await crashlyticsInstance.setCrashlyticsCollectionEnabled(true);
|
||||
// if (crashlyticsInstance.isCrashlyticsCollectionEnabled) {
|
||||
FlutterError.onError = crashlyticsInstance.recordFlutterError;
|
||||
log('Crashlytics enabled');
|
||||
// }
|
||||
if (crashlyticsInstance.isCrashlyticsCollectionEnabled) {
|
||||
// Configura o tratamento de erros não capturados
|
||||
FlutterError.onError = crashlyticsInstance.recordFlutterError;
|
||||
|
||||
crashlyticsInstance.checkForUnsentReports().then((unsentReports) {
|
||||
if (unsentReports) {
|
||||
crashlyticsInstance.sendUnsentReports();
|
||||
print('Existem relatórios de falhas não enviados.');
|
||||
} else {
|
||||
print('Todos os relatórios de falhas foram enviados.');
|
||||
}
|
||||
}).catchError((error) {
|
||||
print('Erro ao verificar ou enviar relatórios não enviados: $error');
|
||||
});
|
||||
}
|
||||
print('Crashlytics enabled');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'dart:async';
|
|||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:app_tracking_transparency/app_tracking_transparency.dart';
|
||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
|
@ -31,7 +32,7 @@ class CustomScrollBehavior extends MaterialScrollBehavior {
|
|||
}
|
||||
|
||||
void main() async {
|
||||
await initializeApp();
|
||||
await initializeBindings();
|
||||
runApp(const ProviderScope(child: App()));
|
||||
FirebaseMessaging.onBackgroundMessage(_backgroundHandlerMessage);
|
||||
}
|
||||
|
@ -147,7 +148,7 @@ class _AppState extends State<App> {
|
|||
void initState() {
|
||||
super.initState();
|
||||
|
||||
FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
|
||||
// FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
|
||||
_appStateNotifier = AppStateNotifier.instance;
|
||||
_router = createRouter(_appStateNotifier);
|
||||
Future.delayed(
|
||||
|
|
|
@ -299,7 +299,7 @@ class _LiberationHistoryWidgetState extends State<LiberationHistoryWidget> {
|
|||
)
|
||||
.then((message) {
|
||||
if (message != null || message != '') {
|
||||
showSnackbar(
|
||||
showSnackbarMessenger(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
enText: 'Successfully resolved visit',
|
||||
|
@ -308,7 +308,7 @@ class _LiberationHistoryWidgetState extends State<LiberationHistoryWidget> {
|
|||
false,
|
||||
);
|
||||
} else {
|
||||
showSnackbar(context, message, true);
|
||||
showSnackbarMessenger(context, message, true);
|
||||
}
|
||||
}).whenComplete(() {
|
||||
safeSetState(() {
|
||||
|
|
|
@ -478,7 +478,7 @@ class PetsPageModel extends FlutterFlowModel<PetsPageWidget> {
|
|||
context.pop(value);
|
||||
|
||||
if (value == false) {
|
||||
showSnackbar(
|
||||
showSnackbarMessenger(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Erro ao excluir pet',
|
||||
|
@ -487,7 +487,7 @@ class PetsPageModel extends FlutterFlowModel<PetsPageWidget> {
|
|||
true,
|
||||
);
|
||||
} else if (value == true) {
|
||||
showSnackbar(
|
||||
showSnackbarMessenger(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
enText: 'Success deleting pet',
|
||||
|
@ -498,7 +498,7 @@ class PetsPageModel extends FlutterFlowModel<PetsPageWidget> {
|
|||
}
|
||||
}).catchError((err, stack) {
|
||||
context.pop();
|
||||
showSnackbar(
|
||||
showSnackbarMessenger(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
enText: 'Error deleting pet',
|
||||
|
|
|
@ -86,9 +86,9 @@ class _PetsPageWidgetState extends State<PetsPageWidget>
|
|||
);
|
||||
}
|
||||
|
||||
void onEditingChanged(bool value) {
|
||||
void onEditingChanged([bool? value]) {
|
||||
setState(() {
|
||||
_model.handleEditingChanged(value);
|
||||
_model.handleEditingChanged(value!);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:awesome_notifications/awesome_notifications.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
|
|
|
@ -472,7 +472,7 @@ class ScheduleCompleteVisitPageModel
|
|||
context.pop(value);
|
||||
|
||||
if (value == false) {
|
||||
showSnackbar(
|
||||
showSnackbarMessenger(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
enText: 'Error blocking visit',
|
||||
|
@ -481,7 +481,7 @@ class ScheduleCompleteVisitPageModel
|
|||
true,
|
||||
);
|
||||
} else if (value == true) {
|
||||
showSnackbar(
|
||||
showSnackbarMessenger(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
enText: 'Success canceling visit',
|
||||
|
@ -492,7 +492,7 @@ class ScheduleCompleteVisitPageModel
|
|||
}
|
||||
}).catchError((err, stack) {
|
||||
context.pop();
|
||||
showSnackbar(
|
||||
showSnackbarMessenger(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
enText: 'Error blocking visit',
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
export 'vehicles_on_the_property.dart';
|
||||
export 'vehicle_model.dart';
|
|
@ -0,0 +1,246 @@
|
|||
part of 'vehicles_on_the_property.dart';
|
||||
|
||||
class VehicleHistoryScreen extends StatefulWidget {
|
||||
final VehicleModel model;
|
||||
const VehicleHistoryScreen(this.model, {super.key});
|
||||
|
||||
@override
|
||||
State<VehicleHistoryScreen> createState() => _VehicleHistoryScreenState();
|
||||
}
|
||||
|
||||
class _VehicleHistoryScreenState extends State<VehicleHistoryScreen>
|
||||
with Pageable {
|
||||
final apiCall = FreAccessWSGlobal.getVehiclesByProperty;
|
||||
int totalOwnerVehicles = 0;
|
||||
final PagingController<int, dynamic> _pagingController =
|
||||
PagingController<int, dynamic>(firstPageKey: 1);
|
||||
bool _isSnackble = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_pagingController.addPageRequestListener(
|
||||
(int pageKey) => fetchPage(
|
||||
dataProvider: () async {
|
||||
final newItems = await apiCall.call(pageKey.toString());
|
||||
if (newItems.jsonBody == null) return (false, null);
|
||||
final List<dynamic> vehicles =
|
||||
(newItems.jsonBody['vehicles'] as List<dynamic>?) ?? [];
|
||||
|
||||
safeSetState(() {
|
||||
totalOwnerVehicles = newItems.jsonBody['total_owner_vehicles'] ?? 0;
|
||||
});
|
||||
|
||||
return (vehicles.isNotEmpty, vehicles);
|
||||
},
|
||||
onDataUnavailable: (vehicles) {
|
||||
setState(() {});
|
||||
final bool isFirst = pageKey == 2;
|
||||
if (!isFirst && _isSnackble) showNoMoreDataSnackBar(context);
|
||||
|
||||
_pagingController.appendLastPage(vehicles);
|
||||
},
|
||||
onDataAvailable: (vehicles) {
|
||||
setState(() {});
|
||||
_pagingController.appendPage(vehicles, pageKey + 1);
|
||||
},
|
||||
onFetchError: (e, s) {
|
||||
DialogUtil.errorDefault(context);
|
||||
LogUtil.requestAPIFailed(
|
||||
"proccessRequest.php", "", "Consulta de Veículo", e, s);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
);
|
||||
_pagingController.addStatusListener(_showError);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pagingController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _showError(PagingStatus status) async {
|
||||
if (status == PagingStatus.subsequentPageError) {
|
||||
final message = FFLocalizations.of(context).getVariableText(
|
||||
enText: 'Something went wrong while fetching a new page.',
|
||||
ptText: 'Algo deu errado ao buscar uma nova página.',
|
||||
);
|
||||
final retry = FFLocalizations.of(context).getVariableText(
|
||||
enText: 'Retry',
|
||||
ptText: 'Recarregar',
|
||||
);
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(message),
|
||||
action: SnackBarAction(
|
||||
label: retry,
|
||||
onPressed: () => _pagingController.retryLastFailedRequest(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
_buildHeader(context),
|
||||
_buildBody(context),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader(BuildContext context) {
|
||||
final bodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context);
|
||||
final headerTitle = FFLocalizations.of(context).getVariableText(
|
||||
ptText: "Meus Veículos: ",
|
||||
enText: "My Vehicles: ",
|
||||
);
|
||||
final totalRegisteredVehicles = widget.model.amountRegister;
|
||||
|
||||
return SizedBox(
|
||||
height: 30,
|
||||
child: Center(
|
||||
child: Text(
|
||||
(widget.model.amountRegister == '0' ||
|
||||
widget.model.amountRegister == null)
|
||||
? ''
|
||||
: "$headerTitle $totalOwnerVehicles/$totalRegisteredVehicles",
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Nunito',
|
||||
fontSize: bodyFontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Expanded _buildBody(BuildContext context) {
|
||||
final noDataFound = FFLocalizations.of(context).getVariableText(
|
||||
ptText: "Nenhum veículo encontrado!",
|
||||
enText: "No vehicle found",
|
||||
);
|
||||
return buildPaginatedListView<int, dynamic>(
|
||||
noDataFound,
|
||||
_pagingController,
|
||||
_generateItems,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _generateItems(
|
||||
BuildContext context,
|
||||
dynamic item,
|
||||
int index,
|
||||
) {
|
||||
log('item: $item');
|
||||
final bool? isOwner = item['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 = item['tag'];
|
||||
final bool containTag = tag.isNotNullAndEmpty;
|
||||
final Map<String, String> labelsHashMap =
|
||||
_generateLabelsHashMap(context, item, tag, containTag);
|
||||
|
||||
final List<Map<String, Color>> statusHashMapList =
|
||||
_generateStatusHashMapList(item);
|
||||
|
||||
Future<void> onTapCardItemAction() async {
|
||||
await _handleCardItemTap(context, cardIcon, item);
|
||||
}
|
||||
|
||||
final statusLinkedHashMap = statusHashMapList
|
||||
.map((map) => LinkedHashMap<String, Color>.from(map))
|
||||
.toList();
|
||||
final length = statusLinkedHashMap.expand((e) => [e.length]);
|
||||
final double itemWidthFactor = length == 1 ? 0.25 : 0.50;
|
||||
|
||||
return CardItemTemplateComponentWidget(
|
||||
icon: cardIcon,
|
||||
labelsHashMap: labelsHashMap,
|
||||
statusHashMap: statusHashMapList,
|
||||
onTapCardItemAction: onTapCardItemAction,
|
||||
itemWidthFactor: itemWidthFactor,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, String> _generateLabelsHashMap(
|
||||
BuildContext context,
|
||||
Map<String, dynamic> item,
|
||||
String? tag,
|
||||
bool containTag,
|
||||
) {
|
||||
final localization = FFLocalizations.of(context);
|
||||
return {
|
||||
'${localization.getVariableText(ptText: "Placa", enText: "License Plate")}:':
|
||||
item['licensePlate'] ?? '',
|
||||
'${localization.getVariableText(ptText: "Modelo", enText: "Model")}:':
|
||||
item['model'] ?? '',
|
||||
'${localization.getVariableText(ptText: "Proprietário", enText: "Owner")}:':
|
||||
item['personName'] ?? '',
|
||||
if (containTag)
|
||||
'${localization.getVariableText(ptText: "Tag", enText: "Tag")}:':
|
||||
tag ?? '',
|
||||
};
|
||||
}
|
||||
|
||||
List<Map<String, Color>> _generateStatusHashMapList(
|
||||
Map<String, dynamic> item) {
|
||||
final statusHashMap = widget.model.generateStatusColorMap(item, false);
|
||||
return statusHashMap != null ? [statusHashMap] : [];
|
||||
}
|
||||
|
||||
Future<void> _handleCardItemTap(
|
||||
BuildContext context,
|
||||
FreCardIcon? cardIcon,
|
||||
Map<String, dynamic> item,
|
||||
) async {
|
||||
try {
|
||||
final dialogContent = widget.model.buildVehicleDetails(
|
||||
icon: cardIcon,
|
||||
item: item,
|
||||
context: context,
|
||||
model: widget.model,
|
||||
);
|
||||
|
||||
await showDialog<bool>(
|
||||
useSafeArea: true,
|
||||
context: context,
|
||||
builder: (context) => Dialog(
|
||||
alignment: Alignment.center,
|
||||
child: dialogContent,
|
||||
),
|
||||
) //
|
||||
.then((response) async {
|
||||
if (response == true) {
|
||||
_isSnackble = false;
|
||||
_pagingController.refresh();
|
||||
} else {
|
||||
_isSnackble = true;
|
||||
}
|
||||
}) //
|
||||
.whenComplete(() {});
|
||||
} catch (e, s) {
|
||||
DialogUtil.errorDefault(context);
|
||||
LogUtil.requestAPIFailed(
|
||||
"proccessRequest.php", "", "Consulta de Veículos", e, s);
|
||||
safeSetState(() {
|
||||
// _hasData = false;
|
||||
// _loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,54 +1,558 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hub/components/templates_components/details_component/details_component_widget.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_model.dart';
|
||||
import 'package:hub/flutter_flow/internationalization.dart';
|
||||
import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart';
|
||||
import 'dart:developer';
|
||||
|
||||
class VehicleModel extends FlutterFlowModel<VehicleOnTheProperty> {
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart';
|
||||
import 'package:hub/components/templates_components/details_component/details_component_widget.dart';
|
||||
import 'package:hub/features/backend/index.dart';
|
||||
import 'package:hub/features/storage/index.dart';
|
||||
import 'package:hub/flutter_flow/index.dart';
|
||||
import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart';
|
||||
import 'package:hub/shared/extensions.dart';
|
||||
import 'package:hub/shared/utils.dart';
|
||||
|
||||
/// [VehicleModel] is a class that contains the business logic of the vehicle page.
|
||||
class VehicleModel extends FlutterFlowModel<VehiclePage>
|
||||
with
|
||||
_BaseVehiclePage,
|
||||
_VehicleHistoryScreenModel,
|
||||
_VehicleRegisterScreenModel,
|
||||
_VehicleUpdateScreenModel {
|
||||
/// [VehicleModel] is a singleton class that contains the business logic of the vehicle page.
|
||||
static VehicleModel? _instance = VehicleModel._internal();
|
||||
VehicleModel._internal();
|
||||
factory VehicleModel() => _instance ?? VehicleModel._internal();
|
||||
static void resetInstance() => _instance = null;
|
||||
|
||||
dynamic item;
|
||||
final GlobalKey<FormState> registerFormKey = GlobalKey<FormState>();
|
||||
final GlobalKey<FormState> updateFormKey = GlobalKey<FormState>();
|
||||
|
||||
@override
|
||||
void initState(BuildContext context) {
|
||||
log('VehicleModel -> initState');
|
||||
resetInstance();
|
||||
|
||||
initAsync();
|
||||
initializeControllers(context);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
amountRegister = '0';
|
||||
});
|
||||
}
|
||||
|
||||
void initializeControllers(BuildContext context) {
|
||||
tabBarController = TabController(
|
||||
vsync: Navigator.of(context),
|
||||
length: 2,
|
||||
);
|
||||
|
||||
textFieldFocusLicensePlate = FocusNode();
|
||||
textFieldControllerLicensePlate = TextEditingController();
|
||||
|
||||
textFieldFocusColor = FocusNode();
|
||||
textFieldControllerColor = TextEditingController();
|
||||
|
||||
textFieldFocusModel = FocusNode();
|
||||
textFieldControllerModel = TextEditingController();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {}
|
||||
void dispose() {
|
||||
disposeControllers();
|
||||
}
|
||||
|
||||
Future<void> initAsync() async {}
|
||||
void disposeControllers() {
|
||||
tabBarController.dispose();
|
||||
textFieldFocusLicensePlate!.dispose();
|
||||
textFieldControllerLicensePlate!.dispose();
|
||||
textFieldFocusColor!.dispose();
|
||||
textFieldControllerColor!.dispose();
|
||||
textFieldFocusModel!.dispose();
|
||||
textFieldControllerModel!.dispose();
|
||||
}
|
||||
|
||||
Widget buildVehicleDetails({
|
||||
required dynamic item,
|
||||
required BuildContext context,
|
||||
required VehicleModel model,
|
||||
}) {
|
||||
return DetailsComponentWidget(
|
||||
buttons: [],
|
||||
labelsHashMap: Map<String, String>.from({
|
||||
if (item['model'] != null && item['model'] != '')
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Modelo", enText: "Model")}:':
|
||||
item['model'].toString().toUpperCase(),
|
||||
if (item['licensePlate'] != null && item['licensePlate'] != '')
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Placa", enText: "License Plate")}:':
|
||||
item['licensePlate'].toString().toUpperCase(),
|
||||
if (item['color'] != null && item['color'] != '')
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Cor", enText: "Color")}:':
|
||||
item['color'].toString().toUpperCase(),
|
||||
if (item['personName'] != null && item['personName'] != '')
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Proprietário", enText: "Owner")}:':
|
||||
item['personName'].toString().toUpperCase(),
|
||||
if (item['tag'] != null && item['tag'] != '')
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Tag", enText: "Tag")}:':
|
||||
item['tag'].toString().toUpperCase(),
|
||||
}),
|
||||
statusHashMap: [],
|
||||
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) {
|
||||
if (formKey.currentState == null) return false;
|
||||
return formKey.currentState!.validate();
|
||||
}
|
||||
}
|
||||
|
||||
/// [_BaseVehiclePage] is a mixin that contains the base logic of the vehicle page.
|
||||
mixin class _BaseVehiclePage {
|
||||
int count = 0;
|
||||
late final TabController tabBarController;
|
||||
// dynamic item;
|
||||
late int vehicleId;
|
||||
BuildContext context = navigatorKey.currentContext!;
|
||||
bool isEditing = false;
|
||||
ApiCallResponse? vehicleResponse;
|
||||
String? amountRegister = '0';
|
||||
late final String? autoApproval;
|
||||
|
||||
VoidCallback? onUpdateVehicle;
|
||||
VoidCallback? onRegisterVehicle;
|
||||
VoidCallback? safeSetState;
|
||||
|
||||
FocusNode? textFieldFocusLicensePlate;
|
||||
TextEditingController? textFieldControllerLicensePlate;
|
||||
String? textControllerLicensePlateValidator(
|
||||
BuildContext context, String? value) {
|
||||
final validationMessage = validateField(
|
||||
context, value, 'Placa é obrigatória', 'License Plate is required');
|
||||
if (validationMessage != null) return validationMessage;
|
||||
|
||||
final brazilianPlateRegex = RegExp(r'^[A-Z]{3}\d{4}$');
|
||||
final mercosurPlateRegex = RegExp(r'^[A-Z]{3}\d[A-Z0-9]\d{2}$');
|
||||
|
||||
if (!brazilianPlateRegex.hasMatch(value!) &&
|
||||
!mercosurPlateRegex.hasMatch(value)) {
|
||||
return FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Placa inválida',
|
||||
enText: 'Invalid license plate',
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
FocusNode? textFieldFocusColor;
|
||||
TextEditingController? textFieldControllerColor;
|
||||
String? textControllerColorValidator(BuildContext contexnt, String? value) {
|
||||
return validateField(
|
||||
context, value, 'Cor é obrigatória', 'Color is required');
|
||||
}
|
||||
|
||||
FocusNode? textFieldFocusModel;
|
||||
TextEditingController? textFieldControllerModel;
|
||||
String? textControllerModelValidator(BuildContext contexnt, String? value) {
|
||||
return validateField(
|
||||
context, value, 'Modelo é obrigatório', 'Model is required');
|
||||
}
|
||||
|
||||
String? validateField(
|
||||
BuildContext context, String? value, String ptText, String enText) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return FFLocalizations.of(context).getVariableText(
|
||||
ptText: ptText,
|
||||
enText: enText,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> switchTab(int index) async {
|
||||
tabBarController.animateTo(index);
|
||||
if (index == 0) await handleEditingChanged(false);
|
||||
safeSetState?.call();
|
||||
}
|
||||
|
||||
Future<void> clearFields() async {
|
||||
textFieldControllerLicensePlate = TextEditingController(text: '');
|
||||
textFieldControllerColor = TextEditingController(text: '');
|
||||
textFieldControllerModel = TextEditingController(text: '');
|
||||
}
|
||||
|
||||
Future<void> handleEditingChanged(bool editing) async {
|
||||
isEditing = editing;
|
||||
await clearFields();
|
||||
}
|
||||
|
||||
Future<void> setEditForm(dynamic item) async {
|
||||
if (item != null) {
|
||||
log('vehicleId: ${item['vehicleId']}');
|
||||
vehicleId = item['vehicleId'];
|
||||
|
||||
textFieldControllerLicensePlate = TextEditingController(
|
||||
text: item != null ? item['licensePlate'] ?? '' : '');
|
||||
textFieldControllerColor =
|
||||
TextEditingController(text: item != null ? item['color'] ?? '' : '');
|
||||
textFieldControllerModel =
|
||||
TextEditingController(text: item != null ? item['model'] ?? '' : '');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> handleVehicleResponse(ApiCallResponse response,
|
||||
String successMessage, String errorMessage) async {
|
||||
if (response.jsonBody['error'] == false) {
|
||||
await DialogUtil.success(context, successMessage).then((_) async {
|
||||
await switchTab(0);
|
||||
});
|
||||
} else {
|
||||
String errorMsg;
|
||||
try {
|
||||
errorMsg = response.jsonBody['error_msg'];
|
||||
} catch (e) {
|
||||
errorMsg = errorMessage;
|
||||
}
|
||||
await DialogUtil.error(context, errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// [_VehicleRegisterScreenModel] is a mixin that contains the business logic of the vehicle register page.
|
||||
mixin _VehicleRegisterScreenModel on _BaseVehiclePage {
|
||||
Future<void> registerVehicle() async {
|
||||
final response = await FreAccessWSGlobal.registerVehicle.call(
|
||||
licensePlate: textFieldControllerLicensePlate!.text,
|
||||
color: textFieldControllerColor!.text,
|
||||
model: textFieldControllerModel!.text,
|
||||
);
|
||||
await handleVehicleResponse(
|
||||
response,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Veículo cadastrado com sucesso',
|
||||
enText: 'Vehicle registered successfully',
|
||||
),
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Erro ao cadastrar veículo',
|
||||
enText: 'Error registering vehicle',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// [_VehicleUpdateScreenModel] is a mixin that contains the business logic of the vehicle update page.
|
||||
mixin _VehicleUpdateScreenModel on _BaseVehiclePage {
|
||||
Future<void> updateVehicle() async {
|
||||
final response = await FreAccessWSGlobal.updateVehicle.call(
|
||||
licensePlate: textFieldControllerLicensePlate!.text,
|
||||
color: textFieldControllerColor!.text,
|
||||
model: textFieldControllerModel!.text,
|
||||
vehicleId: vehicleId,
|
||||
);
|
||||
await handleVehicleResponse(
|
||||
response,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Veículo atualizado com sucesso',
|
||||
enText: 'Vehicle updated successfully',
|
||||
),
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Erro ao atualizar veículo',
|
||||
enText: 'Error updating vehicle',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// [_VehicleHistoryScreenModel] is a mixin that contains the business logic of the vehicle history page.
|
||||
mixin _VehicleHistoryScreenModel on _BaseVehiclePage {
|
||||
Map<String, Color>? generateStatusColorMap(dynamic uItem, bool isDetail) {
|
||||
if (autoApproval.toBoolean == true) return null;
|
||||
final theme = FlutterFlowTheme.of(context);
|
||||
final localization = FFLocalizations.of(context);
|
||||
|
||||
final status = uItem['status'];
|
||||
final isOwner = uItem['isOwnerVehicle'];
|
||||
|
||||
if (isOwner == null && status == null) return null;
|
||||
|
||||
String byLanguage(String en, String pt) =>
|
||||
localization.getVariableText(enText: en, ptText: pt);
|
||||
|
||||
final vehicleStatusMap = {
|
||||
"ATI": {
|
||||
"text": byLanguage('Active', 'Ativo'),
|
||||
"color": theme.success,
|
||||
},
|
||||
"INA": {
|
||||
"text": byLanguage('Inactive', 'Inativo'),
|
||||
"color": theme.accent2,
|
||||
},
|
||||
"APR_CREATE": {
|
||||
"text": byLanguage('Awaiting Creation', 'Aguardando Criação'),
|
||||
"color": theme.success,
|
||||
},
|
||||
"APR_DELETE": {
|
||||
"text": byLanguage('Awaiting Deletion', 'Aguardando Exclusão'),
|
||||
"color": theme.error,
|
||||
},
|
||||
"APR_UPDATE": {
|
||||
"text": byLanguage('Awaiting Update', 'Aguardando Atualização'),
|
||||
"color": theme.accent2,
|
||||
},
|
||||
"AGU_CHANGE": {
|
||||
"text": byLanguage('Awaiting Change', 'Aguardando Alteração'),
|
||||
"color": theme.warning,
|
||||
},
|
||||
};
|
||||
|
||||
if (vehicleStatusMap.containsKey(status)) {
|
||||
final statusMap = {
|
||||
vehicleStatusMap[status]!['text'] as String:
|
||||
vehicleStatusMap[status]!['color'] as Color,
|
||||
};
|
||||
|
||||
if (!isDetail && isOwner && (status != 'ATI' && status != 'INA')) {
|
||||
statusMap[byLanguage('My Vehicle', 'Meu Veículo')] = theme.accent4;
|
||||
}
|
||||
|
||||
return statusMap;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
List<FFButtonWidget> generateActionButtons(dynamic item) {
|
||||
final Color iconButtonColor = FlutterFlowTheme.of(context).primaryText;
|
||||
final FFButtonOptions buttonOptions = FFButtonOptions(
|
||||
height: 40,
|
||||
color: FlutterFlowTheme.of(context).primaryBackground,
|
||||
elevation: 0,
|
||||
textStyle: TextStyle(
|
||||
color: FlutterFlowTheme.of(context).primaryText,
|
||||
fontSize: LimitedFontSizeUtil.getNoResizeFont(context, 15),
|
||||
),
|
||||
splashColor: FlutterFlowTheme.of(context).success,
|
||||
borderSide: BorderSide(
|
||||
color: FlutterFlowTheme.of(context).primaryBackground,
|
||||
width: 1,
|
||||
),
|
||||
);
|
||||
if (item['status'] == null) return [];
|
||||
|
||||
final updateText = FFLocalizations.of(context)
|
||||
.getVariableText(ptText: 'Editar', enText: 'Edit');
|
||||
final updateIcon = Icon(Icons.edit, color: iconButtonColor);
|
||||
Future updateOnPressed() async {
|
||||
context.pop();
|
||||
isEditing = true;
|
||||
|
||||
await switchTab(1);
|
||||
await setEditForm(item);
|
||||
}
|
||||
|
||||
final cancelText = FFLocalizations.of(context)
|
||||
.getVariableText(ptText: 'Cancelar', enText: 'Cancel');
|
||||
final cancelIcon = Icon(Icons.close, color: iconButtonColor);
|
||||
Future cancelOnPressed() async {
|
||||
showAlertDialog(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Cancelar Solicitação',
|
||||
enText: 'Cancel Request',
|
||||
),
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
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));
|
||||
}
|
||||
|
||||
final deleteText = FFLocalizations.of(context)
|
||||
.getVariableText(ptText: 'Excluir', enText: 'Delete');
|
||||
final deleteIcon = Icon(Icons.delete, color: iconButtonColor);
|
||||
Future deleteOnPressed() async {
|
||||
showAlertDialog(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Excluir Veículo',
|
||||
enText: 'Delete Vehicle',
|
||||
),
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Você tem certeza que deseja excluir esse veículo?',
|
||||
enText: 'Are you sure you want to delete this vehicle?',
|
||||
),
|
||||
() async => processDeleteRequest(item),
|
||||
);
|
||||
}
|
||||
|
||||
final bool containStatus = item['status'] == null;
|
||||
final bool isOwnerVehicle = item['isOwnerVehicle'];
|
||||
final bool isAGU = item['status'].contains('AGU');
|
||||
final bool isAPR = item['status'].contains('APR');
|
||||
final bool isATI = item['status'].contains('ATI');
|
||||
|
||||
if (containStatus)
|
||||
return [
|
||||
FFButtonWidget(
|
||||
text: deleteText,
|
||||
icon: deleteIcon,
|
||||
onPressed: deleteOnPressed,
|
||||
options: buttonOptions,
|
||||
),
|
||||
];
|
||||
|
||||
return [
|
||||
if (isAGU && isOwnerVehicle)
|
||||
FFButtonWidget(
|
||||
text: updateText,
|
||||
icon: updateIcon,
|
||||
onPressed: updateOnPressed,
|
||||
options: buttonOptions,
|
||||
),
|
||||
if ((isAPR || isAGU) && (isOwnerVehicle))
|
||||
FFButtonWidget(
|
||||
text: cancelText,
|
||||
icon: cancelIcon,
|
||||
onPressed: cancelOnPressed,
|
||||
options: buttonOptions,
|
||||
),
|
||||
if (isATI && isOwnerVehicle)
|
||||
FFButtonWidget(
|
||||
text: deleteText,
|
||||
icon: deleteIcon,
|
||||
onPressed: deleteOnPressed,
|
||||
options: buttonOptions,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
Future<bool> processDeleteRequest(dynamic item) async {
|
||||
log('processDeleteRequest -> item[$item]');
|
||||
bool result = await FreAccessWSGlobal.deleteVehicle
|
||||
.call(
|
||||
vehicleId: item['vehicleId'],
|
||||
licensePlate: item['licensePlate'],
|
||||
model: item['model'],
|
||||
color: item['color'],
|
||||
)
|
||||
.then((value) {
|
||||
// ignore: unrelated_type_equality_checks
|
||||
if (value.jsonBody['error'] == true) {
|
||||
final String errorMsg = value.jsonBody['error_msg'];
|
||||
showSnackbarMessenger(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
ptText: errorMsg,
|
||||
enText: 'Error deleting vehicle',
|
||||
),
|
||||
true,
|
||||
);
|
||||
return false;
|
||||
// ignore: unrelated_type_equality_checks
|
||||
}
|
||||
showSnackbarMessenger(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
enText: 'Success deleting vehicle',
|
||||
ptText: 'Succeso ao excluir veículo',
|
||||
),
|
||||
false,
|
||||
);
|
||||
return true;
|
||||
}) //
|
||||
.catchError((err, stack) {
|
||||
showSnackbarMessenger(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
enText: 'Error deleting vehicle',
|
||||
ptText: 'Erro ao excluir veículo',
|
||||
),
|
||||
true,
|
||||
);
|
||||
return false;
|
||||
});
|
||||
|
||||
context.pop(result);
|
||||
context.pop(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<bool> processCancelRequest(String status, dynamic item) async {
|
||||
try {
|
||||
final ApiCallResponse value;
|
||||
switch (status) {
|
||||
case 'APR_CREATE':
|
||||
value = await processCancelDeleteRequest(item);
|
||||
break;
|
||||
case 'AGU_CHANGE':
|
||||
value = await processCancelUpdateRequest(item);
|
||||
break;
|
||||
case 'APR_DELETE':
|
||||
value = await processCancelCreateRequest(item);
|
||||
break;
|
||||
default:
|
||||
throw ArgumentError('Status inválido: $status');
|
||||
}
|
||||
|
||||
final bool isError = value.jsonBody['error'] == true;
|
||||
final String message = FFLocalizations.of(context).getVariableText(
|
||||
ptText: value.jsonBody['error_msg'] ?? 'Erro ao cancelar solicitação',
|
||||
enText:
|
||||
isError ? 'Error canceling request' : 'Success canceling request',
|
||||
);
|
||||
showSnackbarMessenger(context, message, isError);
|
||||
context.pop(!isError);
|
||||
context.pop(!isError);
|
||||
return !isError;
|
||||
} catch (err) {
|
||||
final String errorMessage = FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Erro ao cancelar solicitação',
|
||||
enText: 'Error canceling request',
|
||||
);
|
||||
showSnackbarMessenger(context, errorMessage, true);
|
||||
context.pop(false);
|
||||
context.pop(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<ApiCallResponse> processCancelDeleteRequest(dynamic item) async {
|
||||
return await FreAccessWSGlobal.deleteVehicle.call(
|
||||
vehicleId: item['vehicleId'],
|
||||
licensePlate: item['licensePlate'],
|
||||
model: item['model'],
|
||||
color: item['color'],
|
||||
);
|
||||
}
|
||||
|
||||
Future<ApiCallResponse> processCancelUpdateRequest(dynamic item) async {
|
||||
return await FreAccessWSGlobal.deleteVehicle.call(
|
||||
vehicleId: item['vehicleId'],
|
||||
licensePlate: item['licensePlate'],
|
||||
model: item['model'],
|
||||
color: item['color'],
|
||||
);
|
||||
}
|
||||
|
||||
Future<ApiCallResponse> processCancelCreateRequest(dynamic item) async {
|
||||
return await FreAccessWSGlobal.cancelDelete.call(
|
||||
vehicleId: item['vehicleId'],
|
||||
licensePlate: item['licensePlate'],
|
||||
model: item['model'],
|
||||
color: item['color'],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, String> generateLabelsHashMap(dynamic item) {
|
||||
return {
|
||||
if (item['model'] != null && item['model'] != '')
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Modelo", enText: "Model")}:':
|
||||
item['model'].toString().toUpperCase(),
|
||||
if (item['licensePlate'] != null && item['licensePlate'] != '')
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Placa", enText: "License Plate")}:':
|
||||
item['licensePlate'].toString().toUpperCase(),
|
||||
if (item['color'] != null && item['color'] != '')
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Cor", enText: "Color")}:':
|
||||
item['color'].toString().toUpperCase(),
|
||||
if (item['personName'] != null && item['personName'] != '')
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Proprietário", enText: "Owner")}:':
|
||||
item['personName'].toString().toUpperCase(),
|
||||
if (item['tag'] != null && item['tag'] != '')
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Tag", enText: "Tag")}:':
|
||||
item['tag'].toString().toUpperCase(),
|
||||
};
|
||||
}
|
||||
|
||||
DetailsComponentWidget buildVehicleDetails({
|
||||
required dynamic item,
|
||||
required BuildContext context,
|
||||
required VehicleModel model,
|
||||
required FreCardIcon? icon,
|
||||
}) {
|
||||
final status = generateStatusColorMap(item, true);
|
||||
final buttons = generateActionButtons(item);
|
||||
final labels = generateLabelsHashMap(item);
|
||||
return DetailsComponentWidget(
|
||||
icon: icon,
|
||||
buttons: buttons,
|
||||
labelsHashMap: labels,
|
||||
statusHashMap: [status],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
part of 'vehicles_on_the_property.dart';
|
||||
|
||||
/// [VehicleRegisterScreen] is a StatefulWidget that displays a form to register a vehicle.
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class VehicleRegisterScreen extends StatefulWidget {
|
||||
VehicleRegisterScreen(this.model, {super.key});
|
||||
late VehicleModel model;
|
||||
|
||||
@override
|
||||
State<VehicleRegisterScreen> createState() => _VehicleRegisterScreenState();
|
||||
}
|
||||
|
||||
class _VehicleRegisterScreenState extends State<VehicleRegisterScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
_buildHeader(context),
|
||||
_buildBody(context),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Form _buildBody(BuildContext context) {
|
||||
return Form(
|
||||
key: widget.model.registerFormKey,
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
_buildCustomInput(
|
||||
context: context,
|
||||
controller: widget.model.textFieldControllerLicensePlate!,
|
||||
validator: widget.model.textControllerLicensePlateValidator,
|
||||
focusNode: widget.model.textFieldFocusLicensePlate!,
|
||||
labelText: FFLocalizations.of(context)
|
||||
.getVariableText(ptText: 'Placa', enText: 'License Plate'),
|
||||
hintText: FFLocalizations.of(context)
|
||||
.getVariableText(ptText: 'Placa', enText: 'License Plate'),
|
||||
suffixIcon: Symbols.format_color_text,
|
||||
inputFormatters: [UpperCaseTextFormatter()],
|
||||
maxLength: 7,
|
||||
),
|
||||
_buildCustomInput(
|
||||
context: context,
|
||||
controller: widget.model.textFieldControllerModel!,
|
||||
validator: widget.model.textControllerModelValidator,
|
||||
focusNode: widget.model.textFieldFocusModel!,
|
||||
labelText: FFLocalizations.of(context)
|
||||
.getVariableText(ptText: 'Modelo', enText: 'Model'),
|
||||
hintText: FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Ex: Voyage, Ford', enText: 'e.g. Voyage, Ford'),
|
||||
suffixIcon: Symbols.car_repair,
|
||||
inputFormatters: [],
|
||||
),
|
||||
_buildCustomInput(
|
||||
context: context,
|
||||
controller: widget.model.textFieldControllerColor!,
|
||||
validator: widget.model.textControllerColorValidator,
|
||||
focusNode: widget.model.textFieldFocusColor!,
|
||||
labelText: FFLocalizations.of(context)
|
||||
.getVariableText(ptText: 'Cor', enText: 'Color'),
|
||||
hintText: FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Ex: Preto, Amarelo, Branco',
|
||||
enText: 'e.g. Black, Yellow, White'),
|
||||
suffixIcon: Symbols.palette,
|
||||
inputFormatters: [],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(70, 20, 70, 30),
|
||||
child: SubmitButtonUtil(
|
||||
labelText: FFLocalizations.of(context)
|
||||
.getVariableText(ptText: 'Cadastrar', enText: 'Register'),
|
||||
onPressed: widget.model
|
||||
.isFormValid(context, widget.model.registerFormKey)
|
||||
? widget.model.registerVehicle
|
||||
: null,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCustomInput({
|
||||
required BuildContext context,
|
||||
required TextEditingController controller,
|
||||
required String? Function(BuildContext, String?) validator,
|
||||
required FocusNode focusNode,
|
||||
required String labelText,
|
||||
required String hintText,
|
||||
required IconData suffixIcon,
|
||||
required final List<TextInputFormatter>? inputFormatters,
|
||||
int maxLength = 80,
|
||||
}) {
|
||||
return CustomInputUtil(
|
||||
controller: controller,
|
||||
validator: (value) => validator(context, value),
|
||||
focusNode: focusNode,
|
||||
labelText: labelText,
|
||||
hintText: hintText,
|
||||
suffixIcon: suffixIcon,
|
||||
haveMaxLength: true,
|
||||
onChanged: (value) => setState(() {}),
|
||||
inputFormatters: inputFormatters,
|
||||
maxLength: maxLength,
|
||||
);
|
||||
}
|
||||
|
||||
Align _buildHeader(BuildContext context) {
|
||||
double limitedHeaderFontSize =
|
||||
LimitedFontSizeUtil.getHeaderFontSize(context);
|
||||
|
||||
return Align(
|
||||
alignment: const AlignmentDirectional(-1.0, 0.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsetsDirectional.fromSTEB(24.0, 20, 0.0, 15),
|
||||
child: Text(
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
ptText:
|
||||
'Preencha o formulário de cadastro com os dados do seu veículo',
|
||||
enText: 'Fill out the registration form with your vehicle data',
|
||||
),
|
||||
textAlign: TextAlign.start,
|
||||
style: FlutterFlowTheme.of(context).bodyMedium.override(
|
||||
fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily,
|
||||
letterSpacing: 0.0,
|
||||
useGoogleFonts: GoogleFonts.asMap()
|
||||
.containsKey(FlutterFlowTheme.of(context).bodyMediumFamily),
|
||||
fontSize: limitedHeaderFontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
part of 'vehicles_on_the_property.dart';
|
||||
|
||||
/// [VehicleUpdateScreen] is a StatefulWidget that displays a form to update a vehicle.
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class VehicleUpdateScreen extends StatefulWidget {
|
||||
VehicleUpdateScreen(this.model, {super.key});
|
||||
late VehicleModel model;
|
||||
|
||||
@override
|
||||
State<VehicleUpdateScreen> createState() => _VehicleUpdateScreenState();
|
||||
}
|
||||
|
||||
class _VehicleUpdateScreenState extends State<VehicleUpdateScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
_buildHeader(context),
|
||||
buildBody(context),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Form buildBody(BuildContext context) {
|
||||
return Form(
|
||||
key: widget.model.updateFormKey,
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
_buildCustomInput(
|
||||
context: context,
|
||||
controller: widget.model.textFieldControllerLicensePlate!,
|
||||
validator: widget.model.textControllerLicensePlateValidator,
|
||||
focusNode: widget.model.textFieldFocusLicensePlate!,
|
||||
labelText: FFLocalizations.of(context)
|
||||
.getVariableText(ptText: 'Placa', enText: 'License Plate'),
|
||||
hintText: FFLocalizations.of(context)
|
||||
.getVariableText(ptText: 'Placa', enText: 'License Plate'),
|
||||
suffixIcon: Symbols.format_color_text,
|
||||
inputFormatters: [UpperCaseTextFormatter()],
|
||||
maxLength: 7,
|
||||
),
|
||||
_buildCustomInput(
|
||||
context: context,
|
||||
controller: widget.model.textFieldControllerModel!,
|
||||
validator: widget.model.textControllerModelValidator,
|
||||
focusNode: widget.model.textFieldFocusModel!,
|
||||
labelText: FFLocalizations.of(context)
|
||||
.getVariableText(ptText: 'Modelo', enText: 'Model'),
|
||||
hintText: FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Ex: Voyage, Ford', enText: 'e.g. Voyage, Ford'),
|
||||
suffixIcon: Symbols.car_repair,
|
||||
inputFormatters: [],
|
||||
),
|
||||
_buildCustomInput(
|
||||
context: context,
|
||||
controller: widget.model.textFieldControllerColor!,
|
||||
validator: widget.model.textControllerColorValidator,
|
||||
focusNode: widget.model.textFieldFocusColor!,
|
||||
labelText: FFLocalizations.of(context)
|
||||
.getVariableText(ptText: 'Cor', enText: 'Color'),
|
||||
hintText: FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Ex: Preto, Amarelo, Branco',
|
||||
enText: 'e.g. Black, Yellow, White'),
|
||||
suffixIcon: Symbols.palette,
|
||||
inputFormatters: [],
|
||||
),
|
||||
_buildSubmitButton(context),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader(BuildContext context) {
|
||||
return Align(
|
||||
alignment: const AlignmentDirectional(-1.0, 0.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsetsDirectional.fromSTEB(24.0, 20, 0.0, 15),
|
||||
child: Text(
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
ptText:
|
||||
'Preencha o formulário de alteração com os dados do seu veículo',
|
||||
enText: 'Fill out the update form with your vehicle data',
|
||||
),
|
||||
textAlign: TextAlign.start,
|
||||
style: FlutterFlowTheme.of(context).bodyMedium.override(
|
||||
fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily,
|
||||
letterSpacing: 0.0,
|
||||
useGoogleFonts: GoogleFonts.asMap()
|
||||
.containsKey(FlutterFlowTheme.of(context).bodyMediumFamily),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCustomInput({
|
||||
required BuildContext context,
|
||||
required TextEditingController controller,
|
||||
required String? Function(BuildContext, String?) validator,
|
||||
required FocusNode focusNode,
|
||||
required String labelText,
|
||||
required String hintText,
|
||||
required IconData suffixIcon,
|
||||
required List<TextInputFormatter>? inputFormatters,
|
||||
int maxLength = 80,
|
||||
}) {
|
||||
return CustomInputUtil(
|
||||
controller: controller,
|
||||
validator: (value) => validator(context, value),
|
||||
focusNode: focusNode,
|
||||
labelText: labelText,
|
||||
hintText: hintText,
|
||||
suffixIcon: suffixIcon,
|
||||
haveMaxLength: true,
|
||||
onChanged: (value) => setState(() {}),
|
||||
inputFormatters: inputFormatters,
|
||||
maxLength: maxLength,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSubmitButton(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(70, 20, 70, 30),
|
||||
child: SubmitButtonUtil(
|
||||
labelText: FFLocalizations.of(context)
|
||||
.getVariableText(ptText: 'Salvar', enText: 'Save'),
|
||||
onPressed: widget.model.isFormValid(context, widget.model.updateFormKey)
|
||||
? () => widget.model.updateVehicle()
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,149 +1,184 @@
|
|||
import 'dart:collection';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:hub/components/atomic_components/shared_components_atoms/custom_input.dart';
|
||||
import 'package:hub/components/atomic_components/shared_components_atoms/submit_button.dart';
|
||||
import 'package:hub/components/atomic_components/shared_components_atoms/tabview.dart';
|
||||
import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart';
|
||||
import 'package:hub/features/backend/index.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_icon_button.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_util.dart';
|
||||
import 'package:hub/features/module/index.dart';
|
||||
import 'package:hub/flutter_flow/index.dart';
|
||||
import 'package:hub/pages/vehicles_on_the_property/vehicle_model.dart';
|
||||
import 'package:hub/shared/extensions.dart';
|
||||
import 'package:hub/shared/mixins/pageable_mixin.dart';
|
||||
import 'package:hub/shared/utils/dialog_util.dart';
|
||||
import 'package:hub/shared/utils/license_util.dart';
|
||||
import 'package:hub/shared/utils/limited_text_size.dart';
|
||||
import 'package:hub/shared/utils/log_util.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
|
||||
class VehicleOnTheProperty extends StatefulWidget {
|
||||
const VehicleOnTheProperty({super.key});
|
||||
part 'vehicle_history_screen.dart';
|
||||
part 'vehicle_register_screen.dart';
|
||||
part 'vehicle_update_screen.dart';
|
||||
|
||||
/// [VehiclePage] is a StatefulWidget that displays the vehicle screens.
|
||||
class VehiclePage extends StatefulWidget {
|
||||
const VehiclePage({super.key});
|
||||
|
||||
@override
|
||||
_VehicleOnThePropertyState createState() => _VehicleOnThePropertyState();
|
||||
// ignore: library_private_types_in_public_api
|
||||
_VehiclePageState createState() => _VehiclePageState();
|
||||
}
|
||||
|
||||
class _VehicleOnThePropertyState extends State<VehicleOnTheProperty>
|
||||
class _VehiclePageState extends State<VehiclePage>
|
||||
with TickerProviderStateMixin {
|
||||
late ScrollController _scrollController;
|
||||
|
||||
int _pageNumber = 1;
|
||||
bool _hasData = false;
|
||||
bool _loading = false;
|
||||
int count = 0;
|
||||
late final VehicleModel model;
|
||||
|
||||
late Future<void> _future;
|
||||
List<dynamic> _wrap = [];
|
||||
late final VehicleModel _model;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
model = createModel(context, () => VehicleModel());
|
||||
_future = _fetchVisits();
|
||||
_scrollController = ScrollController()
|
||||
..addListener(() {
|
||||
if (_scrollController.position.atEdge &&
|
||||
_scrollController.position.pixels != 0) {
|
||||
_loadMore();
|
||||
}
|
||||
_model = createModel(context, () => VehicleModel());
|
||||
|
||||
_model.updateOnChange = true;
|
||||
_model.onUpdateVehicle = () {
|
||||
safeSetState(() {
|
||||
_model.clearFields();
|
||||
});
|
||||
};
|
||||
_model.onRegisterVehicle = () {
|
||||
safeSetState(() {
|
||||
_model.clearFields();
|
||||
});
|
||||
};
|
||||
_model.safeSetState = () {
|
||||
safeSetState(() {});
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
// @override
|
||||
// void dispose() {
|
||||
// super.dispose();
|
||||
// }
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
late final limitedHeaderTextSize =
|
||||
LimitedFontSizeUtil.getHeaderFontSize(context);
|
||||
|
||||
final backgroundColor = FlutterFlowTheme.of(context).primaryBackground;
|
||||
return Scaffold(
|
||||
backgroundColor: FlutterFlowTheme.of(context).primaryBackground,
|
||||
appBar: _appBar(context),
|
||||
body: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
if (_hasData == false && _pageNumber <= 1 && _loading == false)
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
ptText: "Nenhum veículo encontrado!",
|
||||
enText: "No vehicle found",
|
||||
),
|
||||
style: TextStyle(
|
||||
fontFamily: 'Nunito',
|
||||
fontSize: limitedHeaderTextSize,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
else if (_hasData == true || _pageNumber >= 1)
|
||||
Expanded(
|
||||
child: FutureBuilder<void>(
|
||||
future: _future,
|
||||
builder: (context, snapshot) {
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
controller: _scrollController,
|
||||
itemCount: _wrap.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == 0) {
|
||||
// Add your item here
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 30, top: 10),
|
||||
child: Text(
|
||||
'',
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
final item = _wrap[index - 1];
|
||||
return _item(context, item);
|
||||
}
|
||||
});
|
||||
},
|
||||
)),
|
||||
if (_hasData == true && _loading == true)
|
||||
Container(
|
||||
padding: const EdgeInsets.only(top: 15, bottom: 15),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
FlutterFlowTheme.of(context).primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
].addToStart(const SizedBox(height: 0)),
|
||||
),
|
||||
backgroundColor: backgroundColor,
|
||||
appBar: _buildHeader(context),
|
||||
body: _buildBody(context),
|
||||
);
|
||||
}
|
||||
|
||||
PreferredSizeWidget _appBar(BuildContext context) {
|
||||
/// [Body] of the page.
|
||||
FutureBuilder<bool> _buildBody(BuildContext context) {
|
||||
Widget progressIndicator() {
|
||||
return CircularProgressIndicator(
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
FlutterFlowTheme.of(context).primary,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return FutureBuilder<bool>(
|
||||
future: _initializeModule(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return progressIndicator();
|
||||
} else if (snapshot.hasError) {
|
||||
return progressIndicator();
|
||||
} else if (snapshot.hasData && snapshot.data == true) {
|
||||
return _buildVehicleManager(context);
|
||||
} else {
|
||||
return _buildVehicleHistory(context);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> _initializeModule() async {
|
||||
try {
|
||||
final module =
|
||||
await LicenseRepositoryImpl().getModule('FRE-HUB-VEHICLES-MANAGER');
|
||||
return await LicenseUtil.processModule(module);
|
||||
} catch (e) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
context.pop();
|
||||
await DialogUtil.errorDefault(navigatorKey.currentContext!);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void onEditingChanged([bool? value]) {
|
||||
bool isFirst = _model.tabBarController.index == 0;
|
||||
|
||||
if (_model.isEditing & isFirst) {
|
||||
_model.handleEditingChanged(false);
|
||||
}
|
||||
if (isFirst) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildVehicleHistory(BuildContext context) {
|
||||
return VehicleHistoryScreen(_model);
|
||||
}
|
||||
|
||||
Widget _buildVehicleManager(BuildContext context) {
|
||||
final vehicleHistoryScreenLabel = FFLocalizations.of(context)
|
||||
.getVariableText(ptText: 'Consultar', enText: 'History');
|
||||
final vehicleRegisterScreenLabel = FFLocalizations.of(context)
|
||||
.getVariableText(ptText: 'Cadastrar', enText: 'Register');
|
||||
final vehicleUpdateScreenLabel = FFLocalizations.of(context)
|
||||
.getVariableText(ptText: 'Editar', enText: 'Edit');
|
||||
|
||||
return TabViewUtil(
|
||||
context: context,
|
||||
model: _model,
|
||||
labelTab1: vehicleHistoryScreenLabel,
|
||||
labelTab2: _model.isEditing
|
||||
? vehicleUpdateScreenLabel
|
||||
: vehicleRegisterScreenLabel,
|
||||
controller: _model.tabBarController,
|
||||
widget1: VehicleHistoryScreen(_model),
|
||||
widget2: _model.isEditing
|
||||
? VehicleUpdateScreen(_model)
|
||||
: VehicleRegisterScreen(_model),
|
||||
onEditingChanged: onEditingChanged,
|
||||
);
|
||||
}
|
||||
|
||||
/// -----------------------------------
|
||||
/// [Header] of the page.
|
||||
PreferredSizeWidget _buildHeader(BuildContext context) {
|
||||
final theme = FlutterFlowTheme.of(context);
|
||||
final backgroundColor = theme.primaryBackground;
|
||||
final primaryText = theme.primaryText;
|
||||
final title = FFLocalizations.of(context)
|
||||
.getVariableText(enText: 'Vehicles', ptText: 'Veículos');
|
||||
final titleStyle = theme.headlineMedium.override(
|
||||
fontFamily: theme.headlineMediumFamily,
|
||||
color: primaryText,
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
letterSpacing: 0.0,
|
||||
useGoogleFonts:
|
||||
GoogleFonts.asMap().containsKey(theme.headlineMediumFamily),
|
||||
);
|
||||
final backButton = _backButton(context, theme);
|
||||
|
||||
return AppBar(
|
||||
backgroundColor: FlutterFlowTheme.of(context).primaryBackground,
|
||||
backgroundColor: backgroundColor,
|
||||
automaticallyImplyLeading: false,
|
||||
title: Text(
|
||||
FFLocalizations.of(context)
|
||||
.getVariableText(enText: 'Vehicles', ptText: 'Veículos'),
|
||||
style: FlutterFlowTheme.of(context).headlineMedium.override(
|
||||
fontFamily: FlutterFlowTheme.of(context).headlineMediumFamily,
|
||||
color: FlutterFlowTheme.of(context).primaryText,
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
letterSpacing: 0.0,
|
||||
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||
FlutterFlowTheme.of(context).headlineMediumFamily),
|
||||
),
|
||||
),
|
||||
leading: _backButton(context, FlutterFlowTheme.of(context)),
|
||||
title: Text(title, style: titleStyle),
|
||||
leading: backButton,
|
||||
centerTitle: true,
|
||||
elevation: 0.0,
|
||||
actions: [],
|
||||
|
@ -151,131 +186,22 @@ class _VehicleOnThePropertyState extends State<VehicleOnTheProperty>
|
|||
}
|
||||
|
||||
Widget _backButton(BuildContext context, FlutterFlowTheme theme) {
|
||||
final Icon icon = Icon(
|
||||
Icons.keyboard_arrow_left,
|
||||
color: theme.primaryText,
|
||||
size: 30.0,
|
||||
);
|
||||
onPressed() => Navigator.of(context).pop();
|
||||
return FlutterFlowIconButton(
|
||||
key: ValueKey<String>('BackNavigationAppBar'),
|
||||
borderColor: Colors.transparent,
|
||||
borderRadius: 30.0,
|
||||
borderWidth: 1.0,
|
||||
buttonSize: 60.0,
|
||||
icon: Icon(
|
||||
Icons.keyboard_arrow_left,
|
||||
color: theme.primaryText,
|
||||
size: 30.0,
|
||||
),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
icon: icon,
|
||||
onPressed: onPressed,
|
||||
);
|
||||
}
|
||||
|
||||
Future<ApiCallResponse?> _fetchVisits() async {
|
||||
try {
|
||||
setState(() => _loading = true);
|
||||
|
||||
var response = await FreAccessWSGlobal.getVehiclesByProperty
|
||||
.call(_pageNumber.toString());
|
||||
|
||||
final List<dynamic> vehicles = response.jsonBody['vehicles'] ?? [];
|
||||
safeSetState(() => count = response.jsonBody['total_rows'] ?? 0);
|
||||
|
||||
if (vehicles.isNotEmpty) {
|
||||
setState(() {
|
||||
_wrap.addAll(vehicles);
|
||||
_hasData = true;
|
||||
_loading = false;
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
_showNoMoreDataSnackBar(context);
|
||||
|
||||
setState(() {
|
||||
_hasData = false;
|
||||
_loading = false;
|
||||
});
|
||||
|
||||
return null;
|
||||
} catch (e, s) {
|
||||
DialogUtil.errorDefault(context);
|
||||
LogUtil.requestAPIFailed(
|
||||
"proccessRequest.php", "", "Consulta de Veículo", e, s);
|
||||
setState(() {
|
||||
_hasData = false;
|
||||
_loading = false;
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void _loadMore() {
|
||||
if (_hasData == true) {
|
||||
_pageNumber++;
|
||||
|
||||
_future = _fetchVisits();
|
||||
}
|
||||
}
|
||||
|
||||
void _showNoMoreDataSnackBar(BuildContext context) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
ptText: "Não há mais dados.",
|
||||
enText: "No more data.",
|
||||
),
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: LimitedFontSizeUtil.getBodyFontSize(context),
|
||||
),
|
||||
),
|
||||
duration: const Duration(seconds: 3),
|
||||
backgroundColor: FlutterFlowTheme.of(context).primary,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _item(BuildContext context, dynamic uItem) {
|
||||
return CardItemTemplateComponentWidget(
|
||||
imagePath: null,
|
||||
labelsHashMap: {
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Placa", enText: "License Plate")}:':
|
||||
uItem['licensePlate'] ?? '',
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Modelo", enText: "Model")}:':
|
||||
uItem['model'] ?? '',
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Tag", enText: "Tag")}:':
|
||||
uItem['tag'] ?? '',
|
||||
},
|
||||
statusHashMap: [],
|
||||
onTapCardItemAction: () async {
|
||||
await showDialog(
|
||||
useSafeArea: true,
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return Dialog(
|
||||
alignment: Alignment.center,
|
||||
child: model.buildVehicleDetails(
|
||||
item: uItem,
|
||||
context: context,
|
||||
model: model,
|
||||
),
|
||||
);
|
||||
},
|
||||
).whenComplete(() {
|
||||
safeSetState(() {
|
||||
_pageNumber = 1;
|
||||
_wrap = [];
|
||||
_future = _fetchVisits()
|
||||
.then((value) => value!.jsonBody['vehicles'] ?? []);
|
||||
});
|
||||
}).catchError((e, s) {
|
||||
DialogUtil.errorDefault(context);
|
||||
LogUtil.requestAPIFailed(
|
||||
"proccessRequest.php", "", "Consulta de Veículos", e, s);
|
||||
safeSetState(() {
|
||||
_hasData = false;
|
||||
_loading = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
/// -----------------------------------
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
|||
import 'package:hub/flutter_flow/internationalization.dart';
|
||||
import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart';
|
||||
|
||||
class VisitsModel extends FlutterFlowModel<VehicleOnTheProperty> {
|
||||
class VisitsModel extends FlutterFlowModel<VehiclePage> {
|
||||
static VisitsModel? _instance;
|
||||
|
||||
VisitsModel._internal({this.onRefresh});
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -0,0 +1 @@
|
|||
export 'enums/enum_throw_exception.dart';
|
|
@ -1 +0,0 @@
|
|||
export 'enum_throw_exception.dart';
|
|
@ -0,0 +1,3 @@
|
|||
export 'extensions/dialog_extensions.dart';
|
||||
export 'extensions/flutter_secure_storage_extensions.dart';
|
||||
export 'extensions/string_extensions.dart';
|
|
@ -1,3 +0,0 @@
|
|||
export 'dialog_extensions.dart';
|
||||
export 'flutter_secure_storage_extensions.dart';
|
||||
export 'string_extensions.dart';
|
|
@ -1,7 +1,7 @@
|
|||
import 'dart:ui';
|
||||
|
||||
extension StringNullableExtensions on String? {
|
||||
bool toBoolean() {
|
||||
bool get toBoolean {
|
||||
if (this == null) return false;
|
||||
return this!.toLowerCase() == 'true';
|
||||
}
|
||||
|
@ -11,10 +11,16 @@ extension StringNullableExtensions on String? {
|
|||
if (this == '') return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool get isNotNullAndEmpty {
|
||||
if (this == null) return false;
|
||||
if (this == '') return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension StringExtensions on String {
|
||||
bool toBoolean() {
|
||||
bool get toBoolean {
|
||||
return toLowerCase() == 'true';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
export 'mixins/pegeable_mixin.dart';
|
||||
export 'mixins/status_mixin.dart';
|
||||
export 'mixins/switcher_mixin.dart';
|
||||
export 'mixins/template_mixin.dart';
|
|
@ -1,3 +0,0 @@
|
|||
export 'pegeable_mixin.dart';
|
||||
export 'status_mixin.dart';
|
||||
export 'switcher_mixin.dart';
|
|
@ -0,0 +1,98 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hub/flutter_flow/index.dart';
|
||||
import 'package:hub/shared/utils/limited_text_size.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
|
||||
extension PagedListViewExtension<PageKeyType, ItemType>
|
||||
on PagedSliverList<PageKeyType, ItemType> {}
|
||||
|
||||
mixin Pageable<T extends StatefulWidget> on State<T> {
|
||||
Expanded buildPaginatedListView<X, Y>(
|
||||
String noDataFound,
|
||||
PagingController<X, Y> pg,
|
||||
Widget Function(BuildContext, Y, int) itemBuilder) {
|
||||
final theme = FlutterFlowTheme.of(context);
|
||||
|
||||
return Expanded(
|
||||
child: RefreshIndicator(
|
||||
backgroundColor: theme.primaryBackground,
|
||||
color: theme.primary,
|
||||
onRefresh: () async => pg.refresh(),
|
||||
child: PagedListView<X, Y>(
|
||||
pagingController: pg,
|
||||
builderDelegate: PagedChildBuilderDelegate<Y>(
|
||||
animateTransitions: true,
|
||||
|
||||
itemBuilder: (context, item, index) =>
|
||||
itemBuilder(context, item, index),
|
||||
// noMoreItemsIndicatorBuilder: ,
|
||||
newPageProgressIndicatorBuilder: (context) =>
|
||||
buildLoadingIndicator(context),
|
||||
firstPageProgressIndicatorBuilder: (context) =>
|
||||
buildLoadingIndicator(context),
|
||||
noItemsFoundIndicatorBuilder: (context) =>
|
||||
buildNoDataFound(context, noDataFound),
|
||||
// firstPageErrorIndicatorBuilder: (context) => const Placeholder(),
|
||||
// newPageErrorIndicatorBuilder: (context) => const Placeholder(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> fetchPage({
|
||||
required Future<(bool, dynamic)> Function() dataProvider,
|
||||
required void Function(dynamic data) onDataAvailable,
|
||||
required void Function(dynamic data) onDataUnavailable,
|
||||
required void Function(Object error, StackTrace stackTrace) onFetchError,
|
||||
}) async {
|
||||
try {
|
||||
final (bool isDataAvailable, dynamic data) = await dataProvider();
|
||||
if (isDataAvailable) {
|
||||
onDataAvailable(data);
|
||||
} else {
|
||||
onDataUnavailable(data);
|
||||
}
|
||||
} catch (error, stackTrace) {
|
||||
onFetchError(error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
void showNoMoreDataSnackBar(BuildContext context) {
|
||||
final message = FFLocalizations.of(context).getVariableText(
|
||||
ptText: "Não há mais dados.",
|
||||
enText: "No more data.",
|
||||
);
|
||||
|
||||
showSnackbarMessenger(context, message, true);
|
||||
}
|
||||
|
||||
Widget buildNoDataFound(BuildContext context, String title) {
|
||||
final headerFontSize = LimitedFontSizeUtil.getHeaderFontSize(context);
|
||||
// final bodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context);
|
||||
return Expanded(
|
||||
child: Center(
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Nunito',
|
||||
fontSize: headerFontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildLoadingIndicator(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 15),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
FlutterFlowTheme.of(context).primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -44,7 +44,7 @@ mixin Template {
|
|||
),
|
||||
icon: Icon(
|
||||
Symbols.info_i_rounded,
|
||||
color: Colors.black,
|
||||
color: FlutterFlowTheme.of(context).primaryText,
|
||||
),
|
||||
)
|
||||
];
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
export 'utils/biometric_util.dart';
|
||||
export 'utils/device_util.dart';
|
||||
export 'utils/dialog_util.dart';
|
||||
export 'utils/image_util.dart';
|
||||
export 'utils/limited_text_size.dart';
|
||||
export 'utils/loading_util.dart';
|
||||
export 'utils/log_util.dart';
|
||||
export 'utils/path_util.dart';
|
||||
export 'utils/share_util.dart';
|
||||
export 'utils/snackbar_util.dart';
|
||||
export 'utils/string_util.dart';
|
||||
export 'utils/text_util.dart';
|
||||
export 'utils/validator_util.dart';
|
||||
export 'utils/webview_util.dart';
|
||||
export 'utils/color_util.dart';
|
|
@ -0,0 +1,48 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ColorUtil {
|
||||
static Color getContrastColor(Color a, Color b) {
|
||||
double luminance(Color color) {
|
||||
return (0.299 * color.r + 0.587 * color.g + 0.114 * color.b) / 255;
|
||||
}
|
||||
|
||||
double contrastRatio(Color a, Color b) {
|
||||
final lumA = luminance(a) + 0.05;
|
||||
final lumB = luminance(b) + 0.05;
|
||||
return lumA > lumB ? lumA / lumB : lumB / lumA;
|
||||
}
|
||||
|
||||
if (contrastRatio(a, b) < 4.5) {
|
||||
// Find a color with higher contrast within the same hue
|
||||
final hsv = HSVColor.fromColor(a);
|
||||
double hue = hsv.hue;
|
||||
double saturation = hsv.saturation;
|
||||
double brightness = hsv.value;
|
||||
|
||||
// Increase brightness to ensure higher contrast
|
||||
brightness = brightness > 0.5 ? brightness - 0.5 : brightness + 0.5;
|
||||
|
||||
return HSVColor.fromAHSV(1.0, hue, saturation, brightness).toColor();
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
static Color getSelfContrastColor(Color color) {
|
||||
final hsv = HSVColor.fromColor(color);
|
||||
double hue = hsv.hue;
|
||||
double saturation = hsv.saturation;
|
||||
double brightness = hsv.value;
|
||||
|
||||
// Increase brightness to ensure higher contrast
|
||||
brightness = brightness > 0.5 ? brightness - 0.5 : brightness + 0.5;
|
||||
|
||||
return HSVColor.fromAHSV(1.0, hue, saturation, brightness).toColor();
|
||||
}
|
||||
}
|
||||
|
||||
extension ColorUtilExtension on Color {
|
||||
Color get highlight => ColorUtil.getSelfContrastColor(this);
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import 'dart:developer';
|
||||
|
||||
class DateTimeUtil {
|
||||
static Future<bool> processStartDate(String startDate) async {
|
||||
try {
|
||||
if (startDate.isEmpty) return true;
|
||||
final start = DateTime.tryParse(startDate);
|
||||
if (start == null) return false;
|
||||
return DateTime.now().isAfter(start);
|
||||
} catch (e) {
|
||||
log('Error processing start date for module: $e');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static Future<bool> processExpirationDate(String expirationDate) async {
|
||||
try {
|
||||
if (expirationDate.isEmpty) return false;
|
||||
final expiration = DateTime.tryParse(expirationDate);
|
||||
return expiration != null && DateTime.now().isAfter(expiration);
|
||||
} catch (e) {
|
||||
log('Error processing expiration date for module: $e');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
export 'biometric_util.dart';
|
||||
export 'device_util.dart';
|
||||
export 'dialog_util.dart';
|
||||
export 'image_util.dart';
|
||||
export 'limited_text_size.dart';
|
||||
export 'loading_util.dart';
|
||||
export 'log_util.dart';
|
||||
export 'path_util.dart';
|
||||
export 'share_util.dart';
|
||||
export 'snackbar_util.dart';
|
||||
export 'string_util.dart';
|
||||
export 'text_util.dart';
|
||||
export 'validator_util.dart';
|
||||
export 'webview_util.dart';
|
|
@ -0,0 +1,18 @@
|
|||
import 'package:hub/features/module/index.dart';
|
||||
import 'package:hub/flutter_flow/index.dart';
|
||||
import 'package:hub/shared/utils/datetime_util.dart';
|
||||
|
||||
class LicenseUtil {
|
||||
static Future<bool> processModule(String? module) async {
|
||||
if (module == null) return false;
|
||||
final moduleMap = await stringToMap(module);
|
||||
final startDate = moduleMap['startDate'] ?? '';
|
||||
final expirationDate = moduleMap['expirationDate'] ?? '';
|
||||
final isStarted = await DateTimeUtil.processStartDate(startDate);
|
||||
final isExpired = await DateTimeUtil.processExpirationDate(expirationDate);
|
||||
if (isStarted && !isExpired)
|
||||
return EnumDisplay.fromString(moduleMap["display"]) == EnumDisplay.active;
|
||||
if (isExpired) return false;
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/// [Base]
|
||||
library;
|
||||
|
||||
export 'widgets/page.dart';
|
||||
export 'widgets/component.dart';
|
||||
export 'widgets/screen.dart';
|
||||
export 'widgets/model.dart';
|
||||
export 'widgets/entity.dart';
|
||||
|
||||
/// [View]'s
|
||||
export 'widgets/list_view.dart';
|
||||
export 'widgets/read_view.dart';
|
||||
export 'widgets/enhanced_carousel_view.dart';
|
||||
export 'widgets/enhanced_list_view.dart';
|
||||
export 'widgets/enhanced_search_view.dart';
|
||||
|
||||
/// [Component]'s
|
||||
export 'widgets/text.dart';
|
|
@ -1,4 +1,5 @@
|
|||
part of 'widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hub/shared/widgets.dart';
|
||||
|
||||
/// [ComponentWidget]
|
||||
|
||||
|
|
|
@ -1,68 +1,123 @@
|
|||
part of 'widgets.dart';
|
||||
import 'dart:developer';
|
||||
|
||||
class EnhancedCarouselView<T> extends StatelessWidget {
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hub/flutter_flow/index.dart';
|
||||
import 'package:hub/shared/utils.dart';
|
||||
|
||||
typedef EnhancedCarouselViewKey<T> = GlobalKey<_EnhancedCarouselViewState<T>>;
|
||||
|
||||
class EnhancedCarouselView<T> extends StatefulWidget {
|
||||
final Future<List<T?>> Function() dataProvider;
|
||||
final void Function(T, BuildContext) filter;
|
||||
final Widget Function<T>(T? item) itemBuilder;
|
||||
final void Function(T?, BuildContext) filter;
|
||||
final Widget Function<T>(T? item, bool isSelected) itemBuilder;
|
||||
final bool showIndicator;
|
||||
|
||||
const EnhancedCarouselView({
|
||||
super.key,
|
||||
required this.dataProvider,
|
||||
required this.filter,
|
||||
required this.itemBuilder,
|
||||
this.showIndicator = false,
|
||||
});
|
||||
|
||||
@override
|
||||
_EnhancedCarouselViewState<T> createState() =>
|
||||
_EnhancedCarouselViewState<T>();
|
||||
}
|
||||
|
||||
class _EnhancedCarouselViewState<T> extends State<EnhancedCarouselView<T>> {
|
||||
T? selectedCategory;
|
||||
|
||||
bool itemIsSelected(T item) {
|
||||
return selectedCategory == item;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = FlutterFlowTheme.of(context);
|
||||
final backgroundColor = theme.primary;
|
||||
final overlayColor = WidgetStateProperty.all(Colors.transparent);
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
spacing: 20,
|
||||
return Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(15, 0, 50, 0),
|
||||
child: Text(
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Suas Categorias',
|
||||
enText: 'Your Categories',
|
||||
),
|
||||
style: TextStyle(
|
||||
color: FlutterFlowTheme.of(context).primaryText,
|
||||
fontSize: LimitedFontSizeUtil.getHeaderFontSize(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
FutureBuilder<List<T?>>(
|
||||
future: dataProvider(),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) return SizedBox();
|
||||
final items =
|
||||
snapshot.data!.map((item) => itemBuilder(item)).toList();
|
||||
return SizedBox(
|
||||
height: 130, // Set a specific height
|
||||
child: CarouselView(
|
||||
itemExtent: 140,
|
||||
enableSplash: true,
|
||||
itemSnapping: true,
|
||||
controller: CarouselController(initialItem: 1),
|
||||
backgroundColor: backgroundColor,
|
||||
overlayColor: overlayColor,
|
||||
padding: EdgeInsets.zero,
|
||||
elevation: 0,
|
||||
shrinkExtent: 10,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
onTap: (index) => filter(snapshot.data![index] as T, context),
|
||||
children: items,
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(15, 0, 50, 0),
|
||||
child: Text(
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Suas Categorias',
|
||||
enText: 'Your Categories',
|
||||
),
|
||||
);
|
||||
}),
|
||||
style: TextStyle(
|
||||
color: FlutterFlowTheme.of(context).primaryText,
|
||||
fontSize: LimitedFontSizeUtil.getHeaderFontSize(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
FutureBuilder<List<T?>>(
|
||||
future: widget.dataProvider(),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) return SizedBox();
|
||||
final items = snapshot.data!
|
||||
.map((item) =>
|
||||
widget.itemBuilder(item, itemIsSelected(item as T)))
|
||||
.toList();
|
||||
return SizedBox(
|
||||
height: 130, // Set a specific height
|
||||
child: CarouselView(
|
||||
itemExtent: 140,
|
||||
enableSplash: true,
|
||||
itemSnapping: true,
|
||||
controller: CarouselController(initialItem: 1),
|
||||
backgroundColor: backgroundColor,
|
||||
overlayColor: overlayColor,
|
||||
padding: EdgeInsets.zero,
|
||||
elevation: 0,
|
||||
reverse: true,
|
||||
shrinkExtent: 10,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
onTap: (index) async {
|
||||
log('Selected: ${snapshot.data![index]}');
|
||||
log('Selected Category: $selectedCategory');
|
||||
final bool isSame =
|
||||
itemIsSelected(snapshot.data![index]!);
|
||||
setState(() {
|
||||
if (isSame) {
|
||||
selectedCategory = null;
|
||||
} else {
|
||||
selectedCategory = snapshot.data![index] as T;
|
||||
}
|
||||
});
|
||||
if (isSame)
|
||||
widget.filter(null, context);
|
||||
else
|
||||
widget.filter(snapshot.data![index] as T, context);
|
||||
},
|
||||
children: items,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (widget.showIndicator)
|
||||
Positioned(
|
||||
left: 0,
|
||||
top: 50,
|
||||
child: Icon(Icons.arrow_left, size: 30, color: Colors.grey),
|
||||
),
|
||||
if (widget.showIndicator)
|
||||
Positioned(
|
||||
right: 0,
|
||||
top: 50,
|
||||
child: Icon(Icons.arrow_right, size: 30, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
part of 'widgets.dart';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hub/flutter_flow/index.dart';
|
||||
import 'package:rx_bloc/rx_bloc.dart';
|
||||
import 'package:rx_bloc_list/rx_bloc_list.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
/// [TypeDefs] ----------------------------------------------------
|
||||
|
||||
|
@ -206,6 +212,10 @@ class EnhancedListViewState<ItemType, HeaderType, FooterType, QueryType>
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final String defaultMessage = FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Nenhum item encontrado',
|
||||
enText: 'No items found',
|
||||
);
|
||||
final header = StreamBuilder<List<HeaderType>>(
|
||||
stream: bloc.states.headerItems.cast<List<HeaderType>>(),
|
||||
builder: (context, headerSnapshot) {
|
||||
|
@ -245,7 +255,9 @@ class EnhancedListViewState<ItemType, HeaderType, FooterType, QueryType>
|
|||
} else if (bodySnapshot.hasError) {
|
||||
return EnhancedErrorWidget(error: bodySnapshot.error);
|
||||
} else if (!bodySnapshot.hasData || bodySnapshot.data!.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
return Center(
|
||||
child: Text(defaultMessage),
|
||||
);
|
||||
} else {
|
||||
return ListView.builder(
|
||||
itemCount: bodySnapshot.data?.length ?? 0,
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
part of 'widgets.dart';
|
||||
|
||||
// class EnhancedSearchView<QueryType> extends StatelessWidget {
|
||||
// const EnhancedSearchView({
|
||||
// super.key,
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
part of 'widgets.dart';
|
||||
|
||||
abstract class Entity<T> {}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
part of 'widgets.dart';
|
||||
|
||||
// typedef SearchKey = GlobalKey<EnhancedRemoteListViewState>;
|
||||
|
||||
// typedef Query<X extends Archive> = X?;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
part of 'widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ModelWidget extends Widget {
|
||||
const ModelWidget({super.key});
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
part of 'widgets.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:hub/shared/mixins/template_mixin.dart';
|
||||
import 'package:hub/shared/widgets.dart';
|
||||
|
||||
/// [PageWidget]
|
||||
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
part of 'widgets.dart';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
// Removed unnecessary import
|
||||
import 'package:hub/flutter_flow/index.dart';
|
||||
import 'package:hub/shared/utils/dialog_util.dart';
|
||||
import 'package:hub/shared/widgets.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:pdfx/pdfx.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
// typedef PDFViewerKey = GlobalKey<SfPdfViewerState>;
|
||||
typedef ReadViewController = PdfController;
|
||||
typedef DocumentType = PdfDocument;
|
||||
|
||||
|
@ -23,9 +32,11 @@ abstract interface class Viewer extends StatelessComponent {
|
|||
class ReadView extends StatefulWidget {
|
||||
final String url;
|
||||
final String title;
|
||||
final VoidCallback onError;
|
||||
|
||||
const ReadView({
|
||||
super.key,
|
||||
required this.onError,
|
||||
required this.url,
|
||||
required this.title,
|
||||
});
|
||||
|
@ -36,58 +47,94 @@ class ReadView extends StatefulWidget {
|
|||
|
||||
class ReadViewState extends State<ReadView> {
|
||||
Future<ReadViewController> _initializePdf() async {
|
||||
final file = await downloadPdf(widget.url);
|
||||
final Future<DocumentType> document = DocumentType.openFile(file.path);
|
||||
return ReadViewController(document: document);
|
||||
try {
|
||||
final file = await downloadPdf(widget.url);
|
||||
final Future<DocumentType> document = DocumentType.openFile(file.path);
|
||||
return ReadViewController(document: document);
|
||||
} catch (e) {
|
||||
final message = FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Erro ao baixar o PDF',
|
||||
enText: 'Error downloading PDF',
|
||||
);
|
||||
|
||||
return Future.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
Future<File> downloadPdf(String url) async {
|
||||
final response = await http.get(Uri.parse(url));
|
||||
if (response.statusCode == 200) {
|
||||
final bytes = response.bodyBytes;
|
||||
final dir = await getTemporaryDirectory();
|
||||
final file = File('${dir.path}/downloaded.pdf');
|
||||
await file.writeAsBytes(bytes);
|
||||
return file;
|
||||
} else {
|
||||
throw Exception('Falha ao baixar o PDF');
|
||||
try {
|
||||
final response = await http.get(Uri.parse(url));
|
||||
if (response.statusCode == 200) {
|
||||
final bytes = response.bodyBytes;
|
||||
final dir = await getTemporaryDirectory();
|
||||
final file = File('${dir.path}/downloaded.pdf');
|
||||
await file.writeAsBytes(bytes);
|
||||
return file;
|
||||
} else {
|
||||
throw Exception('Falha ao baixar o PDF');
|
||||
}
|
||||
} catch (e) {
|
||||
final message = FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Erro ao baixar o PDF',
|
||||
enText: 'Error downloading PDF',
|
||||
);
|
||||
await throwError(message, e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> throwError(String message, dynamic error) async {
|
||||
log('$message: $error');
|
||||
await DialogUtil.error(context, message)
|
||||
.whenComplete(() => widget.onError());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
_buildPDFViewer(),
|
||||
buildShareButton(),
|
||||
_buildPDFViewer(context),
|
||||
buildShareButton(context),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Positioned buildShareButton() {
|
||||
Positioned buildShareButton(BuildContext context) {
|
||||
final theme = FlutterFlowTheme.of(context);
|
||||
return Positioned(
|
||||
bottom: 10,
|
||||
right: 10,
|
||||
child: IconButton(
|
||||
icon: Icon(
|
||||
Icons.share,
|
||||
color: Colors.black,
|
||||
color: theme.primaryText,
|
||||
),
|
||||
color: Colors.black,
|
||||
color: theme.primaryText,
|
||||
onPressed: onShare,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void onShare() async {
|
||||
final Uri uri = Uri.parse(widget.url);
|
||||
final response = await http.get(uri);
|
||||
if (response.statusCode == 200) {
|
||||
final XFile xfile = XFile.fromData(response.bodyBytes,
|
||||
name: '${widget.title}.pdf', mimeType: 'application/pdf');
|
||||
await Share.shareXFiles([xfile], text: 'Confira este PDF!');
|
||||
} else {
|
||||
log('Erro ao baixar o arquivo: ${response.statusCode}');
|
||||
Future<void> onShare() async {
|
||||
try {
|
||||
final Uri uri = Uri.parse(widget.url);
|
||||
final response = await http.get(uri);
|
||||
if (response.statusCode == 200) {
|
||||
final XFile xfile = XFile.fromData(response.bodyBytes,
|
||||
name: '${widget.title}.pdf', mimeType: 'application/pdf');
|
||||
await Share.shareXFiles([xfile],
|
||||
text: 'Confira este PDF!',
|
||||
fileNameOverrides: ['${widget.title}.pdf']);
|
||||
} else {
|
||||
throw Exception(
|
||||
'Erro ao compartilhar o arquivo: ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
final message = FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Erro ao compartilhar o arquivo',
|
||||
enText: 'Error sharing file',
|
||||
);
|
||||
await throwError(message, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,12 +151,19 @@ class ReadViewState extends State<ReadView> {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _buildPDFViewer() => Padding(
|
||||
Widget _buildPDFViewer(BuildContext context) => Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: FutureBuilder<ReadViewController>(
|
||||
future: _initializePdf(),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) return buildLoadingIndicator(context);
|
||||
if (snapshot.error != null) {
|
||||
return buildLoadingIndicator(context);
|
||||
}
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return buildLoadingIndicator(context);
|
||||
}
|
||||
|
||||
return PdfView(
|
||||
controller: snapshot.data!,
|
||||
renderer: (PdfPage page) => page.render(
|
||||
|
@ -126,6 +180,4 @@ class ReadViewState extends State<ReadView> {
|
|||
);
|
||||
}),
|
||||
);
|
||||
|
||||
// Widget get progressIndicator => LoadingUtil.buildLoadingIndicator(context);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
part of 'widgets.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hub/shared/mixins/template_mixin.dart';
|
||||
import 'package:hub/shared/widgets.dart';
|
||||
|
||||
abstract class ScreenWidget<T> extends Widget {
|
||||
const ScreenWidget({super.key});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
part of 'widgets.dart';
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
|
||||
class AutoText extends AutoSizeText {
|
||||
const AutoText(
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:easy_debounce/easy_debounce.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:hub/flutter_flow/index.dart';
|
||||
import 'package:hub/shared/mixins/template_mixin.dart';
|
||||
import 'package:hub/shared/utils/index.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:pdfx/pdfx.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:rx_bloc_list/rx_bloc_list.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
import 'package:rx_bloc/rx_bloc.dart';
|
||||
|
||||
part 'widgets.rxb.g.dart';
|
||||
|
||||
/// [Base]
|
||||
part 'page.dart';
|
||||
part 'component.dart';
|
||||
part 'screen.dart';
|
||||
part 'model.dart';
|
||||
part 'entity.dart';
|
||||
|
||||
/// [View]'s
|
||||
part 'list_view.dart';
|
||||
part 'read_view.dart';
|
||||
part 'enhanced_carousel_view.dart';
|
||||
part 'enhanced_list_view.dart';
|
||||
part 'enhanced_search_view.dart';
|
||||
|
||||
/// [Component]'s
|
||||
part 'text.dart';
|
|
@ -1,108 +0,0 @@
|
|||
// // dart format width=80
|
||||
// // GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
// // **************************************************************************
|
||||
// // Generator: RxBlocGeneratorForAnnotation
|
||||
// // **************************************************************************
|
||||
|
||||
part of 'widgets.dart';
|
||||
|
||||
// /// Used as a contractor for the bloc, events and states classes
|
||||
// /// @nodoc
|
||||
// abstract class EnhancedListViewBlocType extends RxBlocTypeBase {
|
||||
// EnhancedListViewEvents get events;
|
||||
// EnhancedListViewStates get states;
|
||||
// }
|
||||
|
||||
// /// [$EnhancedListViewBloc<BodyType, HeaderType, FooterType, QueryType>] extended by the [EnhancedListViewBloc<BodyType, HeaderType, FooterType, QueryType>]
|
||||
// /// @nodoc
|
||||
// abstract class $EnhancedListViewBloc<BodyType, HeaderType, FooterType, QueryType> extends RxBlocBase
|
||||
// implements
|
||||
// EnhancedListViewEvents,
|
||||
// EnhancedListViewStates,
|
||||
// EnhancedListViewBlocType {
|
||||
// final _compositeSubscription = CompositeSubscription();
|
||||
|
||||
// /// Тhe [Subject] where events sink to by calling [loadBodyItems]
|
||||
// final _$loadBodyItemsEvent = PublishSubject<({bool reset, dynamic query})>();
|
||||
|
||||
// /// Тhe [Subject] where events sink to by calling [loadHeaderItems]
|
||||
// final _$loadHeaderItemsEvent = PublishSubject<void>();
|
||||
|
||||
// /// Тhe [Subject] where events sink to by calling [loadFooterItems]
|
||||
// final _$loadFooterItemsEvent = PublishSubject<void>();
|
||||
|
||||
// /// The state of [bodyItems] implemented in [_mapToBodyItemsState]
|
||||
// late final Stream<List<T>> _bodyItemsState = _mapToBodyItemsState();
|
||||
|
||||
// /// The state of [headerItems] implemented in [_mapToHeaderItemsState]
|
||||
// late final Stream<List<H>> _headerItemsState = _mapToHeaderItemsState();
|
||||
|
||||
// /// The state of [footerItems] implemented in [_mapToFooterItemsState]
|
||||
// late final Stream<List<F>> _footerItemsState = _mapToFooterItemsState();
|
||||
|
||||
// /// The state of [isLoading] implemented in [_mapToIsLoadingState]
|
||||
// late final Stream<bool> _isLoadingState = _mapToIsLoadingState();
|
||||
|
||||
// /// The state of [errors] implemented in [_mapToErrorsState]
|
||||
// late final Stream<String> _errorsState = _mapToErrorsState();
|
||||
|
||||
// @override
|
||||
// void loadBodyItems({
|
||||
// bool reset = false,
|
||||
// dynamic query = null,
|
||||
// }) =>
|
||||
// _$loadBodyItemsEvent.add((
|
||||
// reset: reset,
|
||||
// query: query,
|
||||
// ));
|
||||
|
||||
// @override
|
||||
// void loadHeaderItems() => _$loadHeaderItemsEvent.add(null);
|
||||
|
||||
// @override
|
||||
// void loadFooterItems() => _$loadFooterItemsEvent.add(null);
|
||||
|
||||
// @override
|
||||
// Stream<List<T>> get bodyItems => _bodyItemsState;
|
||||
|
||||
// @override
|
||||
// Stream<List<H>> get headerItems => _headerItemsState;
|
||||
|
||||
// @override
|
||||
// Stream<List<F>> get footerItems => _footerItemsState;
|
||||
|
||||
// @override
|
||||
// Stream<bool> get isLoading => _isLoadingState;
|
||||
|
||||
// @override
|
||||
// Stream<String> get errors => _errorsState;
|
||||
|
||||
// Stream<List<T>> _mapToBodyItemsState();
|
||||
|
||||
// Stream<List<H>> _mapToHeaderItemsState();
|
||||
|
||||
// Stream<List<F>> _mapToFooterItemsState();
|
||||
|
||||
// Stream<bool> _mapToIsLoadingState();
|
||||
|
||||
// Stream<String> _mapToErrorsState();
|
||||
|
||||
// @override
|
||||
// EnhancedListViewEvents get events => this;
|
||||
|
||||
// @override
|
||||
// EnhancedListViewStates get states => this;
|
||||
|
||||
// @override
|
||||
// void dispose() {
|
||||
// _$loadBodyItemsEvent.close();
|
||||
// _$loadHeaderItemsEvent.close();
|
||||
// _$loadFooterItemsEvent.close();
|
||||
// _compositeSubscription.dispose();
|
||||
// super.dispose();
|
||||
// }
|
||||
// }
|
||||
|
||||
// // ignore: unused_element
|
||||
// typedef _LoadBodyItemsEventArgs = ({bool reset, dynamic query});
|
|
@ -3,8 +3,8 @@ name: hub
|
|||
description: . # Descrição do projeto (adicione mais detalhes se necessário)
|
||||
publish_to: "none" # Destino de publicação
|
||||
|
||||
# Versão do aplicativo
|
||||
version: 1.3.5+24
|
||||
|
||||
version: 1.4.0+28
|
||||
|
||||
# Restrições de versão do SDK Dart
|
||||
environment:
|
||||
|
@ -113,7 +113,9 @@ dependencies:
|
|||
url_launcher_platform_interface: 2.3.2
|
||||
permission_handler: ^11.3.1
|
||||
awesome_notifications: ^0.10.0
|
||||
app_tracking_transparency: ^2.0.6
|
||||
app_tracking_transparency: ^2.0.6+1
|
||||
# dio: ^5.7.0
|
||||
# crypto: ^3.0.5
|
||||
freezed_annotation: ^2.4.4
|
||||
package_info_plus: ^8.1.1
|
||||
sliver_tools: ^0.2.12
|
||||
|
|
|
@ -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
|
|
@ -29,7 +29,7 @@ void main() {
|
|||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
setUpAll(() async {
|
||||
await initializeApp();
|
||||
await initializeBindings();
|
||||
});
|
||||
|
||||
group('Test', () {
|
||||
|
|
Loading…
Reference in New Issue