check phone bloc

This commit is contained in:
efrilm 2025-09-18 06:18:01 +07:00
parent 207dda29df
commit 99bf4d5c7f
7 changed files with 669 additions and 0 deletions

View File

@ -0,0 +1,56 @@
import 'package:bloc/bloc.dart';
import 'package:dartz/dartz.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:injectable/injectable.dart';
import '../../../domain/auth/auth.dart';
part 'check_phone_form_event.dart';
part 'check_phone_form_state.dart';
part 'check_phone_form_bloc.freezed.dart';
@injectable
class CheckPhoneFormBloc
extends Bloc<CheckPhoneFormEvent, CheckPhoneFormState> {
final IAuthRepository _repository;
CheckPhoneFormBloc(this._repository) : super(CheckPhoneFormState.initial()) {
on<CheckPhoneFormEvent>(_onCheckPhoneFormEvent);
}
Future<void> _onCheckPhoneFormEvent(
CheckPhoneFormEvent event,
Emitter<CheckPhoneFormState> emit,
) {
return event.map(
phoneNumberChanged: (e) async {
emit(
state.copyWith(
phoneNumber: e.phoneNumber,
failureOrCheckPhoneOption: none(),
),
);
},
submitted: (e) async {
Either<AuthFailure, CheckPhone>? failureOrCheckPhone;
emit(
state.copyWith(isSubmitting: true, failureOrCheckPhoneOption: none()),
);
final phoneNumberValid = state.phoneNumber.isNotEmpty;
if (phoneNumberValid) {
failureOrCheckPhone = await _repository.checkPhone(
phoneNumber: state.phoneNumber,
);
emit(
state.copyWith(
isSubmitting: false,
failureOrCheckPhoneOption: optionOf(failureOrCheckPhone),
),
);
}
emit(state.copyWith(showErrorMessages: true, isSubmitting: false));
},
);
}
}

View File

@ -0,0 +1,552 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'check_phone_form_bloc.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models',
);
/// @nodoc
mixin _$CheckPhoneFormEvent {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String phoneNumber) phoneNumberChanged,
required TResult Function() submitted,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String phoneNumber)? phoneNumberChanged,
TResult? Function()? submitted,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String phoneNumber)? phoneNumberChanged,
TResult Function()? submitted,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_PhoneNumberChanged value) phoneNumberChanged,
required TResult Function(_Submitted value) submitted,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_PhoneNumberChanged value)? phoneNumberChanged,
TResult? Function(_Submitted value)? submitted,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_PhoneNumberChanged value)? phoneNumberChanged,
TResult Function(_Submitted value)? submitted,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $CheckPhoneFormEventCopyWith<$Res> {
factory $CheckPhoneFormEventCopyWith(
CheckPhoneFormEvent value,
$Res Function(CheckPhoneFormEvent) then,
) = _$CheckPhoneFormEventCopyWithImpl<$Res, CheckPhoneFormEvent>;
}
/// @nodoc
class _$CheckPhoneFormEventCopyWithImpl<$Res, $Val extends CheckPhoneFormEvent>
implements $CheckPhoneFormEventCopyWith<$Res> {
_$CheckPhoneFormEventCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of CheckPhoneFormEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$PhoneNumberChangedImplCopyWith<$Res> {
factory _$$PhoneNumberChangedImplCopyWith(
_$PhoneNumberChangedImpl value,
$Res Function(_$PhoneNumberChangedImpl) then,
) = __$$PhoneNumberChangedImplCopyWithImpl<$Res>;
@useResult
$Res call({String phoneNumber});
}
/// @nodoc
class __$$PhoneNumberChangedImplCopyWithImpl<$Res>
extends _$CheckPhoneFormEventCopyWithImpl<$Res, _$PhoneNumberChangedImpl>
implements _$$PhoneNumberChangedImplCopyWith<$Res> {
__$$PhoneNumberChangedImplCopyWithImpl(
_$PhoneNumberChangedImpl _value,
$Res Function(_$PhoneNumberChangedImpl) _then,
) : super(_value, _then);
/// Create a copy of CheckPhoneFormEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? phoneNumber = null}) {
return _then(
_$PhoneNumberChangedImpl(
null == phoneNumber
? _value.phoneNumber
: phoneNumber // ignore: cast_nullable_to_non_nullable
as String,
),
);
}
}
/// @nodoc
class _$PhoneNumberChangedImpl implements _PhoneNumberChanged {
const _$PhoneNumberChangedImpl(this.phoneNumber);
@override
final String phoneNumber;
@override
String toString() {
return 'CheckPhoneFormEvent.phoneNumberChanged(phoneNumber: $phoneNumber)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$PhoneNumberChangedImpl &&
(identical(other.phoneNumber, phoneNumber) ||
other.phoneNumber == phoneNumber));
}
@override
int get hashCode => Object.hash(runtimeType, phoneNumber);
/// Create a copy of CheckPhoneFormEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$PhoneNumberChangedImplCopyWith<_$PhoneNumberChangedImpl> get copyWith =>
__$$PhoneNumberChangedImplCopyWithImpl<_$PhoneNumberChangedImpl>(
this,
_$identity,
);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String phoneNumber) phoneNumberChanged,
required TResult Function() submitted,
}) {
return phoneNumberChanged(phoneNumber);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String phoneNumber)? phoneNumberChanged,
TResult? Function()? submitted,
}) {
return phoneNumberChanged?.call(phoneNumber);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String phoneNumber)? phoneNumberChanged,
TResult Function()? submitted,
required TResult orElse(),
}) {
if (phoneNumberChanged != null) {
return phoneNumberChanged(phoneNumber);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_PhoneNumberChanged value) phoneNumberChanged,
required TResult Function(_Submitted value) submitted,
}) {
return phoneNumberChanged(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_PhoneNumberChanged value)? phoneNumberChanged,
TResult? Function(_Submitted value)? submitted,
}) {
return phoneNumberChanged?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_PhoneNumberChanged value)? phoneNumberChanged,
TResult Function(_Submitted value)? submitted,
required TResult orElse(),
}) {
if (phoneNumberChanged != null) {
return phoneNumberChanged(this);
}
return orElse();
}
}
abstract class _PhoneNumberChanged implements CheckPhoneFormEvent {
const factory _PhoneNumberChanged(final String phoneNumber) =
_$PhoneNumberChangedImpl;
String get phoneNumber;
/// Create a copy of CheckPhoneFormEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$PhoneNumberChangedImplCopyWith<_$PhoneNumberChangedImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$SubmittedImplCopyWith<$Res> {
factory _$$SubmittedImplCopyWith(
_$SubmittedImpl value,
$Res Function(_$SubmittedImpl) then,
) = __$$SubmittedImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$SubmittedImplCopyWithImpl<$Res>
extends _$CheckPhoneFormEventCopyWithImpl<$Res, _$SubmittedImpl>
implements _$$SubmittedImplCopyWith<$Res> {
__$$SubmittedImplCopyWithImpl(
_$SubmittedImpl _value,
$Res Function(_$SubmittedImpl) _then,
) : super(_value, _then);
/// Create a copy of CheckPhoneFormEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$SubmittedImpl implements _Submitted {
const _$SubmittedImpl();
@override
String toString() {
return 'CheckPhoneFormEvent.submitted()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$SubmittedImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String phoneNumber) phoneNumberChanged,
required TResult Function() submitted,
}) {
return submitted();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String phoneNumber)? phoneNumberChanged,
TResult? Function()? submitted,
}) {
return submitted?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String phoneNumber)? phoneNumberChanged,
TResult Function()? submitted,
required TResult orElse(),
}) {
if (submitted != null) {
return submitted();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_PhoneNumberChanged value) phoneNumberChanged,
required TResult Function(_Submitted value) submitted,
}) {
return submitted(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_PhoneNumberChanged value)? phoneNumberChanged,
TResult? Function(_Submitted value)? submitted,
}) {
return submitted?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_PhoneNumberChanged value)? phoneNumberChanged,
TResult Function(_Submitted value)? submitted,
required TResult orElse(),
}) {
if (submitted != null) {
return submitted(this);
}
return orElse();
}
}
abstract class _Submitted implements CheckPhoneFormEvent {
const factory _Submitted() = _$SubmittedImpl;
}
/// @nodoc
mixin _$CheckPhoneFormState {
String get phoneNumber => throw _privateConstructorUsedError;
Option<Either<AuthFailure, CheckPhone>> get failureOrCheckPhoneOption =>
throw _privateConstructorUsedError;
bool get isSubmitting => throw _privateConstructorUsedError;
bool get showErrorMessages => throw _privateConstructorUsedError;
/// Create a copy of CheckPhoneFormState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$CheckPhoneFormStateCopyWith<CheckPhoneFormState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $CheckPhoneFormStateCopyWith<$Res> {
factory $CheckPhoneFormStateCopyWith(
CheckPhoneFormState value,
$Res Function(CheckPhoneFormState) then,
) = _$CheckPhoneFormStateCopyWithImpl<$Res, CheckPhoneFormState>;
@useResult
$Res call({
String phoneNumber,
Option<Either<AuthFailure, CheckPhone>> failureOrCheckPhoneOption,
bool isSubmitting,
bool showErrorMessages,
});
}
/// @nodoc
class _$CheckPhoneFormStateCopyWithImpl<$Res, $Val extends CheckPhoneFormState>
implements $CheckPhoneFormStateCopyWith<$Res> {
_$CheckPhoneFormStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of CheckPhoneFormState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? phoneNumber = null,
Object? failureOrCheckPhoneOption = null,
Object? isSubmitting = null,
Object? showErrorMessages = null,
}) {
return _then(
_value.copyWith(
phoneNumber: null == phoneNumber
? _value.phoneNumber
: phoneNumber // ignore: cast_nullable_to_non_nullable
as String,
failureOrCheckPhoneOption: null == failureOrCheckPhoneOption
? _value.failureOrCheckPhoneOption
: failureOrCheckPhoneOption // ignore: cast_nullable_to_non_nullable
as Option<Either<AuthFailure, CheckPhone>>,
isSubmitting: null == isSubmitting
? _value.isSubmitting
: isSubmitting // ignore: cast_nullable_to_non_nullable
as bool,
showErrorMessages: null == showErrorMessages
? _value.showErrorMessages
: showErrorMessages // ignore: cast_nullable_to_non_nullable
as bool,
)
as $Val,
);
}
}
/// @nodoc
abstract class _$$CheckPhoneFormStateImplCopyWith<$Res>
implements $CheckPhoneFormStateCopyWith<$Res> {
factory _$$CheckPhoneFormStateImplCopyWith(
_$CheckPhoneFormStateImpl value,
$Res Function(_$CheckPhoneFormStateImpl) then,
) = __$$CheckPhoneFormStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({
String phoneNumber,
Option<Either<AuthFailure, CheckPhone>> failureOrCheckPhoneOption,
bool isSubmitting,
bool showErrorMessages,
});
}
/// @nodoc
class __$$CheckPhoneFormStateImplCopyWithImpl<$Res>
extends _$CheckPhoneFormStateCopyWithImpl<$Res, _$CheckPhoneFormStateImpl>
implements _$$CheckPhoneFormStateImplCopyWith<$Res> {
__$$CheckPhoneFormStateImplCopyWithImpl(
_$CheckPhoneFormStateImpl _value,
$Res Function(_$CheckPhoneFormStateImpl) _then,
) : super(_value, _then);
/// Create a copy of CheckPhoneFormState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? phoneNumber = null,
Object? failureOrCheckPhoneOption = null,
Object? isSubmitting = null,
Object? showErrorMessages = null,
}) {
return _then(
_$CheckPhoneFormStateImpl(
phoneNumber: null == phoneNumber
? _value.phoneNumber
: phoneNumber // ignore: cast_nullable_to_non_nullable
as String,
failureOrCheckPhoneOption: null == failureOrCheckPhoneOption
? _value.failureOrCheckPhoneOption
: failureOrCheckPhoneOption // ignore: cast_nullable_to_non_nullable
as Option<Either<AuthFailure, CheckPhone>>,
isSubmitting: null == isSubmitting
? _value.isSubmitting
: isSubmitting // ignore: cast_nullable_to_non_nullable
as bool,
showErrorMessages: null == showErrorMessages
? _value.showErrorMessages
: showErrorMessages // ignore: cast_nullable_to_non_nullable
as bool,
),
);
}
}
/// @nodoc
class _$CheckPhoneFormStateImpl implements _CheckPhoneFormState {
const _$CheckPhoneFormStateImpl({
required this.phoneNumber,
required this.failureOrCheckPhoneOption,
this.isSubmitting = false,
this.showErrorMessages = false,
});
@override
final String phoneNumber;
@override
final Option<Either<AuthFailure, CheckPhone>> failureOrCheckPhoneOption;
@override
@JsonKey()
final bool isSubmitting;
@override
@JsonKey()
final bool showErrorMessages;
@override
String toString() {
return 'CheckPhoneFormState(phoneNumber: $phoneNumber, failureOrCheckPhoneOption: $failureOrCheckPhoneOption, isSubmitting: $isSubmitting, showErrorMessages: $showErrorMessages)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$CheckPhoneFormStateImpl &&
(identical(other.phoneNumber, phoneNumber) ||
other.phoneNumber == phoneNumber) &&
(identical(
other.failureOrCheckPhoneOption,
failureOrCheckPhoneOption,
) ||
other.failureOrCheckPhoneOption == failureOrCheckPhoneOption) &&
(identical(other.isSubmitting, isSubmitting) ||
other.isSubmitting == isSubmitting) &&
(identical(other.showErrorMessages, showErrorMessages) ||
other.showErrorMessages == showErrorMessages));
}
@override
int get hashCode => Object.hash(
runtimeType,
phoneNumber,
failureOrCheckPhoneOption,
isSubmitting,
showErrorMessages,
);
/// Create a copy of CheckPhoneFormState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$CheckPhoneFormStateImplCopyWith<_$CheckPhoneFormStateImpl> get copyWith =>
__$$CheckPhoneFormStateImplCopyWithImpl<_$CheckPhoneFormStateImpl>(
this,
_$identity,
);
}
abstract class _CheckPhoneFormState implements CheckPhoneFormState {
const factory _CheckPhoneFormState({
required final String phoneNumber,
required final Option<Either<AuthFailure, CheckPhone>>
failureOrCheckPhoneOption,
final bool isSubmitting,
final bool showErrorMessages,
}) = _$CheckPhoneFormStateImpl;
@override
String get phoneNumber;
@override
Option<Either<AuthFailure, CheckPhone>> get failureOrCheckPhoneOption;
@override
bool get isSubmitting;
@override
bool get showErrorMessages;
/// Create a copy of CheckPhoneFormState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$CheckPhoneFormStateImplCopyWith<_$CheckPhoneFormStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,8 @@
part of 'check_phone_form_bloc.dart';
@freezed
class CheckPhoneFormEvent with _$CheckPhoneFormEvent {
const factory CheckPhoneFormEvent.phoneNumberChanged(String phoneNumber) =
_PhoneNumberChanged;
const factory CheckPhoneFormEvent.submitted() = _Submitted;
}

View File

@ -0,0 +1,14 @@
part of 'check_phone_form_bloc.dart';
@freezed
class CheckPhoneFormState with _$CheckPhoneFormState {
const factory CheckPhoneFormState({
required String phoneNumber,
required Option<Either<AuthFailure, CheckPhone>> failureOrCheckPhoneOption,
@Default(false) bool isSubmitting,
@Default(false) bool showErrorMessages,
}) = _CheckPhoneFormState;
factory CheckPhoneFormState.initial() =>
CheckPhoneFormState(phoneNumber: '', failureOrCheckPhoneOption: none());
}

View File

@ -11,6 +11,8 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes // ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:connectivity_plus/connectivity_plus.dart' as _i895; import 'package:connectivity_plus/connectivity_plus.dart' as _i895;
import 'package:dio/dio.dart' as _i361; import 'package:dio/dio.dart' as _i361;
import 'package:enaklo/application/auth/check_phone_form/check_phone_form_bloc.dart'
as _i869;
import 'package:enaklo/common/api/api_client.dart' as _i842; import 'package:enaklo/common/api/api_client.dart' as _i842;
import 'package:enaklo/common/di/di_auto_route.dart' as _i619; import 'package:enaklo/common/di/di_auto_route.dart' as _i619;
import 'package:enaklo/common/di/di_connectivity.dart' as _i644; import 'package:enaklo/common/di/di_connectivity.dart' as _i644;
@ -63,6 +65,9 @@ extension GetItInjectableX on _i174.GetIt {
gh.factory<_i995.IAuthRepository>( gh.factory<_i995.IAuthRepository>(
() => _i879.AuthRepository(gh<_i818.AuthRemoteDataProvider>()), () => _i879.AuthRepository(gh<_i818.AuthRemoteDataProvider>()),
); );
gh.factory<_i869.CheckPhoneFormBloc>(
() => _i869.CheckPhoneFormBloc(gh<_i995.IAuthRepository>()),
);
return this; return this;
} }
} }

View File

@ -121,6 +121,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.0" version: "1.3.0"
bloc:
dependency: "direct main"
description:
name: bloc
sha256: "52c10575f4445c61dd9e0cafcc6356fdd827c4c64dd7945ef3c4105f6b6ac189"
url: "https://pub.dev"
source: hosted
version: "9.0.0"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -422,6 +430,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_bloc:
dependency: "direct main"
description:
name: flutter_bloc
sha256: cf51747952201a455a1c840f8171d273be009b932c75093020f9af64f2123e38
url: "https://pub.dev"
source: hosted
version: "9.1.1"
flutter_cache_manager: flutter_cache_manager:
dependency: transitive dependency: transitive
description: description:
@ -704,6 +720,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.0" version: "2.0.0"
nested:
dependency: transitive
description:
name: nested
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
nm: nm:
dependency: transitive dependency: transitive
description: description:
@ -832,6 +856,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.3" version: "6.0.3"
provider:
dependency: transitive
description:
name: provider
sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272"
url: "https://pub.dev"
source: hosted
version: "6.1.5+1"
pub_semver: pub_semver:
dependency: transitive dependency: transitive
description: description:

View File

@ -32,6 +32,8 @@ dependencies:
cached_network_image: ^3.4.1 cached_network_image: ^3.4.1
shimmer: ^3.0.0 shimmer: ^3.0.0
audioplayers: ^6.5.1 audioplayers: ^6.5.1
flutter_bloc: ^9.1.1
bloc: ^9.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: