usability fixes

This commit is contained in:
2026-06-01 20:49:53 +02:00
parent 7b5b962fe6
commit 1d8ab2088c
6 changed files with 167 additions and 38 deletions

View File

@@ -58,6 +58,7 @@ abstract class Category with _$Category {
@freezed
abstract class BillProvider with _$BillProvider {
const factory BillProvider({
required String id,
required String clientId,
required String name,
required String description,
@@ -100,7 +101,7 @@ class HomeController extends ChangeNotifier {
return ApiResponse.success(model.accounts);
}
Future<ApiResponse<List<BillProvider>>> getProviders() async {
Future<ApiResponse<List<BillProvider>>> getProviders(String currency) async {
try {
model.filterableProviders = getFakeProviders();
model.transactions = getFakeTransactions();
@@ -108,7 +109,9 @@ class HomeController extends ChangeNotifier {
model.isLoading = true;
if (!_disposed) notifyListeners();
final response = await http.get('/public/providers?sort=priority,asc');
final response = await http.get(
'/public/providers?currency=$currency&sort=priority,asc',
);
List<BillProvider> providers = PageableModel.fromJson(response).content
.map((e) => BillProvider.fromJson(e as Map<String, dynamic>))
.toList();
@@ -258,6 +261,7 @@ class HomeController extends ChangeNotifier {
List<BillProvider> getFakeProviders() {
return [
BillProvider(
id: '1',
clientId: 'econet_airtime',
name: 'Econet Airtime',
description: 'Econet Airtime',

View File

@@ -287,7 +287,7 @@ as String,
/// @nodoc
mixin _$BillProvider {
String get clientId; String get name; String get description; bool get requiresAccount; bool get requiresAmount; bool? get requiresAmountFromMerchant; bool get requiresPhone; bool? get requiresReversal; String? get additionalDataString; String? get processorType; String get uid; String get image; String get label; String get category; String? get accountFieldName;
String get id; String get clientId; String get name; String get description; bool get requiresAccount; bool get requiresAmount; bool? get requiresAmountFromMerchant; bool get requiresPhone; bool? get requiresReversal; String? get additionalDataString; String? get processorType; String get uid; String get image; String get label; String get category; String? get accountFieldName;
/// Create a copy of BillProvider
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -300,16 +300,16 @@ $BillProviderCopyWith<BillProvider> get copyWith => _$BillProviderCopyWithImpl<B
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is BillProvider&&(identical(other.clientId, clientId) || other.clientId == clientId)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.requiresAccount, requiresAccount) || other.requiresAccount == requiresAccount)&&(identical(other.requiresAmount, requiresAmount) || other.requiresAmount == requiresAmount)&&(identical(other.requiresAmountFromMerchant, requiresAmountFromMerchant) || other.requiresAmountFromMerchant == requiresAmountFromMerchant)&&(identical(other.requiresPhone, requiresPhone) || other.requiresPhone == requiresPhone)&&(identical(other.requiresReversal, requiresReversal) || other.requiresReversal == requiresReversal)&&(identical(other.additionalDataString, additionalDataString) || other.additionalDataString == additionalDataString)&&(identical(other.processorType, processorType) || other.processorType == processorType)&&(identical(other.uid, uid) || other.uid == uid)&&(identical(other.image, image) || other.image == image)&&(identical(other.label, label) || other.label == label)&&(identical(other.category, category) || other.category == category)&&(identical(other.accountFieldName, accountFieldName) || other.accountFieldName == accountFieldName));
return identical(this, other) || (other.runtimeType == runtimeType&&other is BillProvider&&(identical(other.id, id) || other.id == id)&&(identical(other.clientId, clientId) || other.clientId == clientId)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.requiresAccount, requiresAccount) || other.requiresAccount == requiresAccount)&&(identical(other.requiresAmount, requiresAmount) || other.requiresAmount == requiresAmount)&&(identical(other.requiresAmountFromMerchant, requiresAmountFromMerchant) || other.requiresAmountFromMerchant == requiresAmountFromMerchant)&&(identical(other.requiresPhone, requiresPhone) || other.requiresPhone == requiresPhone)&&(identical(other.requiresReversal, requiresReversal) || other.requiresReversal == requiresReversal)&&(identical(other.additionalDataString, additionalDataString) || other.additionalDataString == additionalDataString)&&(identical(other.processorType, processorType) || other.processorType == processorType)&&(identical(other.uid, uid) || other.uid == uid)&&(identical(other.image, image) || other.image == image)&&(identical(other.label, label) || other.label == label)&&(identical(other.category, category) || other.category == category)&&(identical(other.accountFieldName, accountFieldName) || other.accountFieldName == accountFieldName));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,clientId,name,description,requiresAccount,requiresAmount,requiresAmountFromMerchant,requiresPhone,requiresReversal,additionalDataString,processorType,uid,image,label,category,accountFieldName);
int get hashCode => Object.hash(runtimeType,id,clientId,name,description,requiresAccount,requiresAmount,requiresAmountFromMerchant,requiresPhone,requiresReversal,additionalDataString,processorType,uid,image,label,category,accountFieldName);
@override
String toString() {
return 'BillProvider(clientId: $clientId, name: $name, description: $description, requiresAccount: $requiresAccount, requiresAmount: $requiresAmount, requiresAmountFromMerchant: $requiresAmountFromMerchant, requiresPhone: $requiresPhone, requiresReversal: $requiresReversal, additionalDataString: $additionalDataString, processorType: $processorType, uid: $uid, image: $image, label: $label, category: $category, accountFieldName: $accountFieldName)';
return 'BillProvider(id: $id, clientId: $clientId, name: $name, description: $description, requiresAccount: $requiresAccount, requiresAmount: $requiresAmount, requiresAmountFromMerchant: $requiresAmountFromMerchant, requiresPhone: $requiresPhone, requiresReversal: $requiresReversal, additionalDataString: $additionalDataString, processorType: $processorType, uid: $uid, image: $image, label: $label, category: $category, accountFieldName: $accountFieldName)';
}
@@ -320,7 +320,7 @@ abstract mixin class $BillProviderCopyWith<$Res> {
factory $BillProviderCopyWith(BillProvider value, $Res Function(BillProvider) _then) = _$BillProviderCopyWithImpl;
@useResult
$Res call({
String clientId, String name, String description, bool requiresAccount, bool requiresAmount, bool? requiresAmountFromMerchant, bool requiresPhone, bool? requiresReversal, String? additionalDataString, String? processorType, String uid, String image, String label, String category, String? accountFieldName
String id, String clientId, String name, String description, bool requiresAccount, bool requiresAmount, bool? requiresAmountFromMerchant, bool requiresPhone, bool? requiresReversal, String? additionalDataString, String? processorType, String uid, String image, String label, String category, String? accountFieldName
});
@@ -337,9 +337,10 @@ class _$BillProviderCopyWithImpl<$Res>
/// Create a copy of BillProvider
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? clientId = null,Object? name = null,Object? description = null,Object? requiresAccount = null,Object? requiresAmount = null,Object? requiresAmountFromMerchant = freezed,Object? requiresPhone = null,Object? requiresReversal = freezed,Object? additionalDataString = freezed,Object? processorType = freezed,Object? uid = null,Object? image = null,Object? label = null,Object? category = null,Object? accountFieldName = freezed,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? clientId = null,Object? name = null,Object? description = null,Object? requiresAccount = null,Object? requiresAmount = null,Object? requiresAmountFromMerchant = freezed,Object? requiresPhone = null,Object? requiresReversal = freezed,Object? additionalDataString = freezed,Object? processorType = freezed,Object? uid = null,Object? image = null,Object? label = null,Object? category = null,Object? accountFieldName = freezed,}) {
return _then(_self.copyWith(
clientId: null == clientId ? _self.clientId : clientId // ignore: cast_nullable_to_non_nullable
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,clientId: null == clientId ? _self.clientId : clientId // ignore: cast_nullable_to_non_nullable
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String,requiresAccount: null == requiresAccount ? _self.requiresAccount : requiresAccount // ignore: cast_nullable_to_non_nullable
@@ -439,10 +440,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String clientId, String name, String description, bool requiresAccount, bool requiresAmount, bool? requiresAmountFromMerchant, bool requiresPhone, bool? requiresReversal, String? additionalDataString, String? processorType, String uid, String image, String label, String category, String? accountFieldName)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String clientId, String name, String description, bool requiresAccount, bool requiresAmount, bool? requiresAmountFromMerchant, bool requiresPhone, bool? requiresReversal, String? additionalDataString, String? processorType, String uid, String image, String label, String category, String? accountFieldName)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _BillProvider() when $default != null:
return $default(_that.clientId,_that.name,_that.description,_that.requiresAccount,_that.requiresAmount,_that.requiresAmountFromMerchant,_that.requiresPhone,_that.requiresReversal,_that.additionalDataString,_that.processorType,_that.uid,_that.image,_that.label,_that.category,_that.accountFieldName);case _:
return $default(_that.id,_that.clientId,_that.name,_that.description,_that.requiresAccount,_that.requiresAmount,_that.requiresAmountFromMerchant,_that.requiresPhone,_that.requiresReversal,_that.additionalDataString,_that.processorType,_that.uid,_that.image,_that.label,_that.category,_that.accountFieldName);case _:
return orElse();
}
@@ -460,10 +461,10 @@ return $default(_that.clientId,_that.name,_that.description,_that.requiresAccoun
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String clientId, String name, String description, bool requiresAccount, bool requiresAmount, bool? requiresAmountFromMerchant, bool requiresPhone, bool? requiresReversal, String? additionalDataString, String? processorType, String uid, String image, String label, String category, String? accountFieldName) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String clientId, String name, String description, bool requiresAccount, bool requiresAmount, bool? requiresAmountFromMerchant, bool requiresPhone, bool? requiresReversal, String? additionalDataString, String? processorType, String uid, String image, String label, String category, String? accountFieldName) $default,) {final _that = this;
switch (_that) {
case _BillProvider():
return $default(_that.clientId,_that.name,_that.description,_that.requiresAccount,_that.requiresAmount,_that.requiresAmountFromMerchant,_that.requiresPhone,_that.requiresReversal,_that.additionalDataString,_that.processorType,_that.uid,_that.image,_that.label,_that.category,_that.accountFieldName);case _:
return $default(_that.id,_that.clientId,_that.name,_that.description,_that.requiresAccount,_that.requiresAmount,_that.requiresAmountFromMerchant,_that.requiresPhone,_that.requiresReversal,_that.additionalDataString,_that.processorType,_that.uid,_that.image,_that.label,_that.category,_that.accountFieldName);case _:
throw StateError('Unexpected subclass');
}
@@ -480,10 +481,10 @@ return $default(_that.clientId,_that.name,_that.description,_that.requiresAccoun
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String clientId, String name, String description, bool requiresAccount, bool requiresAmount, bool? requiresAmountFromMerchant, bool requiresPhone, bool? requiresReversal, String? additionalDataString, String? processorType, String uid, String image, String label, String category, String? accountFieldName)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String clientId, String name, String description, bool requiresAccount, bool requiresAmount, bool? requiresAmountFromMerchant, bool requiresPhone, bool? requiresReversal, String? additionalDataString, String? processorType, String uid, String image, String label, String category, String? accountFieldName)? $default,) {final _that = this;
switch (_that) {
case _BillProvider() when $default != null:
return $default(_that.clientId,_that.name,_that.description,_that.requiresAccount,_that.requiresAmount,_that.requiresAmountFromMerchant,_that.requiresPhone,_that.requiresReversal,_that.additionalDataString,_that.processorType,_that.uid,_that.image,_that.label,_that.category,_that.accountFieldName);case _:
return $default(_that.id,_that.clientId,_that.name,_that.description,_that.requiresAccount,_that.requiresAmount,_that.requiresAmountFromMerchant,_that.requiresPhone,_that.requiresReversal,_that.additionalDataString,_that.processorType,_that.uid,_that.image,_that.label,_that.category,_that.accountFieldName);case _:
return null;
}
@@ -495,9 +496,10 @@ return $default(_that.clientId,_that.name,_that.description,_that.requiresAccoun
@JsonSerializable()
class _BillProvider implements BillProvider {
const _BillProvider({required this.clientId, required this.name, required this.description, required this.requiresAccount, required this.requiresAmount, required this.requiresAmountFromMerchant, required this.requiresPhone, required this.requiresReversal, required this.additionalDataString, required this.processorType, required this.uid, required this.image, required this.label, required this.category, required this.accountFieldName});
const _BillProvider({required this.id, required this.clientId, required this.name, required this.description, required this.requiresAccount, required this.requiresAmount, required this.requiresAmountFromMerchant, required this.requiresPhone, required this.requiresReversal, required this.additionalDataString, required this.processorType, required this.uid, required this.image, required this.label, required this.category, required this.accountFieldName});
factory _BillProvider.fromJson(Map<String, dynamic> json) => _$BillProviderFromJson(json);
@override final String id;
@override final String clientId;
@override final String name;
@override final String description;
@@ -527,16 +529,16 @@ Map<String, dynamic> toJson() {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _BillProvider&&(identical(other.clientId, clientId) || other.clientId == clientId)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.requiresAccount, requiresAccount) || other.requiresAccount == requiresAccount)&&(identical(other.requiresAmount, requiresAmount) || other.requiresAmount == requiresAmount)&&(identical(other.requiresAmountFromMerchant, requiresAmountFromMerchant) || other.requiresAmountFromMerchant == requiresAmountFromMerchant)&&(identical(other.requiresPhone, requiresPhone) || other.requiresPhone == requiresPhone)&&(identical(other.requiresReversal, requiresReversal) || other.requiresReversal == requiresReversal)&&(identical(other.additionalDataString, additionalDataString) || other.additionalDataString == additionalDataString)&&(identical(other.processorType, processorType) || other.processorType == processorType)&&(identical(other.uid, uid) || other.uid == uid)&&(identical(other.image, image) || other.image == image)&&(identical(other.label, label) || other.label == label)&&(identical(other.category, category) || other.category == category)&&(identical(other.accountFieldName, accountFieldName) || other.accountFieldName == accountFieldName));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _BillProvider&&(identical(other.id, id) || other.id == id)&&(identical(other.clientId, clientId) || other.clientId == clientId)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.requiresAccount, requiresAccount) || other.requiresAccount == requiresAccount)&&(identical(other.requiresAmount, requiresAmount) || other.requiresAmount == requiresAmount)&&(identical(other.requiresAmountFromMerchant, requiresAmountFromMerchant) || other.requiresAmountFromMerchant == requiresAmountFromMerchant)&&(identical(other.requiresPhone, requiresPhone) || other.requiresPhone == requiresPhone)&&(identical(other.requiresReversal, requiresReversal) || other.requiresReversal == requiresReversal)&&(identical(other.additionalDataString, additionalDataString) || other.additionalDataString == additionalDataString)&&(identical(other.processorType, processorType) || other.processorType == processorType)&&(identical(other.uid, uid) || other.uid == uid)&&(identical(other.image, image) || other.image == image)&&(identical(other.label, label) || other.label == label)&&(identical(other.category, category) || other.category == category)&&(identical(other.accountFieldName, accountFieldName) || other.accountFieldName == accountFieldName));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,clientId,name,description,requiresAccount,requiresAmount,requiresAmountFromMerchant,requiresPhone,requiresReversal,additionalDataString,processorType,uid,image,label,category,accountFieldName);
int get hashCode => Object.hash(runtimeType,id,clientId,name,description,requiresAccount,requiresAmount,requiresAmountFromMerchant,requiresPhone,requiresReversal,additionalDataString,processorType,uid,image,label,category,accountFieldName);
@override
String toString() {
return 'BillProvider(clientId: $clientId, name: $name, description: $description, requiresAccount: $requiresAccount, requiresAmount: $requiresAmount, requiresAmountFromMerchant: $requiresAmountFromMerchant, requiresPhone: $requiresPhone, requiresReversal: $requiresReversal, additionalDataString: $additionalDataString, processorType: $processorType, uid: $uid, image: $image, label: $label, category: $category, accountFieldName: $accountFieldName)';
return 'BillProvider(id: $id, clientId: $clientId, name: $name, description: $description, requiresAccount: $requiresAccount, requiresAmount: $requiresAmount, requiresAmountFromMerchant: $requiresAmountFromMerchant, requiresPhone: $requiresPhone, requiresReversal: $requiresReversal, additionalDataString: $additionalDataString, processorType: $processorType, uid: $uid, image: $image, label: $label, category: $category, accountFieldName: $accountFieldName)';
}
@@ -547,7 +549,7 @@ abstract mixin class _$BillProviderCopyWith<$Res> implements $BillProviderCopyWi
factory _$BillProviderCopyWith(_BillProvider value, $Res Function(_BillProvider) _then) = __$BillProviderCopyWithImpl;
@override @useResult
$Res call({
String clientId, String name, String description, bool requiresAccount, bool requiresAmount, bool? requiresAmountFromMerchant, bool requiresPhone, bool? requiresReversal, String? additionalDataString, String? processorType, String uid, String image, String label, String category, String? accountFieldName
String id, String clientId, String name, String description, bool requiresAccount, bool requiresAmount, bool? requiresAmountFromMerchant, bool requiresPhone, bool? requiresReversal, String? additionalDataString, String? processorType, String uid, String image, String label, String category, String? accountFieldName
});
@@ -564,9 +566,10 @@ class __$BillProviderCopyWithImpl<$Res>
/// Create a copy of BillProvider
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? clientId = null,Object? name = null,Object? description = null,Object? requiresAccount = null,Object? requiresAmount = null,Object? requiresAmountFromMerchant = freezed,Object? requiresPhone = null,Object? requiresReversal = freezed,Object? additionalDataString = freezed,Object? processorType = freezed,Object? uid = null,Object? image = null,Object? label = null,Object? category = null,Object? accountFieldName = freezed,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? clientId = null,Object? name = null,Object? description = null,Object? requiresAccount = null,Object? requiresAmount = null,Object? requiresAmountFromMerchant = freezed,Object? requiresPhone = null,Object? requiresReversal = freezed,Object? additionalDataString = freezed,Object? processorType = freezed,Object? uid = null,Object? image = null,Object? label = null,Object? category = null,Object? accountFieldName = freezed,}) {
return _then(_BillProvider(
clientId: null == clientId ? _self.clientId : clientId // ignore: cast_nullable_to_non_nullable
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,clientId: null == clientId ? _self.clientId : clientId // ignore: cast_nullable_to_non_nullable
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String,requiresAccount: null == requiresAccount ? _self.requiresAccount : requiresAccount // ignore: cast_nullable_to_non_nullable

View File

@@ -22,6 +22,7 @@ Map<String, dynamic> _$CategoryToJson(_Category instance) => <String, dynamic>{
_BillProvider _$BillProviderFromJson(Map<String, dynamic> json) =>
_BillProvider(
id: json['id'] as String,
clientId: json['clientId'] as String,
name: json['name'] as String,
description: json['description'] as String,
@@ -41,6 +42,7 @@ _BillProvider _$BillProviderFromJson(Map<String, dynamic> json) =>
Map<String, dynamic> _$BillProviderToJson(_BillProvider instance) =>
<String, dynamic>{
'id': instance.id,
'clientId': instance.clientId,
'name': instance.name,
'description': instance.description,

View File

@@ -25,6 +25,7 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
late final router = GoRouter.of(context);
bool _isLoggedIn = false;
bool _isZWGSelected = false;
String initials = '';
late AnimationController _providersAnimController;
@@ -103,7 +104,7 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
homeController = HomeController(context);
await homeController.getAccounts();
await homeController.getCategories();
await homeController.getProviders();
await homeController.getProviders(_isZWGSelected ? "ZWG" : "USD");
prefs = await SharedPreferences.getInstance();
@@ -124,7 +125,7 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
Future<void> _onRefresh() async {
await homeController.getAccounts();
await homeController.getCategories();
await homeController.getProviders();
await homeController.getProviders(_isZWGSelected ? "ZWG" : "USD");
prefs = await SharedPreferences.getInstance();
if (prefs.getString("userId") != null) {
@@ -343,14 +344,127 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_buildSectionLabel(theme, isDark, Icons.grid_view_rounded, "Pay"),
IconButton(
icon: Icon(
Icons.refresh_rounded,
color: isDark ? Colors.white54 : Colors.black54,
size: 20,
),
onPressed: _onRefresh,
tooltip: 'Refresh',
Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 2,
),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF1E1E1E) : Colors.white,
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: isDark
? Colors.white.withValues(alpha: 0.12)
: Colors.black.withValues(alpha: 0.08),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'USD',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: !_isZWGSelected
? theme.colorScheme.primary
: isDark
? Colors.white54
: Colors.black45,
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: GestureDetector(
onTap: () {
setState(() {
_isZWGSelected = !_isZWGSelected;
});
if (_isZWGSelected) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Center(
child: const Text(
'ZWG currency not supported yet',
),
),
behavior: SnackBarBehavior.floating,
backgroundColor: theme.colorScheme.primary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
duration: const Duration(seconds: 3),
width: 300,
),
);
}
Future.delayed(
const Duration(milliseconds: 1000),
() {
setState(() {
_isZWGSelected = !_isZWGSelected;
});
},
);
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
width: 32,
height: 18,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: _isZWGSelected
? theme.colorScheme.primary
: Colors.grey.shade300,
),
child: AnimatedAlign(
duration: const Duration(milliseconds: 200),
alignment: _isZWGSelected
? Alignment.centerRight
: Alignment.centerLeft,
child: Container(
width: 14,
height: 14,
margin: const EdgeInsets.all(2),
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
),
),
),
),
),
Text(
'ZWG',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: _isZWGSelected
? theme.colorScheme.primary
: isDark
? Colors.white54
: Colors.black45,
),
),
],
),
),
const SizedBox(width: 4),
IconButton(
icon: Icon(
Icons.refresh_rounded,
color: isDark ? Colors.white54 : Colors.black54,
size: 20,
),
onPressed: _onRefresh,
tooltip: 'Refresh',
),
],
),
],
),

View File

@@ -73,7 +73,9 @@ class PayController extends ChangeNotifier {
// formData can come from a repeat transaction
// otherwise build it from elements of the normal flow
Future<ApiResponse<Map<String, dynamic>>> doTransaction([tc.FormData? formData]) async {
Future<ApiResponse<Map<String, dynamic>>> doTransaction([
tc.FormData? formData,
]) async {
try {
model.isLoading = true;
notifyListeners();
@@ -129,7 +131,9 @@ class PayController extends ChangeNotifier {
logger.e(s);
model.isLoading = false;
notifyListeners();
return ApiResponse.failure("Network error. Please try again or contact support");
return ApiResponse.failure(
"Network error. Please try again or contact support",
);
} catch (e, s) {
logger.e(s);
model.isLoading = false;
@@ -170,8 +174,9 @@ class PayController extends ChangeNotifier {
notifyListeners();
List<dynamic> response = await http.get('/public/payment-processors');
model.paymentProcessors =
response.map((e) => PaymentProcessor.fromJson(e)).toList();
model.paymentProcessors = response
.map((e) => PaymentProcessor.fromJson(e))
.toList();
model.isLoading = false;
notifyListeners();

View File

@@ -65,7 +65,7 @@ class _PayScreenState extends State<PayScreen> with TickerProviderStateMixin {
controller = PayController(context);
controller.getPaymentProcessors();
controller.getProducts(controller.model.selectedProvider?.uid ?? "");
controller.getProducts(controller.model.selectedProvider?.id ?? "");
transactionController = Provider.of<TransactionController>(
context,
listen: false,
@@ -303,7 +303,8 @@ class _PayScreenState extends State<PayScreen> with TickerProviderStateMixin {
if (showAdditionalRecipientDetails)
_buildAdditionalRecipientDetails(),
const SizedBox(height: 7),
if (controller.model.products.isNotEmpty)
if (controller.model.products.isNotEmpty ||
controller.model.isLoading)
_buildBillProductSelector(controller),
const SizedBox(height: 7),
_buildPhoneField(),