chore: clean up flutter analyze warnings

Highlights:
- Add publish_to: 'none' to legacy pubspec.yaml files                                                      - Replace print() with dart:developer log() in Treezor SDK
- Add !context.mounted guards in async callbacks (defensive bug fix)                                                       - Add super.key to widget constructors
- Remove redundant @Default(null) from device_model                                     - Fix implementation_imports in legacy_auth datasources                                        - Add ignore comments for scaffolding code
- Add missing shared_preferences dependency in splash module                                                                                                            Mostly code quality improvements, with one defensive bug fix              context.mounted) and one missing dependency fix (shared_preferences).
This commit is contained in:
2026-04-06 22:21:22 +02:00
parent b63b06ef14
commit 3d267aff37
66 changed files with 504 additions and 490 deletions

View File

@@ -338,7 +338,7 @@ packages:
source: hosted
version: "0.1.6"
dio:
dependency: transitive
dependency: "direct main"
description:
name: dio
sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c
@@ -1249,10 +1249,10 @@ packages:
dependency: transitive
description:
name: shared_preferences
sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64"
sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf
url: "https://pub.dev"
source: hosted
version: "2.5.4"
version: "2.5.5"
shared_preferences_android:
dependency: transitive
description:

View File

@@ -97,6 +97,7 @@ dependencies:
country_code_picker: ^3.4.1
flutter_native_splash: ^2.4.7
permission_handler: ^12.0.1
dio: ^5.9.2
dev_dependencies:
flutter_test:
sdk: flutter

View File

@@ -1,8 +1,6 @@
library account;
export 'src/features/account_settings/account_settings_builder.dart';
export 'src/features/personal_data/personal_data_builder.dart';
export 'src/features/change_password/change_password_builder.dart';
export 'src/features/linked_devices/linked_devices_builder.dart';
export 'src/features/app_users/app_users_builder.dart';
export 'src/features/delete_account/delete_account_builder.dart';
export 'src/features/delete_account/delete_account_builder.dart';

View File

@@ -1,4 +1,3 @@
import 'package:legacy_shared/legacy_shared.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:sf_shared/sf_shared.dart';

View File

@@ -58,7 +58,8 @@ class AppUserCard extends ConsumerWidget {
final bool isEditing;
const AppUserCard({
required this.user,
super.key, required this.user,
required this.isEditing,
});

View File

@@ -222,6 +222,7 @@ class _SaveSection extends ConsumerWidget {
child: PrimaryButton(
onPressed: () async {
await vm.submit();
if (!context.mounted) return;
final errorMessage = ref.read(
changePasswordViewModelProvider.select((s)=>s.errorMessage)

View File

@@ -7,9 +7,10 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sf_shared/sf_shared.dart';
final changePasswordViewModelProvider =
NotifierProvider.autoDispose<ChangePasswordViewModel, ChangePasswordViewState>(
ChangePasswordViewModel.new,
);
NotifierProvider.autoDispose<
ChangePasswordViewModel,
ChangePasswordViewState
>(ChangePasswordViewModel.new);
class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
late final ChangePasswordUseCase _changePasswordUseCase;
@@ -38,21 +39,15 @@ class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
}
void toggleCurrentPasswordVisibility() {
state = state.copyWith(
showCurrentPassword: !state.showCurrentPassword
);
state = state.copyWith(showCurrentPassword: !state.showCurrentPassword);
}
void toggleNewPasswordVisibility() {
state = state.copyWith(
showNewPassword: !state.showNewPassword
);
state = state.copyWith(showNewPassword: !state.showNewPassword);
}
void toggleRepeatedPasswordVisibility() {
state = state.copyWith(
showRepeatedPassword: !state.showRepeatedPassword
);
state = state.copyWith(showRepeatedPassword: !state.showRepeatedPassword);
}
void _onNewPasswordChanged() {
@@ -60,10 +55,7 @@ class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
if (value == state.newPassword) return;
state = state.copyWith(
newPassword: value,
errorMessage: ''
);
state = state.copyWith(newPassword: value, errorMessage: '');
}
void _onRepeatPasswordChanged() {
@@ -71,45 +63,41 @@ class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
if (value == state.repeatPassword) return;
state = state.copyWith(
repeatPassword: value,
errorMessage: ''
);
state = state.copyWith(repeatPassword: value, errorMessage: '');
}
bool _validateForm() {
final _upperRegex = RegExp(r'[A-Z]');
final _digitRegex = RegExp(r'[0-9]');
final _specialRegex =
RegExp(r'[!@#$%^&*(),.?":{}|<>\-_+=\[\]\\\/~`]');
final upperRegex = RegExp(r'[A-Z]');
final digitRegex = RegExp(r'[0-9]');
final specialRegex = RegExp(r'[!@#$%^&*(),.?":{}|<>\-_+=\[\]\\\/~`]');
final password = state.newPassword.trim();
if (password.isEmpty){
if (password.isEmpty) {
state = state.copyWith(errorMessage: 'errorMessageNewPasswordIsEmpty');
return false;
}
if (state.repeatPassword.trim().isEmpty){
if (state.repeatPassword.trim().isEmpty) {
state = state.copyWith(errorMessage: 'errorMessageRepeatPasswordIsEmpty');
return false;
}
if (password != state.repeatPassword.trim()){
if (password != state.repeatPassword.trim()) {
state = state.copyWith(errorMessage: 'errorMessagePasswordsDontMatch');
return false;
}
if (password.length < 8){
if (password.length < 8) {
state = state.copyWith(errorMessage: 'errorPasswordMinLength');
return false;
}
if (!_upperRegex.hasMatch(password)) {
if (!upperRegex.hasMatch(password)) {
state = state.copyWith(errorMessage: 'errorPasswordUppercase');
return false;
}
if (!_digitRegex.hasMatch(password)) {
if (!digitRegex.hasMatch(password)) {
state = state.copyWith(errorMessage: 'errorPasswordDigits');
return false;
}
if (!_specialRegex.hasMatch(password)) {
if (!specialRegex.hasMatch(password)) {
state = state.copyWith(errorMessage: 'errorPasswordSpecial');
return false;
}
@@ -117,10 +105,7 @@ class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
}
ChangePasswordRequestEntity _toRequest() {
return ChangePasswordRequestEntity(
password: state.newPassword.trim(),
);
return ChangePasswordRequestEntity(password: state.newPassword.trim());
}
Future<void> submit() async {
@@ -128,20 +113,17 @@ class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
if (!_validateForm()) return;
try {
state = state.copyWith(
isLoading: true,
isComplete: false
);
state = state.copyWith(isLoading: true, isComplete: false);
final user = await ref.read(userInfoProvider.future);
final request = _toRequest();
await _changePasswordUseCase.changePassword(userId: user.id, request: request);
state = state.copyWith(
isLoading: false,
isComplete: true
await _changePasswordUseCase.changePassword(
userId: user.id,
request: request,
);
state = state.copyWith(isLoading: false, isComplete: true);
} catch (e) {
if (!ref.mounted) return;
_finishWithError(message: e.toString());
@@ -163,6 +145,5 @@ class ChangePasswordViewModel extends Notifier<ChangePasswordViewState> {
repeatPasswordController.removeListener(_onRepeatPasswordChanged);
repeatPasswordController.dispose();
}
}
}

View File

@@ -7,18 +7,13 @@ import 'package:navigation/navigation_contract.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
class ConfirmDialog extends ConsumerWidget{
class ConfirmDialog extends ConsumerWidget {
final NavigationContract navigationContract;
const ConfirmDialog({
super.key,
required this.navigationContract,
});
const ConfirmDialog({super.key, required this.navigationContract});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final state = ref.watch(deleteAccountViewModelProvider);
@@ -36,16 +31,16 @@ class ConfirmDialog extends ConsumerWidget{
theme: theme,
toggleDeleteDevice: viewModel.toggleDeleteDevice,
deviceNames: state.deviceNames,
onCancel: (){
onCancel: () {
viewModel.resetConfirmStep();
Navigator.pop(context);
},
onSubmit: () async {
viewModel.deleteAccount();
if (!context.mounted) return;
final isComplete = ref.read(
deleteAccountViewModelProvider.select((s)=>s.isComplete)
deleteAccountViewModelProvider.select((s) => s.isComplete),
);
if (isComplete) {
navigationContract.goTo(AppRoutes.login);
@@ -59,7 +54,6 @@ class ConfirmDialog extends ConsumerWidget{
}
class _VerifyAccountStep extends StatelessWidget {
final String email;
final TextEditingController passwordController;
final String errorMessage;
@@ -88,26 +82,29 @@ class _VerifyAccountStep extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(context.translate(I18n.verifyAccount),
style: TextStyle(
fontWeight: FontWeight.w500
),
Text(
context.translate(I18n.verifyAccount),
style: TextStyle(fontWeight: FontWeight.w500),
),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 16)),
Text('${context.translate(I18n.email)}: ${email}'),
Text('${context.translate(I18n.email)}: $email'),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 6)),
Row(
children: [
Text('${context.translate(I18n.password)}: '),
SizedBox(width: SizeUtils.getByScreen(small: 12, big: 10)),
Expanded(child: TextField(
controller: passwordController,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(hintText: context.translate(I18n.password)),
obscureText: true,
enableSuggestions: false,
autocorrect: true,
))
Expanded(
child: TextField(
controller: passwordController,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
hintText: context.translate(I18n.password),
),
obscureText: true,
enableSuggestions: false,
autocorrect: true,
),
),
],
),
if (errorMessage.isNotEmpty)
@@ -122,32 +119,36 @@ class _VerifyAccountStep extends StatelessWidget {
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 10)),
Row(
children: [
Expanded(child: SecondaryButton(
onPressed: (){Navigator.pop(context);},
text: context.translate(I18n.cancel),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: 40,
radius: 20,
)),
Expanded(
child: SecondaryButton(
onPressed: () {
Navigator.pop(context);
},
text: context.translate(I18n.cancel),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: 40,
radius: 20,
),
),
SizedBox(width: SizeUtils.getByScreen(small: 12, big: 10)),
Expanded(child: PrimaryButton(
onPressed: nextStep,
text: context.translate(I18n.accept),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: 40,
radius: 20,
)),
Expanded(
child: PrimaryButton(
onPressed: nextStep,
text: context.translate(I18n.accept),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: 40,
radius: 20,
),
),
],
)
),
],
),
);
}
}
class _ConfirmRequestStep extends StatelessWidget {
final ThemePort theme;
final Function toggleDeleteDevice;
final List<String> deviceNames;
@@ -177,58 +178,68 @@ class _ConfirmRequestStep extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(height: SizeUtils.getByScreen(small: 14, big: 12)),
Text(context.translate(I18n.requestCancelTitle),
style: TextStyle(
fontWeight: FontWeight.w500
),
Text(
context.translate(I18n.requestCancelTitle),
style: TextStyle(fontWeight: FontWeight.w500),
),
SizedBox(height: SizeUtils.getByScreen(small: 14, big: 12)),
Expanded(child: SingleChildScrollView(child: Column(
children: [
Text(context.translate(I18n.requestCancelBody),
style: TextStyle(height: 1.5),
),
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 10)),
...List<Widget>.generate(deviceNames.length, (int index) =>
CheckboxListTile(
contentPadding: EdgeInsets.zero,
title: Text(context.translate(I18n.deleteDeviceData,
args: {'name': deviceNames[index]}
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
Text(
context.translate(I18n.requestCancelBody),
style: TextStyle(height: 1.5),
),
style: TextStyle(height: 0),
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 10)),
...List<Widget>.generate(
deviceNames.length,
(int index) => CheckboxListTile(
contentPadding: EdgeInsets.zero,
title: Text(
context.translate(
I18n.deleteDeviceData,
args: {'name': deviceNames[index]},
),
style: TextStyle(height: 0),
),
controlAffinity: ListTileControlAffinity.leading,
value: false,
onChanged: (_) {
toggleDeleteDevice(index);
},
),
),
controlAffinity: ListTileControlAffinity.leading,
value: false,
onChanged: (_){
toggleDeleteDevice(index);
}
)
],
),
]
))),
),
),
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 10)),
Row(
children: [
Expanded(child: SecondaryButton(
onPressed: onCancel,
text: context.translate(I18n.cancel),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: 50,
radius: 25,
)),
Expanded(
child: SecondaryButton(
onPressed: onCancel,
text: context.translate(I18n.cancel),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: 50,
radius: 25,
),
),
SizedBox(width: SizeUtils.getByScreen(small: 12, big: 10)),
Expanded(child: PrimaryButton(
onPressed: onSubmit,
text: context.translate(I18n.confirm),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: 50,
radius: 25,
)),
Expanded(
child: PrimaryButton(
onPressed: onSubmit,
text: context.translate(I18n.confirm),
color: theme.getColorFor(ThemeCode.legacyPrimary),
height: 50,
radius: 25,
),
),
],
)
),
],
),
);
}
}
}

View File

@@ -9,7 +9,7 @@ import 'package:utils/utils.dart';
class EditLinkedDeviceScreen extends ConsumerWidget {
const EditLinkedDeviceScreen();
const EditLinkedDeviceScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
@@ -100,6 +100,7 @@ class _SaveSection extends ConsumerWidget{
PrimaryButton(
onPressed: () async {
await vm.updateDevice();
if (!context.mounted) return;
final errorMessage = ref.read(
linkedDevicesViewModelProvider.select((s) => s.errorMessage)

View File

@@ -13,8 +13,9 @@ class DeleteDeviceDialog extends ConsumerWidget {
final DeviceEntity device;
const DeleteDeviceDialog({
super.key,
required this.navigationContract,
required this.device
required this.device,
});
@override
@@ -51,6 +52,7 @@ class DeleteDeviceDialog extends ConsumerWidget {
Expanded(child: PrimaryButton(
onPressed: () async {
await vm.deleteDevice(device);
if (!context.mounted) return;
final isComplete = ref.read(
linkedDevicesViewModelProvider.select((s)=>s.isComplete)

View File

@@ -1,5 +1,6 @@
name: account
description: "A new Flutter project."
publish_to: 'none'
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43

View File

@@ -1,3 +1,5 @@
// ignore_for_file: non_constant_identifier_names
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:control_panel/src/core/domain/entities/address_entity.dart';
import 'package:control_panel/src/core/domain/entities/network_entity.dart';
@@ -7,7 +9,8 @@ part 'latest_positions_response_model.freezed.dart';
part 'latest_positions_response_model.g.dart';
@freezed
abstract class LatestPositionsResponseModel with _$LatestPositionsResponseModel {
abstract class LatestPositionsResponseModel
with _$LatestPositionsResponseModel {
const factory LatestPositionsResponseModel({
required List<LatestPositionsItemResponseModel> items,
}) = _LatestPositionsResponseModel;
@@ -40,37 +43,43 @@ abstract class LatestPositionsItemResponseModel
required bool frequentPlace,
}) = _LatestPositionsItemResponseModel;
factory LatestPositionsItemResponseModel.fromJson(Map<String, dynamic> json) =>
_$LatestPositionsItemResponseModelFromJson(json);
factory LatestPositionsItemResponseModel.fromJson(
Map<String, dynamic> json,
) => _$LatestPositionsItemResponseModelFromJson(json);
}
extension LatestPositionsResponseModelMapper on LatestPositionsResponseModel {
List<PositionEntity> toEntity() {
return items.map((LatestPositionsItemResponseModel item) => PositionEntity(
id: item.id,
deviceIdentificator: item.deviceIdentificator,
latitude: item.latitude,
longitude: item.longitude,
hpe: item.hpe,
ncell: item.ncell,
type: item.type,
steps: item.steps,
address: item.address?.toEntity(),
createdAt: item.createdAt,
positionDate: item.positionDate,
positionDateOriginal: item.positionDateOriginal,
frequentPlaceName: item.frequentPlaceName,
message: item.message,
networks: item.networks.map((n)=>n.toEntity()).toList(),
ignore: item.ignore,
suspect: item.suspect,
frequentPlace: item.frequentPlace,
)).toList();
return items
.map(
(LatestPositionsItemResponseModel item) => PositionEntity(
id: item.id,
deviceIdentificator: item.deviceIdentificator,
latitude: item.latitude,
longitude: item.longitude,
hpe: item.hpe,
ncell: item.ncell,
type: item.type,
steps: item.steps,
address: item.address?.toEntity(),
createdAt: item.createdAt,
positionDate: item.positionDate,
positionDateOriginal: item.positionDateOriginal,
frequentPlaceName: item.frequentPlaceName,
message: item.message,
networks: item.networks.map((n) => n.toEntity()).toList(),
ignore: item.ignore,
suspect: item.suspect,
frequentPlace: item.frequentPlace,
),
)
.toList();
}
}
@freezed
abstract class LatestPositionsAddressResponseModel with _$LatestPositionsAddressResponseModel {
abstract class LatestPositionsAddressResponseModel
with _$LatestPositionsAddressResponseModel {
const factory LatestPositionsAddressResponseModel({
String? street,
String? city,
@@ -79,11 +88,13 @@ abstract class LatestPositionsAddressResponseModel with _$LatestPositionsAddress
String? country,
}) = _LatestPositionsAddressResponseModel;
factory LatestPositionsAddressResponseModel.fromJson(Map<String, dynamic> json) =>
_$LatestPositionsAddressResponseModelFromJson(json);
factory LatestPositionsAddressResponseModel.fromJson(
Map<String, dynamic> json,
) => _$LatestPositionsAddressResponseModelFromJson(json);
}
extension LatestPositionsAddressResponseModelMapper on LatestPositionsAddressResponseModel {
extension LatestPositionsAddressResponseModelMapper
on LatestPositionsAddressResponseModel {
AddressEntity toEntity() {
return AddressEntity(
street: street,
@@ -96,23 +107,22 @@ extension LatestPositionsAddressResponseModelMapper on LatestPositionsAddressRes
}
@freezed
abstract class LatestPositionsNetworkResponseModel with _$LatestPositionsNetworkResponseModel {
abstract class LatestPositionsNetworkResponseModel
with _$LatestPositionsNetworkResponseModel {
const factory LatestPositionsNetworkResponseModel({
required String SSID,
required String BSSID,
required String signal,
}) = _LatestPositionsNetworkResponseModel;
factory LatestPositionsNetworkResponseModel.fromJson(Map<String, dynamic> json) =>
_$LatestPositionsNetworkResponseModelFromJson(json);
factory LatestPositionsNetworkResponseModel.fromJson(
Map<String, dynamic> json,
) => _$LatestPositionsNetworkResponseModelFromJson(json);
}
extension LatestPositionsNetworkResponseModelMapper on LatestPositionsNetworkResponseModel {
extension LatestPositionsNetworkResponseModelMapper
on LatestPositionsNetworkResponseModel {
NetworkEntity toEntity() {
return NetworkEntity(
SSID: SSID,
BSSID: BSSID,
signal: signal,
);
return NetworkEntity(SSID: SSID, BSSID: BSSID, signal: signal);
}
}
}

View File

@@ -1,3 +1,5 @@
// ignore_for_file: non_constant_identifier_names
import 'package:freezed_annotation/freezed_annotation.dart';
part 'network_entity.freezed.dart';

View File

@@ -1,3 +1 @@
library customer_service;
export 'src/customer_service_builder.dart';

View File

@@ -23,24 +23,26 @@ class ContactScreen extends ConsumerWidget {
title: context.translate(I18n.contactTitle),
body: Padding(
padding: EdgeInsets.symmetric(
horizontal: SizeUtils.getByScreen(small: 38, big: 36)
horizontal: SizeUtils.getByScreen(small: 38, big: 36),
),
child: SingleChildScrollView(
child: Column(
children: [
const _CountrySection(),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
const _ChannelSection(),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
const _NameSection(),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
const _EmailSection(),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
const _SubjectSection(),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
_MessageSection(onSubmit: viewModel.sendEmail),
const _ErrorMessageSection(),
],
),
),
child: SingleChildScrollView(child: Column(
children: [
const _CountrySection(),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
const _ChannelSection(),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
const _NameSection(),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
const _EmailSection(),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
const _SubjectSection(),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
_MessageSection(onSubmit: viewModel.sendEmail),
const _ErrorMessageSection(),
],
)),
),
footer: _SendSection(onSend: viewModel.sendEmail),
);
@@ -48,12 +50,10 @@ class ContactScreen extends ConsumerWidget {
}
class _CountrySection extends ConsumerWidget {
const _CountrySection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final List<String> countries = [
'España',
'Portugal',
@@ -66,20 +66,20 @@ class _CountrySection extends ConsumerWidget {
final vm = ref.read(contactViewModelProvider.notifier);
return CustomDropdown(
items: countries.map(Text.new).toList(growable: false),
onChanged: (x){vm.setCountry(x);},
hint: context.translate(I18n.selectCountry)
items: countries.map(Text.new).toList(growable: false),
onChanged: (x) {
vm.setCountry(x);
},
hint: context.translate(I18n.selectCountry),
);
}
}
class _ChannelSection extends ConsumerWidget {
const _ChannelSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final List<String> channels = [
context.translate(I18n.channelOnline),
context.translate(I18n.channelAmazon),
@@ -90,20 +90,20 @@ class _ChannelSection extends ConsumerWidget {
final vm = ref.read(contactViewModelProvider.notifier);
return CustomDropdown(
items: channels.map(Text.new).toList(growable: false),
onChanged: (x){vm.setChannel(x);},
hint: context.translate(I18n.selectChannel)
items: channels.map(Text.new).toList(growable: false),
onChanged: (x) {
vm.setChannel(x);
},
hint: context.translate(I18n.selectChannel),
);
}
}
class _NameSection extends ConsumerWidget {
const _NameSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final vm = ref.read(contactViewModelProvider.notifier);
return CustomTextField(
@@ -114,12 +114,10 @@ class _NameSection extends ConsumerWidget {
}
class _EmailSection extends ConsumerWidget {
const _EmailSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final vm = ref.read(contactViewModelProvider.notifier);
return CustomTextField(
@@ -131,12 +129,10 @@ class _EmailSection extends ConsumerWidget {
}
class _SubjectSection extends ConsumerWidget {
const _SubjectSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final vm = ref.read(contactViewModelProvider.notifier);
return CustomTextField(
@@ -147,16 +143,12 @@ class _SubjectSection extends ConsumerWidget {
}
class _MessageSection extends ConsumerWidget {
final VoidCallback onSubmit;
const _MessageSection({
required this.onSubmit,
});
const _MessageSection({required this.onSubmit});
@override
Widget build(BuildContext context, WidgetRef ref) {
final vm = ref.read(contactViewModelProvider.notifier);
return CustomTextField(
@@ -170,12 +162,10 @@ class _MessageSection extends ConsumerWidget {
}
class _ErrorMessageSection extends ConsumerWidget {
const _ErrorMessageSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final viewState = ref.watch(contactViewModelProvider);
if (viewState.errorMessage.isNotEmpty) {
@@ -192,34 +182,31 @@ class _ErrorMessageSection extends ConsumerWidget {
),
],
);
} else return SizedBox.shrink();
} else {
return SizedBox.shrink();
}
}
}
class _SendSection extends ConsumerWidget {
final VoidCallback onSend;
const _SendSection({
required this.onSend,
});
const _SendSection({required this.onSend});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.read(themePortProvider);
return Container(
padding: SizeUtils.getByScreen(
small: EdgeInsets.symmetric(horizontal: 38, vertical: 14),
big: EdgeInsets.symmetric(horizontal: 36, vertical: 12)
big: EdgeInsets.symmetric(horizontal: 36, vertical: 12),
),
child: PrimaryButton(
onPressed: onSend,
text: context.translate(I18n.sendEmail),
color: theme.getColorFor(ThemeCode.buttonPrimary)
)
color: theme.getColorFor(ThemeCode.buttonPrimary),
),
);
}
}
}

View File

@@ -1,5 +1,6 @@
name: customer_service
description: "A new Flutter project."
publish_to: 'none'
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43

View File

@@ -1,5 +1,3 @@
library functions;
export 'src/features/device_management/device_management_builder.dart';
export 'src/features/contacts/contacts_builder.dart';
export 'src/features/contacts/edit_contact_builder.dart';
@@ -12,4 +10,4 @@ export 'src/features/activity_meter/activity_meter_builder.dart';
export 'src/features/apps_use/apps_use_builder.dart';
export 'src/features/volume_control/volume_control_builder.dart';
export 'src/features/call_history/call_history_builder.dart';
export 'src/features/background_image/background_image_builder.dart';
export 'src/features/background_image/background_image_builder.dart';

View File

@@ -134,7 +134,7 @@ class _NewContactDialogState extends ConsumerState<NewContactDialog> {
name: _nameController.text,
phone: _phoneController.text,
);
if (success && mounted) Navigator.pop(context);
if (success && context.mounted) Navigator.pop(context);
},
text: context.translate(I18n.save),
color: theme.getColorFor(ThemeCode.legacyPrimary),

View File

@@ -3,7 +3,6 @@ import 'dart:async';
import 'package:device_management/src/core/providers/pictures_repository_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:device_management/src/features/remote_connection/domain/entities/picture_entity.dart';
import 'package:device_management/src/features/remote_connection/presentation/state/remote_connection_view_state.dart';
import 'package:legacy_shared/legacy_shared.dart';

View File

@@ -7,7 +7,7 @@ import 'package:device_management/src/features/remote_connection/presentation/st
import 'package:utils/utils.dart';
class ShowPictureDialog extends ConsumerWidget {
const ShowPictureDialog();
const ShowPictureDialog({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {

View File

@@ -7,7 +7,7 @@ import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
class SpyCallDialog extends ConsumerWidget {
const SpyCallDialog();
const SpyCallDialog({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {

View File

@@ -1,5 +1,6 @@
name: device_management
description: "A new Flutter project."
publish_to: 'none'
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43

View File

@@ -1,5 +1,5 @@
import 'package:legacy_auth/src/core/data/models/two_fa_secret_response_model.dart';
import 'package:legacy_shared/src/utils/dio_error_mapper.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'auth_remote_datasource.dart';

View File

@@ -1,5 +1,4 @@
import 'package:legacy_auth/src/core/data/datasource/device_setup_remote_datasource.dart';
import 'package:legacy_shared/src/utils/dio_error_mapper.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';

View File

@@ -1,7 +1,7 @@
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'package:legacy_auth/src/core/data/datasource/login_remote_datasource.dart';
import 'package:legacy_auth/src/core/data/models/login_response_model.dart';
import 'package:legacy_shared/src/utils/dio_error_mapper.dart';
import 'package:legacy_shared/legacy_shared.dart';
class LegacyLoginRemoteDatasourceImpl implements LegacyLoginRemoteDatasource {
const LegacyLoginRemoteDatasourceImpl(this._repository);

View File

@@ -1,6 +1,6 @@
import 'package:legacy_auth/src/core/data/models/sign_up_request_model.dart';
import 'package:legacy_auth/src/core/data/models/sign_up_response_model.dart';
import 'package:legacy_shared/src/utils/dio_error_mapper.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'sign_up_remote_datasource.dart';

View File

@@ -44,6 +44,7 @@ class LegacyScanWatchStepScreen extends ConsumerWidget {
MaterialPageRoute(builder: (_) => const LegacyQrScannerScreen()),
);
if (result == null || result.isEmpty) return;
if (!context.mounted) return;
vm.onWatchQrScanned(result);
showActivationCodeDialog(context);
},

View File

@@ -4,6 +4,7 @@ import 'package:legacy_auth/src/features/link_phone/domain/use_cases/link_phone_
class LegacyLinkPhoneUseCaseImpl implements LegacyLinkPhoneUseCase {
LegacyLinkPhoneUseCaseImpl(this._repository);
// ignore: unused_field
final LegacyAuthRepository _repository;
@override

View File

@@ -117,6 +117,7 @@ class LegacyRequestRecoveryScreen extends ConsumerWidget {
child: PrimaryButton(
onPressed: () async {
await viewModel.requestRecovery();
if (!context.mounted) return;
final updatedState = ref.read(
legacyRecoverPasswordViewModelProvider,
);

View File

@@ -2,7 +2,7 @@ import 'dart:async';
import 'package:legacy_auth/src/core/domain/repositories/sign_up_repository.dart';
import 'package:legacy_auth/src/core/providers/sign_up_repository_provider.dart';
import 'package:legacy_shared/src/utils/dio_error_mapper.dart';
import 'package:legacy_shared/legacy_shared.dart';
import 'package:legacy_auth/src/core/utils/text_format_utils.dart';
import 'package:legacy_auth/src/features/sign_up/domain/entities/address_entity.dart';
import 'package:legacy_auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart';
@@ -90,8 +90,6 @@ class LegacySignUpViewModel extends Notifier<LegacySignUpViewState>
repeatPasswordController = TextEditingController(text: s.repeatPassword);
}
void _syncField(
String text, {
required String current,
@@ -107,14 +105,9 @@ class LegacySignUpViewModel extends Notifier<LegacySignUpViewState>
required LegacyAddressViewState Function() updateAddress,
}) {
if (text == current) return;
state = state.copyWith(
address: updateAddress(),
errorMessage: '',
);
state = state.copyWith(address: updateAddress(), errorMessage: '');
}
void _addListeners() {
firstNameController.addListener(_onFirstNameChanged);
lastNameController.addListener(_onLastNameChanged);
@@ -144,10 +137,8 @@ class LegacySignUpViewModel extends Notifier<LegacySignUpViewState>
_syncField(
firstNameController.text,
current: state.firstName,
update: () => state.copyWith(
firstName: firstNameController.text,
errorMessage: '',
),
update: () =>
state.copyWith(firstName: firstNameController.text, errorMessage: ''),
);
}
@@ -156,10 +147,8 @@ class LegacySignUpViewModel extends Notifier<LegacySignUpViewState>
_syncField(
lastNameController.text,
current: state.lastName,
update: () => state.copyWith(
lastName: lastNameController.text,
errorMessage: '',
),
update: () =>
state.copyWith(lastName: lastNameController.text, errorMessage: ''),
);
}
@@ -176,22 +165,22 @@ class LegacySignUpViewModel extends Notifier<LegacySignUpViewState>
}
void _onDocumentTypeChanged() => _syncField(
documentTypeController.text,
current: state.documentType,
update: () => state.copyWith(
documentType: documentTypeController.text,
errorMessage: '',
),
);
documentTypeController.text,
current: state.documentType,
update: () => state.copyWith(
documentType: documentTypeController.text,
errorMessage: '',
),
);
void _onRelationshipChanged() => _syncField(
relationshipController.text,
current: state.relationType,
update: () => state.copyWith(
relationType: relationshipController.text,
errorMessage: '',
),
);
relationshipController.text,
current: state.relationType,
update: () => state.copyWith(
relationType: relationshipController.text,
errorMessage: '',
),
);
void _onPlaceOfBirthChanged() {
toCapitalizedController(placeOfBirthController);
@@ -206,13 +195,13 @@ class LegacySignUpViewModel extends Notifier<LegacySignUpViewState>
}
void _onBirthCountryChanged() => _syncField(
birthCountryController.text,
current: state.birthCountry,
update: () => state.copyWith(
birthCountry: birthCountryController.text,
errorMessage: '',
),
);
birthCountryController.text,
current: state.birthCountry,
update: () => state.copyWith(
birthCountry: birthCountryController.text,
errorMessage: '',
),
);
void _onPhoneChanged() {
final text = phoneController.text;
@@ -325,11 +314,11 @@ class LegacySignUpViewModel extends Notifier<LegacySignUpViewState>
}
void _onAddressCountryChanged() => _syncAddressField(
addressCountryController.text,
current: state.address.country,
updateAddress: () =>
state.address.copyWith(country: addressCountryController.text),
);
addressCountryController.text,
current: state.address.country,
updateAddress: () =>
state.address.copyWith(country: addressCountryController.text),
);
void _onAddressPostCodeChanged() {
final text = addressPostCodeController.text.trim();
@@ -353,8 +342,6 @@ class LegacySignUpViewModel extends Notifier<LegacySignUpViewState>
}
}
void next() {
if (state.isLoading) return;
@@ -386,8 +373,6 @@ class LegacySignUpViewModel extends Notifier<LegacySignUpViewState>
);
}
void updateDialCode(String dialCode) {
state = state.copyWith(dialCode: dialCode);
}
@@ -449,8 +434,6 @@ class LegacySignUpViewModel extends Notifier<LegacySignUpViewState>
state = state.copyWith(isShowPassword: !state.isShowPassword);
}
bool _validateStep0() {
final emailErr = emailErrorFor(state.email);
final phoneErr = phoneErrorFor(state.phone);
@@ -544,8 +527,6 @@ class LegacySignUpViewModel extends Notifier<LegacySignUpViewState>
bool _validateForm() =>
_validateStep0() && _validateStep1() && _validateStep2();
Future<bool> signUp() async {
if (state.isLoading) return false;
if (!_validateForm()) return false;
@@ -573,13 +554,11 @@ class LegacySignUpViewModel extends Notifier<LegacySignUpViewState>
if (!ref.mounted) return false;
final msg = formatErrorMessage(e);
final errorMsg =
msg == 'BadRequest' ? I18n.errorEmailAlreadyRegistered : msg;
final errorMsg = msg == 'BadRequest'
? I18n.errorEmailAlreadyRegistered
: msg;
state = state.copyWith(
isLoading: false,
errorMessage: errorMsg,
);
state = state.copyWith(isLoading: false, errorMessage: errorMsg);
return false;
}
}

View File

@@ -1,3 +1,5 @@
// ignore_for_file: non_constant_identifier_names
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:location/src/core/domain/entities/frequent_place_entity.dart';

View File

@@ -4,5 +4,6 @@ import 'package:sf_infrastructure/sf_infrastructure.dart';
class SettingsRemoteDatasourceImpl implements SettingsRemoteDatasource {
SettingsRemoteDatasourceImpl(this._repository);
// ignore: unused_field
final QuestiaRepository _repository;
}

View File

@@ -4,5 +4,6 @@ import 'package:settings/src/core/domain/repositories/settings_repository.dart';
class SettingsRepositoryImpl implements SettingsRepository {
const SettingsRepositoryImpl(this._remote);
// ignore: unused_field
final SettingsRemoteDatasource _remote;
}

View File

@@ -5,6 +5,7 @@ import 'shutdown_use_case.dart';
class ShutdownUseCaseImpl implements ShutdownUseCase {
ShutdownUseCaseImpl(this._repository);
// ignore: unused_field
final SettingsRepository _repository;
@override

View File

@@ -47,7 +47,6 @@ class _OptionsSection extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(remoteManagementViewModelProvider);
final vm = ref.read(remoteManagementViewModelProvider.notifier);
return SingleChildScrollView(
@@ -63,6 +62,7 @@ class _OptionsSection extends ConsumerWidget {
message: context.translate(I18n.remoteTurnOffConfirm),
onConfirm: () async {
await vm.shutdown();
if (!context.mounted) return;
final errorMessage = ref.read(
remoteManagementViewModelProvider.select((s)=>s.errorMessage)

View File

@@ -10,7 +10,8 @@ class ConfirmDialog extends ConsumerWidget {
final VoidCallback onConfirm;
const ConfirmDialog({
required this.title,
super.key, required this.title,
required this.message,
required this.onConfirm,
});

View File

@@ -6,6 +6,7 @@ class SyncClockUseCaseImpl implements SyncClockUseCase {
SyncClockUseCaseImpl(this._repository);
// ignore: unused_field
final SettingsRepository _repository;
@override

View File

@@ -1,4 +1,3 @@
import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'sync_clock_view_state.freezed.dart';

View File

@@ -37,6 +37,7 @@ class SyncClockScreen extends ConsumerWidget {
}
}
// ignore: unused_element
class _OptionsSection extends ConsumerWidget {
const _OptionsSection();

View File

@@ -1,5 +1,6 @@
name: settings
description: "A new Flutter project."
publish_to: 'none'
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43

View File

@@ -1,5 +1,3 @@
library legacy_shared;
export 'src/providers/map_style_provider.dart';
export 'src/providers/selected_device_provider.dart';
export 'src/widgets/layouts/page_layout.dart';
@@ -17,4 +15,4 @@ export 'src/providers/commands_repository_provider.dart';
export 'src/domain/repositories/devices_repository.dart';
export 'src/providers/devices_repository_provider.dart';
export 'src/data/datasources/device_settings_update_datasource.dart';
export 'src/providers/device_settings_update_provider.dart';
export 'src/providers/device_settings_update_provider.dart';

View File

@@ -1,9 +1,7 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:utils/utils.dart';
class AppMenuButton extends StatelessWidget {
final GestureTapCallback onPressed;
final IconData icon;
final bool negativeIcon;
@@ -12,6 +10,7 @@ class AppMenuButton extends StatelessWidget {
final Color color;
const AppMenuButton({
super.key,
required this.onPressed,
required this.icon,
this.negativeIcon = false,
@@ -22,43 +21,40 @@ class AppMenuButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return TextButton(
onPressed: onPressed,
style: ButtonStyle(
overlayColor: WidgetStatePropertyAll(Color(0xFFF7F7F7))
overlayColor: WidgetStatePropertyAll(Color(0xFFF7F7F7)),
),
child: Row(
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: negativeIcon
? Colors.white
: color,
color: negativeIcon ? Colors.white : color,
),
height: SizeUtils.getByScreen(small: 52, big: 48),
width: SizeUtils.getByScreen(small: 52, big: 48),
child: Icon(icon,
child: Icon(
icon,
size: iconSize ?? SizeUtils.getByScreen(small: 52, big: 48),
color: negativeIcon
? color
: Colors.white,
color: negativeIcon ? color : Colors.white,
weight: 30,
),
),
SizedBox(width: SizeUtils.getByScreen(small: 16, big: 15)),
Expanded(
child: Text(text,
child: Text(
text,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 18, big: 19),
fontWeight: FontWeight.w500,
color: Color(0xFF4B4B4B)
)
)
)
color: Color(0xFF4B4B4B),
),
),
),
],
),
);
}
}
}

View File

@@ -9,7 +9,8 @@ class SectionButton extends StatelessWidget {
final Widget body;
const SectionButton({
required this.onPressed,
super.key, required this.onPressed,
required this.icon,
this.iconPadding,
required this.body,

View File

@@ -723,7 +723,7 @@ packages:
source: path
version: "0.0.1"
sf_localizations:
dependency: "direct overridden"
dependency: "direct main"
description:
path: "../../../../packages/sf_localizations"
relative: true

View File

@@ -1,5 +1,6 @@
name: legacy_shared
description: "A new Flutter project."
publish_to: 'none'
version: 0.0.1
homepage:
@@ -30,6 +31,7 @@ dependencies:
freezed: ^3.2.3
json_annotation: ^4.9.0
json_serializable: ^6.11.2
sf_localizations: ^0.0.1
dev_dependencies:
flutter_test:

View File

@@ -24,7 +24,6 @@ class ScanStrapAndWatchStepScreen extends ConsumerWidget {
final textPrimary = theme.getColorFor(ThemeCode.textPrimary);
final vm = ref.read(deviceSetupViewModelProvider.notifier);
final state = ref.watch(deviceSetupViewModelProvider);
return SingleChildScrollView(
child: Column(

View File

@@ -11,7 +11,6 @@ class SuccessStepScreen extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final state = ref.watch(deviceSetupViewModelProvider);
final vm = ref.read(deviceSetupViewModelProvider.notifier);
return Column(
mainAxisSize: MainAxisSize.max,

View File

@@ -4,6 +4,7 @@ import 'package:auth/src/features/link_phone/domain/use_cases/link_phone_use_cas
class LinkPhoneUseCaseImpl implements LinkPhoneUseCase {
LinkPhoneUseCaseImpl(this._repository);
// ignore: unused_field
final AuthRepository _repository;
@override

View File

@@ -312,6 +312,7 @@ class _SignInSection extends ConsumerWidget {
}
}
// ignore: unused_element
class _OrContinueWith extends StatelessWidget {
const _OrContinueWith({required this.theme});
final ThemePort theme;
@@ -334,6 +335,7 @@ class _OrContinueWith extends StatelessWidget {
}
}
// ignore: unused_element
class _SocialButtons extends ConsumerWidget {
const _SocialButtons({required this.theme});
final ThemePort theme;

View File

@@ -117,6 +117,7 @@ class RequestRecoveryScreen extends ConsumerWidget {
child: PrimaryButton(
onPressed: () async {
await viewModel.requestRecovery();
if (!context.mounted) return;
final updatedState = ref.read(
recoverPasswordViewModelProvider,
);

View File

@@ -28,11 +28,20 @@ class ExtractScreen extends ConsumerWidget {
ref.listen(extractViewModelProvider(childId), (prev, next) {
if (next.success && !(prev?.success ?? false)) {
showTopSnackbar(context, message: context.translate(I18n.walletMoveSuccess), type: MessageType.success);
showTopSnackbar(
context,
message: context.translate(I18n.walletMoveSuccess),
type: MessageType.success,
);
navigation.goBack();
}
if (next.errorMessage.isNotEmpty && next.errorMessage != (prev?.errorMessage ?? '')) {
showTopSnackbar(context, message: context.translate(I18n.walletMoveError), type: MessageType.error);
if (next.errorMessage.isNotEmpty &&
next.errorMessage != (prev?.errorMessage ?? '')) {
showTopSnackbar(
context,
message: context.translate(I18n.walletMoveError),
type: MessageType.error,
);
}
});
@@ -53,8 +62,37 @@ class ExtractScreen extends ConsumerWidget {
return WalletManagementLayout(
childName: childName,
balance: availableBalance,
cardColors: cardColorsFor(theme: theme, carrierGenre: viewState.device?.carrierGenre, cardStatus: cardStatus),
cardColors: cardColorsFor(
theme: theme,
carrierGenre: viewState.device?.carrierGenre,
cardStatus: cardStatus,
),
navigation: navigation,
footer: FooterContainer(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
primaryColor: theme.getColorFor(ThemeCode.buttonPrimary),
primaryText: viewState.isSubmitting
? context.translate(I18n.walletMoveProcessing)
: context.translate(I18n.sendMessageAndBlock),
onPrimaryPressed: viewState.isSubmitting
? null
: () {
final amount = double.tryParse(
viewModel.amountController.text.trim(),
);
if (amount == null || amount <= 0) {
showTopSnackbar(
context,
message: context.translate(I18n.walletMoveAmountRequired),
type: MessageType.warning,
);
return;
}
viewModel.submit();
},
cancelText: context.translate(I18n.cancel),
onCancelPressed: () => navigation.goBack(),
),
children: [
SectionContainer(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
@@ -95,9 +133,10 @@ class ExtractScreen extends ConsumerWidget {
context.translate(
I18n.allowanceBalanceAfter,
args: {
'amount': (availableBalance -
(double.tryParse(viewState.amount) ?? 0))
.toStringAsFixed(2),
'amount':
(availableBalance -
(double.tryParse(viewState.amount) ?? 0))
.toStringAsFixed(2),
},
),
),
@@ -131,7 +170,10 @@ class ExtractScreen extends ConsumerWidget {
Align(
alignment: Alignment.topLeft,
child: Text(
context.translate(I18n.extractMessageLabel, args: {'name': childName}),
context.translate(
I18n.extractMessageLabel,
args: {'name': childName},
),
style: TextStyle(fontSize: 14, letterSpacing: 0),
),
),
@@ -146,7 +188,10 @@ class ExtractScreen extends ConsumerWidget {
children: [
Icon(Icons.info_outline, size: 16),
Text(
context.translate(I18n.allowanceMaxChars, args: {'count': '150'}),
context.translate(
I18n.allowanceMaxChars,
args: {'count': '150'},
),
style: TextStyle(fontSize: 14, letterSpacing: 0),
),
],
@@ -156,25 +201,6 @@ class ExtractScreen extends ConsumerWidget {
],
),
],
footer: FooterContainer(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
primaryColor: theme.getColorFor(ThemeCode.buttonPrimary),
primaryText: viewState.isSubmitting
? context.translate(I18n.walletMoveProcessing)
: context.translate(I18n.sendMessageAndBlock),
onPrimaryPressed: viewState.isSubmitting
? null
: () {
final amount = double.tryParse(viewModel.amountController.text.trim());
if (amount == null || amount <= 0) {
showTopSnackbar(context, message: context.translate(I18n.walletMoveAmountRequired), type: MessageType.warning);
return;
}
viewModel.submit();
},
cancelText: context.translate(I18n.cancel),
onCancelPressed: () => navigation.goBack(),
),
);
}
}

View File

@@ -44,8 +44,13 @@ class GoalsScreen extends ConsumerWidget {
return WalletManagementLayout(
childName: childName,
balance: availableBalance,
cardColors: cardColorsFor(theme: theme, carrierGenre: viewState.device?.carrierGenre, cardStatus: cardStatus),
cardColors: cardColorsFor(
theme: theme,
carrierGenre: viewState.device?.carrierGenre,
cardStatus: cardStatus,
),
navigation: navigation,
footer: Container(),
children: [
SectionContainer(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
@@ -86,7 +91,6 @@ class GoalsScreen extends ConsumerWidget {
childId: childId,
),
],
footer: Container(),
);
}
}
@@ -123,7 +127,7 @@ class SavingsBlockState extends ConsumerState<SavingsBlock> {
Widget build(BuildContext context) {
final theme = ref.watch(themePortProvider);
var emptyBlock = ({fullPlan}) => Container(
Container emptyBlock({fullPlan}) => Container(
padding: EdgeInsets.all(24),
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
@@ -213,7 +217,7 @@ class SavingsBlockState extends ConsumerState<SavingsBlock> {
),
);
var editBlock = ({create, index}) => Column(
Column editBlock({create, index}) => Column(
spacing: 24,
children: [
CustomTextField(
@@ -292,7 +296,10 @@ class SavingsBlockState extends ConsumerState<SavingsBlock> {
children: [
Icon(Icons.info_outline, size: 16),
Text(
context.translate(I18n.allowanceMaxChars, args: {'count': '150'}),
context.translate(
I18n.allowanceMaxChars,
args: {'count': '150'},
),
style: TextStyle(fontSize: 14, letterSpacing: 0),
),
],
@@ -492,7 +499,9 @@ class SavingsBlockState extends ConsumerState<SavingsBlock> {
ThemeCode.textSecondary,
),
),
Center(child: Text(context.translate(I18n.goalsSaved))),
Center(
child: Text(context.translate(I18n.goalsSaved)),
),
if (!showAdd[index])
TextButton(
style: ButtonStyle(
@@ -637,7 +646,7 @@ class TasksBlockState extends ConsumerState<TasksBlock> {
final theme = ref.watch(themePortProvider);
final viewModel = ref.read(goalsViewModelProvider(widget.childId).notifier);
final emptyBlock = ({fullPlan}) => Container(
Container emptyBlock({fullPlan}) => Container(
padding: EdgeInsets.all(24),
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
@@ -818,7 +827,9 @@ class TasksBlockState extends ConsumerState<TasksBlock> {
children: [
Expanded(
child: Text(
context.translate(I18n.goalsRewardForCompletion),
context.translate(
I18n.goalsRewardForCompletion,
),
style: TextStyle(
fontSize: 16,
letterSpacing: 0,
@@ -854,14 +865,18 @@ class TasksBlockState extends ConsumerState<TasksBlock> {
Align(
alignment: Alignment.topLeft,
child: Text(
context.translate(I18n.goalsTasksCompletedMessagePrefix),
context.translate(
I18n.goalsTasksCompletedMessagePrefix,
),
style: TextStyle(fontSize: 14, letterSpacing: 0),
),
),
Align(
alignment: Alignment.topLeft,
child: Text(
context.translate(I18n.goalsTasksCompletedMessage),
context.translate(
I18n.goalsTasksCompletedMessage,
),
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,

View File

@@ -188,17 +188,13 @@ class WalletItem extends ConsumerWidget {
Widget _buildGenderAvatar(String? carrierGenre, double size) {
final IconData icon;
final Color color;
switch (carrierGenre) {
case 'M':
icon = Icons.face;
color = const Color(0xFF64B5F6);
case 'F':
icon = Icons.face_3;
color = const Color(0xFFF48FB1);
default:
icon = Icons.face_2;
color = const Color(0xFF90A4AE);
}
return CircleAvatar(
radius: size / 2,
@@ -209,6 +205,8 @@ class WalletItem extends ConsumerWidget {
}
class PhotoDialog extends ConsumerWidget {
const PhotoDialog({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);

View File

@@ -20,6 +20,7 @@ dependencies:
path: ../../packages/sf_infrastructure
sf_shared:
path: ../../packages/sf_shared
shared_preferences: ^2.5.5
dev_dependencies:
flutter_test:
sdk: flutter

View File

@@ -1,29 +1,29 @@
/// Flutter icons SFIcons
/// Copyright (C) 2026 by original authors @ fluttericon.com, fontello.com
/// This font was generated by FlutterIcon.com, which is derived from Fontello.
///
/// To use this font, place it in your fonts/ directory and include the
/// following in your pubspec.yaml
///
/// flutter:
/// fonts:
/// - family: SFIcons
/// fonts:
/// - asset: fonts/SFIcons.ttf
///
///
/// * Material Design Icons, Copyright (C) Google, Inc
/// Author: Google
/// License: Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
/// Homepage: https://design.google.com/icons/
///
// Flutter icons SFIcons
// Copyright (C) 2026 by original authors @ fluttericon.com, fontello.com
// This font was generated by FlutterIcon.com, which is derived from Fontello.
//
// To use this font, place it in your fonts/ directory and include the
// following in your pubspec.yaml
//
// flutter:
// fonts:
// - family: SFIcons
// fonts:
// - asset: fonts/SFIcons.ttf
//
//
// * Material Design Icons, Copyright (C) Google, Inc
// Author: Google
// License: Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
// Homepage: https://design.google.com/icons/
//
import 'package:flutter/widgets.dart';
class SFIcons {
SFIcons._();
static const _kFontFam = 'SFIcons';
static const String? _kFontPkg = 'design_system';
static const String _kFontPkg = 'design_system';
static const IconData back = IconData(0xe800, fontFamily: _kFontFam, fontPackage: _kFontPkg);
static const IconData euro = IconData(0xe801, fontFamily: _kFontFam, fontPackage: _kFontPkg);

View File

@@ -16,7 +16,7 @@ void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('getPlatformVersion test', (WidgetTester tester) async {
final FlutterTreezorEntrustSdkBridge plugin = FlutterTreezorEntrustSdkBridge();
FlutterTreezorEntrustSdkBridge();
// The version string depends on the host platform running the test, so
// just assert that some non-empty string is returned.
});

View File

@@ -1,5 +1,6 @@
import 'package:flutter/services.dart';
import 'package:flutter_treezor_entrust_sdk_bridge/model/antelop_error.dart';
import 'dart:developer' show log;
class CustomerAuthenticatedSignatureModule {
static const _channel = MethodChannel('CustomerAuthenticatedSignature');
@@ -41,7 +42,7 @@ class CustomerAuthenticatedSignatureModule {
?.call();
break;
default:
print('Unknown event received: $eventName');
log('Unknown event received: $eventName');
}
});
}
@@ -86,7 +87,7 @@ class CustomerAuthenticatedSignatureModule {
?.call();
break;
default:
print('Unknown event received: $eventName');
log('Unknown event received: $eventName');
}
});
}
@@ -101,7 +102,7 @@ class CustomerAuthenticatedSignatureModule {
'input': input,
});
} catch (e) {
print("Initialization error: $e");
log("Initialization error: $e");
}
}
@@ -114,7 +115,7 @@ class CustomerAuthenticatedSignatureModule {
defaultCustomerAuthenticatedProcessCallback);
await _channel.invokeMethod('signWithDefaultAuthentication');
} catch (e) {
print("signWithDefaultAuthentication error: $e");
log("signWithDefaultAuthentication error: $e");
}
}
@@ -127,7 +128,7 @@ class CustomerAuthenticatedSignatureModule {
try {
await _channel.invokeMethod('signWithCustomAuthentication');
} catch (e) {
print("signWithCustomAuthentication error: $e");
log("signWithCustomAuthentication error: $e");
}
}
@@ -138,7 +139,7 @@ class CustomerAuthenticatedSignatureModule {
'passcode': passcode,
});
} catch (e) {
print("setPinCustomerCredentials error: $e");
log("setPinCustomerCredentials error: $e");
}
}
@@ -150,7 +151,7 @@ class CustomerAuthenticatedSignatureModule {
'subTitle': subTitle,
});
} catch (e) {
print("setBioCustomerCredentials error: $e");
log("setBioCustomerCredentials error: $e");
}
}
@@ -160,7 +161,7 @@ class CustomerAuthenticatedSignatureModule {
return await _channel
.invokeMethod<String>('getAuthenticationPatternName');
} catch (e) {
print("getAuthenticationPatternName error: $e");
log("getAuthenticationPatternName error: $e");
return null;
}
}
@@ -170,7 +171,7 @@ class CustomerAuthenticatedSignatureModule {
try {
return await _channel.invokeMethod<bool>('isOnline');
} catch (e) {
print("isOnline error: $e");
log("isOnline error: $e");
return null;
}
}
@@ -180,7 +181,7 @@ class CustomerAuthenticatedSignatureModule {
try {
return await _channel.invokeMethod<String>('getMessage');
} catch (e) {
print("getMessage error: $e");
log("getMessage error: $e");
return null;
}
}
@@ -190,7 +191,7 @@ class CustomerAuthenticatedSignatureModule {
try {
return await _channel.invokeMethod<String>('getType');
} catch (e) {
print("getType error: $e");
log("getType error: $e");
return null;
}
}
@@ -200,7 +201,7 @@ class CustomerAuthenticatedSignatureModule {
try {
return await _channel.invokeMethod<String>('getResult');
} catch (e) {
print("getResult error: $e");
log("getResult error: $e");
return null;
}
}
@@ -210,7 +211,7 @@ class CustomerAuthenticatedSignatureModule {
try {
return await _channel.invokeMethod<List<int>>('getInputData');
} catch (e) {
print("getInputData error: $e");
log("getInputData error: $e");
return null;
}
}
@@ -220,7 +221,7 @@ class CustomerAuthenticatedSignatureModule {
try {
await _channel.invokeMethod('clean');
} catch (e) {
print("clean error: $e");
log("clean error: $e");
}
}

View File

@@ -1,6 +1,7 @@
//import '../flutter_treezor_entrust_sdk_bridge_method_channel.dart';
import 'package:flutter/services.dart';
import 'package:flutter_treezor_entrust_sdk_bridge/model/antelop_error.dart';
import 'dart:developer' show log;
class WalletManagerModule {
static const _channel = MethodChannel('WalletManager');
@@ -41,7 +42,7 @@ class WalletManagerModule {
walletManagerCallbacks.onConnectionSuccess?.call();
break;
default:
print('Unknown event received: $eventName');
log('Unknown event received: $eventName');
}
});
}

View File

@@ -1,10 +1,12 @@
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_treezor_entrust_sdk_bridge/model/antelop_error.dart';
import 'dart:developer' show log;
class WalletNotificationService {
// Event channel name, should match the Android side.
static const _eventChannel = EventChannel("wallet_notification_service_events");
static const _eventChannel =
EventChannel("wallet_notification_service_events");
// Singleton instance
//static final WalletNotificationService _instance = WalletNotificationService._internal();
@@ -13,8 +15,8 @@ class WalletNotificationService {
//final StreamController<Map<String, dynamic>> _eventStreamController = StreamController.broadcast();
//WalletNotificationService._internal() {
// Initialize the event listener when the singleton is created.
//_eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
// Initialize the event listener when the singleton is created.
//_eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
//}
// Factory constructor to return the singleton instance.
@@ -23,7 +25,7 @@ class WalletNotificationService {
// Public stream to allow other parts of the app to listen for wallet events.
//Stream<Map<String, dynamic>> get events => _eventStreamController.stream;
// Initialize event callbacks
// Initialize event callbacks
void subscribeWalletEvent(WalletEvents walletEvents) {
eventStream.listen((event) {
final eventName = event['event'] as String;
@@ -50,12 +52,12 @@ class WalletNotificationService {
walletEvents.onCountersUpdated?.call();
break;
default:
print('Unknown event received: $eventName');
log('Unknown event received: $eventName');
}
});
}
/* // Handler for receiving events from the native platform.
/* // Handler for receiving events from the native platform.
void _onEvent(dynamic event) {
if (event is Map) {
// Add the event to the stream if it's in the expected format.
@@ -69,18 +71,15 @@ class WalletNotificationService {
final eventMap = Map<String, dynamic>.from(event);
// Parse the error if present
if (eventMap.containsKey('error') && eventMap['error'] is Map<String, dynamic>) {
eventMap['error'] = AntelopError.fromMap(Map<String, dynamic>.from(eventMap['error']));
if (eventMap.containsKey('error') &&
eventMap['error'] is Map<String, dynamic>) {
eventMap['error'] =
AntelopError.fromMap(Map<String, dynamic>.from(eventMap['error']));
}
return eventMap;
});
}
// Error handler for any errors occurring in the native event channel.
void _onError(dynamic error) {
print("Error receiving wallet notification: $error");
}
// Clean up resources when the instance is disposed.
/*void dispose() {
_eventStreamController.close();
@@ -95,12 +94,11 @@ class WalletEvents {
final void Function()? onWalletDeleted;
final void Function()? onCountersUpdated;
WalletEvents({
this.onWalletLoaded,
this.onWalletLocked,
this.onWalletUnlocked,
this.onLogout,
this.onWalletDeleted,
this.onCountersUpdated
});
}
WalletEvents(
{this.onWalletLoaded,
this.onWalletLocked,
this.onWalletUnlocked,
this.onLogout,
this.onWalletDeleted,
this.onCountersUpdated});
}

View File

@@ -1,5 +1,6 @@
import 'package:flutter/services.dart';
import 'package:flutter_treezor_entrust_sdk_bridge/model/antelop_error.dart';
import 'dart:developer' show log;
class WalletProvisioningModule {
static const _channel = MethodChannel('WalletProvisioning');
@@ -24,7 +25,7 @@ class WalletProvisioningModule {
break;
case 'onInitializationError':
final error = AntelopError.handleError(params?['error']);
if (error != null) callbacks.onInitializationError?.call(error);
callbacks.onInitializationError?.call(error);
break;
case 'onPermissionNotGranted':
final permissions = List<String>.from(params?['permissions'] ?? []);
@@ -42,7 +43,7 @@ class WalletProvisioningModule {
break;
case 'onCheckEligibilityError':
final error = AntelopError.handleError(params?['error']);
if (error != null) callbacks.onCheckEligibilityError?.call(error);
callbacks.onCheckEligibilityError?.call(error);
break;
case 'onProvisioningPending':
callbacks.onProvisioningPending?.call();
@@ -52,10 +53,10 @@ class WalletProvisioningModule {
break;
case 'onProvisioningError':
final error = AntelopError.handleError(params?['error']);
if (error != null) callbacks.onProvisioningError?.call(error);
callbacks.onProvisioningError?.call(error);
break;
default:
print('Unknown event received: $eventName');
log('Unknown event received: $eventName');
}
});
}
@@ -127,10 +128,6 @@ class WalletProvisioningModule {
}
// Helper method to parse AntelopError
AntelopError? _parseError(Map<String, dynamic>? errorMap) {
if (errorMap == null) return null;
return AntelopError.fromMap(errorMap);
}
}
// Callback class for handling Wallet Provisioning events

View File

@@ -1,4 +1,5 @@
import 'package:dio/dio.dart';
import 'dart:developer' show log;
class QuestiaApi {
final Dio _dio;
@@ -30,7 +31,7 @@ class QuestiaApi {
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
}) {
print('URI:${String.fromEnvironment('env')}');
log('URI:${String.fromEnvironment('env')}');
return _dio.post<T>(
path,
data: data,

View File

@@ -9,6 +9,7 @@ class TreezorTokenInterceptor extends Interceptor {
}) : _onTokenExpired = onTokenExpired,
_onUnauthorized = onUnauthorized;
// ignore: unused_field
final void Function() _onTokenExpired;
final void Function() _onUnauthorized;
bool _handling = false;
@@ -38,6 +39,7 @@ class TreezorTokenInterceptor extends Interceptor {
handler.next(err);
}
// ignore: unused_element
String? _extractApiMessage(Object? data) {
if (data == null) return null;

View File

@@ -27,30 +27,30 @@ abstract class DeviceModel with _$DeviceModel {
const factory DeviceModel({
required String id,
@JsonKey(fromJson: _toString) required String identificator,
@Default(null) int? battery,
@Default(null) String? userId,
@Default(null) String? companyId,
@Default(null) String? delegationId,
@Default(null) String? groupId,
int? battery,
String? userId,
String? companyId,
String? delegationId,
String? groupId,
@Default({}) Map<String, dynamic> flags,
@Default(null) List<String>? tags,
@Default(null) String? lastConnection,
@Default(null) String? carrierGenre,
@JsonKey(fromJson: _toNullableString) @Default(null) String? carrierBirthday,
@Default(null) int? carrierWeight,
@Default(null) int? carrierStepLength,
@Default(null) String? carrierName,
@Default(null) String? comment,
@Default(null) String? phone,
@Default(null) String? simId,
@Default(null) Map<String, dynamic>? paymentOptions,
List<String>? tags,
String? lastConnection,
String? carrierGenre,
@JsonKey(fromJson: _toNullableString) String? carrierBirthday,
int? carrierWeight,
int? carrierStepLength,
String? carrierName,
String? comment,
String? phone,
String? simId,
Map<String, dynamic>? paymentOptions,
@Default(DeviceSettingsModel()) DeviceSettingsModel settings,
@Default('') String connectionServer,
@Default('') String protocol,
@Default('') String type,
@Default(null) DeviceCapabilitiesModel? capabilities,
DeviceCapabilitiesModel? capabilities,
@JsonKey(fromJson: _toString) @Default('') String createdAt,
@JsonKey(fromJson: _toNullableString) @Default(null) String? updatedAt,
@JsonKey(fromJson: _toNullableString) String? updatedAt,
}) = _DeviceModel;
factory DeviceModel.fromJson(Map<String, dynamic> json) =>

View File

@@ -536,16 +536,16 @@ return $default(_that.id,_that.identificator,_that.battery,_that.userId,_that.co
@JsonSerializable()
class _DeviceModel extends DeviceModel {
const _DeviceModel({required this.id, @JsonKey(fromJson: _toString) required this.identificator, this.battery = null, this.userId = null, this.companyId = null, this.delegationId = null, this.groupId = null, final Map<String, dynamic> flags = const {}, final List<String>? tags = null, this.lastConnection = null, this.carrierGenre = null, @JsonKey(fromJson: _toNullableString) this.carrierBirthday = null, this.carrierWeight = null, this.carrierStepLength = null, this.carrierName = null, this.comment = null, this.phone = null, this.simId = null, final Map<String, dynamic>? paymentOptions = null, this.settings = const DeviceSettingsModel(), this.connectionServer = '', this.protocol = '', this.type = '', this.capabilities = null, @JsonKey(fromJson: _toString) this.createdAt = '', @JsonKey(fromJson: _toNullableString) this.updatedAt = null}): _flags = flags,_tags = tags,_paymentOptions = paymentOptions,super._();
const _DeviceModel({required this.id, @JsonKey(fromJson: _toString) required this.identificator, this.battery, this.userId, this.companyId, this.delegationId, this.groupId, final Map<String, dynamic> flags = const {}, final List<String>? tags, this.lastConnection, this.carrierGenre, @JsonKey(fromJson: _toNullableString) this.carrierBirthday, this.carrierWeight, this.carrierStepLength, this.carrierName, this.comment, this.phone, this.simId, final Map<String, dynamic>? paymentOptions, this.settings = const DeviceSettingsModel(), this.connectionServer = '', this.protocol = '', this.type = '', this.capabilities, @JsonKey(fromJson: _toString) this.createdAt = '', @JsonKey(fromJson: _toNullableString) this.updatedAt}): _flags = flags,_tags = tags,_paymentOptions = paymentOptions,super._();
factory _DeviceModel.fromJson(Map<String, dynamic> json) => _$DeviceModelFromJson(json);
@override final String id;
@override@JsonKey(fromJson: _toString) final String identificator;
@override@JsonKey() final int? battery;
@override@JsonKey() final String? userId;
@override@JsonKey() final String? companyId;
@override@JsonKey() final String? delegationId;
@override@JsonKey() final String? groupId;
@override final int? battery;
@override final String? userId;
@override final String? companyId;
@override final String? delegationId;
@override final String? groupId;
final Map<String, dynamic> _flags;
@override@JsonKey() Map<String, dynamic> get flags {
if (_flags is EqualUnmodifiableMapView) return _flags;
@@ -554,7 +554,7 @@ class _DeviceModel extends DeviceModel {
}
final List<String>? _tags;
@override@JsonKey() List<String>? get tags {
@override List<String>? get tags {
final value = _tags;
if (value == null) return null;
if (_tags is EqualUnmodifiableListView) return _tags;
@@ -562,17 +562,17 @@ class _DeviceModel extends DeviceModel {
return EqualUnmodifiableListView(value);
}
@override@JsonKey() final String? lastConnection;
@override@JsonKey() final String? carrierGenre;
@override final String? lastConnection;
@override final String? carrierGenre;
@override@JsonKey(fromJson: _toNullableString) final String? carrierBirthday;
@override@JsonKey() final int? carrierWeight;
@override@JsonKey() final int? carrierStepLength;
@override@JsonKey() final String? carrierName;
@override@JsonKey() final String? comment;
@override@JsonKey() final String? phone;
@override@JsonKey() final String? simId;
@override final int? carrierWeight;
@override final int? carrierStepLength;
@override final String? carrierName;
@override final String? comment;
@override final String? phone;
@override final String? simId;
final Map<String, dynamic>? _paymentOptions;
@override@JsonKey() Map<String, dynamic>? get paymentOptions {
@override Map<String, dynamic>? get paymentOptions {
final value = _paymentOptions;
if (value == null) return null;
if (_paymentOptions is EqualUnmodifiableMapView) return _paymentOptions;
@@ -584,7 +584,7 @@ class _DeviceModel extends DeviceModel {
@override@JsonKey() final String connectionServer;
@override@JsonKey() final String protocol;
@override@JsonKey() final String type;
@override@JsonKey() final DeviceCapabilitiesModel? capabilities;
@override final DeviceCapabilitiesModel? capabilities;
@override@JsonKey(fromJson: _toString) final String createdAt;
@override@JsonKey(fromJson: _toNullableString) final String? updatedAt;

View File

@@ -18,27 +18,23 @@ Map<String, dynamic> _$DeviceResponseModelToJson(
_DeviceModel _$DeviceModelFromJson(Map<String, dynamic> json) => _DeviceModel(
id: json['id'] as String,
identificator: _toString(json['identificator']),
battery: (json['battery'] as num?)?.toInt() ?? null,
userId: json['userId'] as String? ?? null,
companyId: json['companyId'] as String? ?? null,
delegationId: json['delegationId'] as String? ?? null,
groupId: json['groupId'] as String? ?? null,
battery: (json['battery'] as num?)?.toInt(),
userId: json['userId'] as String?,
companyId: json['companyId'] as String?,
delegationId: json['delegationId'] as String?,
groupId: json['groupId'] as String?,
flags: json['flags'] as Map<String, dynamic>? ?? const {},
tags:
(json['tags'] as List<dynamic>?)?.map((e) => e as String).toList() ??
null,
lastConnection: json['lastConnection'] as String? ?? null,
carrierGenre: json['carrierGenre'] as String? ?? null,
carrierBirthday: json['carrierBirthday'] == null
? null
: _toNullableString(json['carrierBirthday']),
carrierWeight: (json['carrierWeight'] as num?)?.toInt() ?? null,
carrierStepLength: (json['carrierStepLength'] as num?)?.toInt() ?? null,
carrierName: json['carrierName'] as String? ?? null,
comment: json['comment'] as String? ?? null,
phone: json['phone'] as String? ?? null,
simId: json['simId'] as String? ?? null,
paymentOptions: json['paymentOptions'] as Map<String, dynamic>? ?? null,
tags: (json['tags'] as List<dynamic>?)?.map((e) => e as String).toList(),
lastConnection: json['lastConnection'] as String?,
carrierGenre: json['carrierGenre'] as String?,
carrierBirthday: _toNullableString(json['carrierBirthday']),
carrierWeight: (json['carrierWeight'] as num?)?.toInt(),
carrierStepLength: (json['carrierStepLength'] as num?)?.toInt(),
carrierName: json['carrierName'] as String?,
comment: json['comment'] as String?,
phone: json['phone'] as String?,
simId: json['simId'] as String?,
paymentOptions: json['paymentOptions'] as Map<String, dynamic>?,
settings: json['settings'] == null
? const DeviceSettingsModel()
: DeviceSettingsModel.fromJson(json['settings'] as Map<String, dynamic>),
@@ -51,9 +47,7 @@ _DeviceModel _$DeviceModelFromJson(Map<String, dynamic> json) => _DeviceModel(
json['capabilities'] as Map<String, dynamic>,
),
createdAt: json['createdAt'] == null ? '' : _toString(json['createdAt']),
updatedAt: json['updatedAt'] == null
? null
: _toNullableString(json['updatedAt']),
updatedAt: _toNullableString(json['updatedAt']),
);
Map<String, dynamic> _$DeviceModelToJson(_DeviceModel instance) =>

View File

@@ -40,7 +40,7 @@ class WalletBalanceBlock extends ConsumerWidget {
MoneyText(
text: context.translate(
I18n.walletTotal,
args: {'amount': '${totalBalance.toStringAsFixed(2)}'},
args: {'amount': totalBalance.toStringAsFixed(2)},
),
size: 26,
secondarySize: 16,