feat: Refactor code structure and optimize performance across multiple modules
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:krow/core/application/clients/api/api_client.dart';
|
||||
import 'package:krow/core/data/models/staff/bank_acc.dart';
|
||||
import 'package:krow/features/profile/bank_account/data/gql.dart';
|
||||
|
||||
@injectable
|
||||
class BankAccountApiProvider {
|
||||
final ApiClient _apiClient;
|
||||
|
||||
BankAccountApiProvider(this._apiClient);
|
||||
|
||||
Stream<BankAcc?> getStaffBankAcc() async* {
|
||||
await for (var response
|
||||
in _apiClient.queryWithCache(schema: staffBankAccount)) {
|
||||
if (response == null || response.data == null) {
|
||||
continue;
|
||||
}
|
||||
if (response.hasException) {
|
||||
throw Exception(response.exception.toString());
|
||||
}
|
||||
final bankAcc =
|
||||
BankAcc.fromJson(response.data?['me']?['bank_account'] ?? {});
|
||||
yield bankAcc;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> putBunkAccount(BankAcc bankAcc) async {
|
||||
final Map<String, dynamic> variables = {
|
||||
'input': bankAcc.toJson(),
|
||||
};
|
||||
|
||||
var result =
|
||||
await _apiClient.mutate(schema: updateBankAccount, body: variables);
|
||||
|
||||
if (result.hasException) {
|
||||
throw Exception(result.exception.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import 'package:krow/core/data/models/staff/bank_acc.dart';
|
||||
|
||||
abstract class BankAccountRepository {
|
||||
Stream<BankAcc?> getStaffBankAcc();
|
||||
|
||||
Future<void> putBankAcc(BankAcc address);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
const String staffBankAccount = r'''
|
||||
query staffAddress {
|
||||
me {
|
||||
id
|
||||
bank_account {
|
||||
id
|
||||
holder_name
|
||||
bank_name
|
||||
number
|
||||
routing_number
|
||||
country
|
||||
state
|
||||
city
|
||||
street
|
||||
building
|
||||
zip
|
||||
}
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
const String updateBankAccount = r'''
|
||||
mutation updateBankAccount($input: UpdateStaffBankAccountInput!) {
|
||||
update_staff_bank_account(input: $input) {
|
||||
|
||||
}
|
||||
}
|
||||
''';
|
||||
@@ -0,0 +1,22 @@
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:krow/core/data/models/staff/bank_acc.dart';
|
||||
import 'package:krow/features/profile/bank_account/data/bank_account_api_provider.dart';
|
||||
import 'package:krow/features/profile/bank_account/data/bank_account_repository.dart';
|
||||
|
||||
@Singleton(as: BankAccountRepository)
|
||||
class BankAccountRepositoryImpl implements BankAccountRepository {
|
||||
final BankAccountApiProvider _apiProvider;
|
||||
|
||||
BankAccountRepositoryImpl({required BankAccountApiProvider apiProvider})
|
||||
: _apiProvider = apiProvider;
|
||||
|
||||
@override
|
||||
Stream<BankAcc?> getStaffBankAcc() {
|
||||
return _apiProvider.getStaffBankAcc();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> putBankAcc(BankAcc acc) {
|
||||
return _apiProvider.putBunkAccount(acc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:krow/core/application/di/injectable.dart';
|
||||
import 'package:krow/core/data/models/staff/bank_acc.dart';
|
||||
import 'package:krow/features/profile/bank_account/data/bank_account_repository.dart';
|
||||
|
||||
part 'bank_account_event.dart';
|
||||
part 'bank_account_state.dart';
|
||||
|
||||
class BankAccountBloc extends Bloc<BankAccountEvent, BankAccountState> {
|
||||
BankAccountBloc() : super(const BankAccountState()) {
|
||||
on<BankAccountEventInit>(_onInit);
|
||||
on<BankAccountEventUpdate>(_onSubmit);
|
||||
}
|
||||
|
||||
FutureOr<void> _onInit(event, emit) async {
|
||||
await for (var account
|
||||
in getIt<BankAccountRepository>().getStaffBankAcc()) {
|
||||
emit(state.copyWith(bankAcc: account));
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _onSubmit(BankAccountEventUpdate event, emit) async {
|
||||
emit(state.copyWith(inLoading: true));
|
||||
var newBankAcc = BankAcc.fromJson(event.bankAcc);
|
||||
await getIt<BankAccountRepository>().putBankAcc(newBankAcc);
|
||||
emit(state.copyWith(success: true, bankAcc: newBankAcc));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
part of 'bank_account_bloc.dart';
|
||||
|
||||
@immutable
|
||||
sealed class BankAccountEvent {}
|
||||
|
||||
class BankAccountEventInit extends BankAccountEvent {}
|
||||
|
||||
class BankAccountEventUpdate extends BankAccountEvent {
|
||||
final Map<String, String> bankAcc;
|
||||
|
||||
BankAccountEventUpdate(this.bankAcc);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
part of 'bank_account_bloc.dart';
|
||||
|
||||
@immutable
|
||||
class BankAccountState {
|
||||
final BankAcc? bankAcc;
|
||||
final bool inLoading;
|
||||
|
||||
final bool success;
|
||||
|
||||
const BankAccountState(
|
||||
{this.inLoading = false, this.success = false, this.bankAcc});
|
||||
|
||||
BankAccountState copyWith({
|
||||
BankAcc? bankAcc,
|
||||
bool? inLoading,
|
||||
bool? success,
|
||||
}) {
|
||||
return BankAccountState(
|
||||
inLoading: inLoading ?? false,
|
||||
success: success ?? false,
|
||||
bankAcc: bankAcc ?? this.bankAcc,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, BankAccField> mapFromBankAccount() {
|
||||
return {
|
||||
'holder_name': BankAccField(
|
||||
title: 'account_holder_name'.tr(),
|
||||
value: bankAcc?.holderName,
|
||||
),
|
||||
'bank_name': BankAccField(
|
||||
title: 'bank_name'.tr(),
|
||||
value: bankAcc?.bankName,
|
||||
),
|
||||
'number': BankAccField(
|
||||
title: 'account_number'.tr(),
|
||||
value: bankAcc?.number,
|
||||
),
|
||||
'routing_number': BankAccField(
|
||||
title: 'routing_number_us'.tr(),
|
||||
value: bankAcc?.routingNumber,
|
||||
optional: true),
|
||||
'country': BankAccField(
|
||||
title: 'country'.tr(),
|
||||
value: bankAcc?.country,
|
||||
),
|
||||
'state': BankAccField(
|
||||
title: 'state'.tr(),
|
||||
value: bankAcc?.state,
|
||||
),
|
||||
'city': BankAccField(
|
||||
title: 'city'.tr(),
|
||||
value: bankAcc?.city,
|
||||
),
|
||||
'street': BankAccField(
|
||||
title: 'street_address'.tr(),
|
||||
value: bankAcc?.street,
|
||||
),
|
||||
'building': BankAccField(
|
||||
title: 'apt_suite_building'.tr(),
|
||||
value: bankAcc?.building,
|
||||
optional: true),
|
||||
'zip': BankAccField(
|
||||
title: 'zip_code'.tr(),
|
||||
value: bankAcc?.zip,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class BankAccField {
|
||||
String title;
|
||||
String? value;
|
||||
bool optional;
|
||||
|
||||
BankAccField({required this.title, this.value = '', this.optional = false});
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:krow/features/profile/bank_account/domain/bloc/bank_account_bloc.dart';
|
||||
|
||||
@RoutePage()
|
||||
class BankAccountFlowScreen extends StatelessWidget {
|
||||
const BankAccountFlowScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MultiBlocProvider(providers: [
|
||||
BlocProvider<BankAccountBloc>(
|
||||
create: (context) => BankAccountBloc()..add(BankAccountEventInit()),
|
||||
),
|
||||
], child: const AutoRouter());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:krow/core/presentation/styles/kw_box_decorations.dart';
|
||||
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
|
||||
import 'package:krow/core/presentation/styles/theme.dart';
|
||||
import 'package:krow/core/presentation/widgets/scroll_layout_helper.dart';
|
||||
import 'package:krow/core/presentation/widgets/ui_kit/kw_app_bar.dart';
|
||||
import 'package:krow/core/presentation/widgets/ui_kit/kw_button.dart';
|
||||
import 'package:krow/core/presentation/widgets/ui_kit/kw_input.dart';
|
||||
import 'package:krow/features/profile/bank_account/domain/bloc/bank_account_bloc.dart';
|
||||
|
||||
@RoutePage()
|
||||
class BankAccountEditScreen extends StatefulWidget {
|
||||
const BankAccountEditScreen({super.key});
|
||||
|
||||
@override
|
||||
State<BankAccountEditScreen> createState() => _BankAccountEditScreenState();
|
||||
}
|
||||
|
||||
class _BankAccountEditScreenState extends State<BankAccountEditScreen> {
|
||||
Map<String, BankAccField>? bankAccInfo;
|
||||
final Map<String, TextEditingController> _controllers = {};
|
||||
|
||||
var showInputError = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
bankAccInfo = Map.of(
|
||||
BlocProvider.of<BankAccountBloc>(context).state.mapFromBankAccount());
|
||||
|
||||
bankAccInfo?.keys.forEach((key) {
|
||||
_controllers[key] =
|
||||
TextEditingController(text: bankAccInfo?[key]?.value);
|
||||
});
|
||||
setState(() {});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocConsumer<BankAccountBloc, BankAccountState>(
|
||||
listener: (context, state) {
|
||||
if (state.success) {
|
||||
context.router.maybePop();
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
appBar: KwAppBar(
|
||||
titleText: 'edit_bank_account'.tr(),
|
||||
showNotification: true,
|
||||
),
|
||||
body: ScrollLayoutHelper(
|
||||
padding: const EdgeInsets.all(16),
|
||||
upperWidget: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'edit_information_below'.tr(),
|
||||
style: AppTextStyles.bodySmallReg
|
||||
.copyWith(color: AppColors.blackGray),
|
||||
),
|
||||
const Gap(24),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: KwBoxDecorations.primaryLight12,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildCardHeader(context, 'account_details'.tr()),
|
||||
...(bankAccInfo?.entries
|
||||
.take(4)
|
||||
.map((entry) =>
|
||||
_buildInput(entry.key, entry.value))
|
||||
.toList() ??
|
||||
[])
|
||||
],
|
||||
),
|
||||
),
|
||||
const Gap(12),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: KwBoxDecorations.primaryLight12,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildCardHeader(context, 'billing_address'.tr()),
|
||||
...(bankAccInfo?.entries
|
||||
.skip(4)
|
||||
.map((entry) =>
|
||||
_buildInput(entry.key, entry.value))
|
||||
.toList() ??
|
||||
[])
|
||||
],
|
||||
),
|
||||
),
|
||||
const Gap(24),
|
||||
],
|
||||
),
|
||||
lowerWidget: KwButton.primary(
|
||||
label: 'save_changes'.tr(),
|
||||
onPressed: () {
|
||||
if (bankAccInfo?.values.any((element) =>
|
||||
!element.optional &&
|
||||
(element.value?.isEmpty ?? false)) ??
|
||||
false) {
|
||||
setState(() {
|
||||
showInputError = true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var newAcc = bankAccInfo?.map((key, value) => MapEntry(
|
||||
key,
|
||||
_controllers[key]?.text ?? '',
|
||||
));
|
||||
|
||||
BlocProvider.of<BankAccountBloc>(context)
|
||||
.add(BankAccountEventUpdate(newAcc!));
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCardHeader(BuildContext context, String title) {
|
||||
return Text(
|
||||
title,
|
||||
style: AppTextStyles.bodyMediumMed,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInput(String key, BankAccField field) {
|
||||
var hasError = !field.optional &&
|
||||
(_controllers[key]?.text.isEmpty ?? false) &&
|
||||
showInputError;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: KwTextInput(
|
||||
hintText: '',
|
||||
title: field.title,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
field.value = value;
|
||||
});
|
||||
|
||||
},
|
||||
helperText: hasError ? 'field_cant_be_empty'.tr() : null,
|
||||
showError: hasError,
|
||||
controller: _controllers[key],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:krow/core/application/routing/routes.gr.dart';
|
||||
import 'package:krow/core/presentation/gen/assets.gen.dart';
|
||||
import 'package:krow/core/presentation/styles/kw_box_decorations.dart';
|
||||
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
|
||||
import 'package:krow/core/presentation/styles/theme.dart';
|
||||
import 'package:krow/core/presentation/widgets/scroll_layout_helper.dart';
|
||||
import 'package:krow/core/presentation/widgets/ui_kit/kw_app_bar.dart';
|
||||
import 'package:krow/features/profile/bank_account/domain/bloc/bank_account_bloc.dart';
|
||||
|
||||
@RoutePage()
|
||||
class BankAccountScreen extends StatefulWidget implements AutoRouteWrapper {
|
||||
const BankAccountScreen({super.key});
|
||||
|
||||
@override
|
||||
State<BankAccountScreen> createState() => _BankAccountScreenState();
|
||||
|
||||
@override
|
||||
Widget wrappedRoute(BuildContext context) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
class _BankAccountScreenState extends State<BankAccountScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<BankAccountBloc, BankAccountState>(
|
||||
builder: (context, state) {
|
||||
return Scaffold(
|
||||
appBar: KwAppBar(
|
||||
titleText: 'bank_account'.tr(),
|
||||
showNotification: true,
|
||||
),
|
||||
body: ScrollLayoutHelper(
|
||||
padding: const EdgeInsets.all(16),
|
||||
upperWidget: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'securely_manage_bank_account'.tr(),
|
||||
style: AppTextStyles.bodySmallReg
|
||||
.copyWith(color: AppColors.blackGray),
|
||||
),
|
||||
const Gap(24),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: KwBoxDecorations.primaryLight12,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildCardHeader(context),
|
||||
...(state
|
||||
.mapFromBankAccount()
|
||||
.entries
|
||||
.map((entry) => infoRow(
|
||||
entry.value.title, entry.value.value ?? ''))
|
||||
.expand((e) => e)
|
||||
.toList())
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
lowerWidget: Container(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Row _buildCardHeader(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'your_payment_details'.tr(),
|
||||
style: AppTextStyles.bodyMediumMed,
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
context.router.push(const BankAccountEditRoute());
|
||||
},
|
||||
child: Assets.images.icons.edit.svg(height: 16, width: 16))
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> infoRow(
|
||||
String title,
|
||||
String value,
|
||||
) {
|
||||
return [
|
||||
const Gap(24),
|
||||
Text(
|
||||
'$title:'.toUpperCase(),
|
||||
style: AppTextStyles.captionReg.copyWith(color: AppColors.blackGray),
|
||||
),
|
||||
const Gap(8),
|
||||
Text(
|
||||
value,
|
||||
style: AppTextStyles.bodyMediumMed,
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user