Skip to content

Commit

Permalink
refactor: change responsive form and submit button for web or mobile
Browse files Browse the repository at this point in the history
  • Loading branch information
cevheri committed Dec 29, 2024
1 parent c76cba5 commit 969f172
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 72 deletions.
4 changes: 4 additions & 0 deletions lib/configuration/environment.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ class ProfileConstants {
return _config == _Config.devConstants;
}

static bool get isTest {
return _config == _Config.testConstants;
}

static get api {
return _config![_Config.api];
}
Expand Down
7 changes: 6 additions & 1 deletion lib/data/http_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,14 @@ class HttpUtils {
static Future<http.Response> mockRequest(String httpMethod, String endpoint, {String? pathParams, Map<String, String>? queryParams}) async {
debugPrint("BEGIN: Mock Request Method start : $httpMethod $endpoint");

if (!ProfileConstants.isTest) {
await Future.delayed(const Duration(milliseconds: 500));
}

var headers = await HttpUtils.headers();
if (!allowedPaths.contains(endpoint)) {
debugPrint("mockRequest: Unauthorized Access. endpoint: $endpoint, httpMethod: $httpMethod, headers: $headers, allowedPaths: $allowedPaths");
debugPrint(
"mockRequest: Unauthorized Access. endpoint: $endpoint, httpMethod: $httpMethod, headers: $headers, allowedPaths: $allowedPaths");
if (headers['Authorization'] == null) {
throw UnauthorizedException("Unauthorized Access");
}
Expand Down
17 changes: 17 additions & 0 deletions lib/presentation/common_blocs/account/account_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_bloc_advance/configuration/app_logger.dart';
import 'package:flutter_bloc_advance/configuration/local_storage.dart';
import 'package:flutter_bloc_advance/data/repository/user_repository.dart';

import '../../../data/models/user.dart';
import '../../../data/repository/account_repository.dart';
Expand All @@ -22,8 +23,11 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
super(const AccountState()) {
on<AccountEvent>((event, emit) {});
on<AccountFetchEvent>(_onFetchAccount);
on<AccountSubmitEvent>(_onSubmit);
}

final UserRepository _userRepository = UserRepository();

/// Load the current account.
FutureOr<void> _onFetchAccount(AccountFetchEvent event, Emitter<AccountState> emit) async {
_log.debug("BEGIN: getAccount bloc: _onLoad");
Expand All @@ -41,4 +45,17 @@ class AccountBloc extends Bloc<AccountEvent, AccountState> {
_log.error("END: getAccount bloc: _onLoad error: {}", [e.toString()]);
}
}

FutureOr<void> _onSubmit(AccountSubmitEvent event, Emitter<AccountState> emit) async {
_log.debug("BEGIN: onSubmit AccountSubmitEvent event: {}", [event.data.toString()]);
emit(state.copyWith(status: AccountStatus.loading));
try {
final user = await _userRepository.update(event.data);
emit(state.copyWith(status: AccountStatus.success, data: user));
_log.debug("END:onSubmitAccountSubmitEvent event success: {}", [user.toString()]);
} catch (e) {
emit(state.copyWith(status: AccountStatus.failure));
_log.error("END:onSubmit AccountSubmitEvent event error: {}", [e.toString()]);
}
}
}
9 changes: 9 additions & 0 deletions lib/presentation/common_blocs/account/account_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,12 @@ class AccountFetchEvent extends AccountEvent {
@override
List<Object> get props => [];
}

class AccountSubmitEvent extends AccountEvent {
final User data;

const AccountSubmitEvent(this.data);

@override
List<Object> get props => [data];
}
92 changes: 30 additions & 62 deletions lib/presentation/screen/account/account_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import 'package:flutter_bloc_advance/data/models/user.dart';
import 'package:flutter_bloc_advance/generated/l10n.dart';
import 'package:flutter_bloc_advance/presentation/common_blocs/account/account.dart';
import 'package:flutter_bloc_advance/presentation/screen/components/confirmation_dialog_widget.dart';
import 'package:flutter_bloc_advance/presentation/screen/components/responsive_form_widget.dart';
import 'package:flutter_bloc_advance/presentation/screen/components/submit_button_widget.dart';
import 'package:flutter_bloc_advance/presentation/screen/components/user_form_fields.dart';
import 'package:flutter_bloc_advance/presentation/screen/user/bloc/user.dart';
import 'package:flutter_bloc_advance/routes/app_routes_constants.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:go_router/go_router.dart';
Expand All @@ -31,7 +32,7 @@ class AccountScreen extends StatelessWidget {
);
}

_buildAppBar(BuildContext context) {
AppBar _buildAppBar(BuildContext context) {
return AppBar(
title: Text(S.of(context).account),
leading: IconButton(
Expand All @@ -42,43 +43,19 @@ class AccountScreen extends StatelessWidget {
);
}

_buildBody(BuildContext context) {
Widget _buildBody(BuildContext context) {
return BlocBuilder<AccountBloc, AccountState>(
buildWhen: (previous, current) => previous.data != current.data || previous.status != current.status,
buildWhen: (previous, current) => previous.status != current.status, //previous.data != current.data ||
builder: (context, state) {
if (state.data == null) {
return Center(child: Text(S.of(context).no_data));
}
if (state.status == AccountStatus.loading) {
return const Center(child: CircularProgressIndicator());
}

return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 700),
child: FormBuilder(
key: _formKey,
child: Column(
spacing: 16,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
..._buildFormFields(context, state),
_submitButton(context, state),
],
),
),
),
),
),
return ResponsiveFormBuilder(
formKey: _formKey,
children: [..._buildFormFields(context, state), _submitButton(context, state)],
);
},
);
}

_buildFormFields(BuildContext context, AccountState state) {
List<Widget> _buildFormFields(BuildContext context, AccountState state) {
return [
UserFormFields.usernameField(context, state.data?.login, enabled: false),
UserFormFields.firstNameField(context, state.data?.firstName),
Expand All @@ -88,20 +65,11 @@ class AccountScreen extends StatelessWidget {
];
}

/// Submit button
/// This button is used to submit the form.
///
/// [context] BuildContext current context
/// [state] AccountState state of the bloc
/// return ElevatedButton
Widget _submitButton(BuildContext context, AccountState state) {
return SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed: () => state.status == AccountStatus.loading ? null : _onSubmit(context, state),
child: Text(S.of(context).save),
),
debugPrint('state.status: ${state.status}');
return ResponsiveSubmitButton(
onPressed: () => state.status == AccountStatus.loading ? null : _onSubmit(context, state),
isLoading: state.status == AccountStatus.loading,
);
}

Expand All @@ -115,30 +83,31 @@ class AccountScreen extends StatelessWidget {

if (_formKey.currentState?.saveAndValidate() ?? false) {
final formData = _formKey.currentState!.value;
final user = _createUserFromFormData(formData, state.data?.id);

context.read<UserBloc>().add(UserSubmitEvent(user));
late final StreamSubscription<UserState> subscription;
subscription = context.read<UserBloc>().stream.listen((userState) {
if ((userState.status == UserStatus.success || userState.status == UserStatus.saveSuccess) && context.mounted) {
context.read<AccountBloc>().add(const AccountFetchEvent());
_formKey.currentState?.reset();
subscription.cancel();
}
}); // cancel the stream after the first event
final user = _createUserFromData(formData, state.data?.id);
context.read<AccountBloc>().add(AccountSubmitEvent(user));
if (state.status == AccountStatus.success) {
_formKey.currentState?.reset();
}

//context.read<UserBloc>().add(UserSubmitEvent(user));
// late final StreamSubscription<UserState> subscription;
// subscription = context.read<UserBloc>().stream.listen((userState) {
// if ((userState.status == UserStatus.success || userState.status == UserStatus.saveSuccess) && context.mounted) {
// context.read<AccountBloc>().add(const AccountFetchEvent());
// _formKey.currentState?.reset();
// subscription.cancel();
// }
// }); // cancel the stream after the first event
}
}

User _createUserFromFormData(Map<String, dynamic> formData, String? userId) {
return User(
User _createUserFromData(Map<String, dynamic> formData, String? userId) => User(
id: userId,
login: formData['login'],
firstName: formData['firstName'],
lastName: formData['lastName'],
email: formData['email'],
activated: formData['activated'],
);
}
activated: formData['activated']);

void _handleStateChanges(BuildContext context, AccountState state) {
const duration = Duration(milliseconds: 1000);
Expand Down Expand Up @@ -179,5 +148,4 @@ class AccountScreen extends StatelessWidget {
context.go(ApplicationRoutesConstants.home);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class ChangePasswordScreen extends StatelessWidget {
},
child: FilledButton(
key: changePasswordButtonSubmitKey,
child: Text(S.of(context).change_password),
child: Text(S.of(context).save),
onPressed: () {
//without blocConsumer access to bloc directly
final currentState = context.read<ChangePasswordBloc>().state;
Expand Down
16 changes: 8 additions & 8 deletions test/presentation/screen/account/account_screen_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -167,14 +167,14 @@ void main() {
expect(find.byType(CircularProgressIndicator), findsNothing);
});

testWidgets('Should display no data message when data is null', (tester) async {
when(mockAccountBloc.state).thenReturn(const AccountState(status: AccountStatus.success));

await tester.pumpWidget(buildTestableWidget());
await tester.pumpAndSettle();

expect(find.text(S.current.no_data), findsOneWidget);
});
// testWidgets('Should display no data message when data is null', (tester) async {
// when(mockAccountBloc.state).thenReturn(const AccountState(status: AccountStatus.success));
//
// await tester.pumpWidget(buildTestableWidget());
// await tester.pumpAndSettle();
//
// expect(find.text(S.current.no_data), findsOneWidget);
// });
});

group('AccountScreen Form Operations', () {
Expand Down

0 comments on commit 969f172

Please sign in to comment.