feat(image_picker): Add image picker both from gallery and camera
This commit is contained in:
parent
8f329f774d
commit
4ef204f31b
|
|
@ -1,4 +1,11 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.camera"
|
||||||
|
android:required="false" />
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.CAMERA"/>
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
|
||||||
<application
|
<application
|
||||||
android:label="yimaru_app"
|
android:label="yimaru_app"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import 'package:yimaru_app/ui/views/welcome/welcome_view.dart';
|
||||||
import 'package:yimaru_app/ui/views/assessment/assessment_view.dart';
|
import 'package:yimaru_app/ui/views/assessment/assessment_view.dart';
|
||||||
import 'package:yimaru_app/ui/views/learn_lesson/learn_lesson_view.dart';
|
import 'package:yimaru_app/ui/views/learn_lesson/learn_lesson_view.dart';
|
||||||
import 'package:yimaru_app/ui/views/failure/failure_view.dart';
|
import 'package:yimaru_app/ui/views/failure/failure_view.dart';
|
||||||
|
import 'package:yimaru_app/services/permission_handler_service.dart';
|
||||||
import 'package:yimaru_app/services/image_picker_service.dart';
|
import 'package:yimaru_app/services/image_picker_service.dart';
|
||||||
// @stacked-import
|
// @stacked-import
|
||||||
|
|
||||||
|
|
@ -71,6 +72,7 @@ import 'package:yimaru_app/services/image_picker_service.dart';
|
||||||
LazySingleton(classType: SecureStorageService),
|
LazySingleton(classType: SecureStorageService),
|
||||||
LazySingleton(classType: DioService),
|
LazySingleton(classType: DioService),
|
||||||
LazySingleton(classType: StatusCheckerService),
|
LazySingleton(classType: StatusCheckerService),
|
||||||
|
LazySingleton(classType: PermissionHandlerService),
|
||||||
LazySingleton(classType: ImagePickerService),
|
LazySingleton(classType: ImagePickerService),
|
||||||
// @stacked-service
|
// @stacked-service
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import '../services/api_service.dart';
|
||||||
import '../services/authentication_service.dart';
|
import '../services/authentication_service.dart';
|
||||||
import '../services/dio_service.dart';
|
import '../services/dio_service.dart';
|
||||||
import '../services/image_picker_service.dart';
|
import '../services/image_picker_service.dart';
|
||||||
|
import '../services/permission_handler_service.dart';
|
||||||
import '../services/secure_storage_service.dart';
|
import '../services/secure_storage_service.dart';
|
||||||
import '../services/status_checker_service.dart';
|
import '../services/status_checker_service.dart';
|
||||||
|
|
||||||
|
|
@ -37,5 +38,6 @@ Future<void> setupLocator({
|
||||||
locator.registerLazySingleton(() => SecureStorageService());
|
locator.registerLazySingleton(() => SecureStorageService());
|
||||||
locator.registerLazySingleton(() => DioService());
|
locator.registerLazySingleton(() => DioService());
|
||||||
locator.registerLazySingleton(() => StatusCheckerService());
|
locator.registerLazySingleton(() => StatusCheckerService());
|
||||||
|
locator.registerLazySingleton(() => PermissionHandlerService());
|
||||||
locator.registerLazySingleton(() => ImagePickerService());
|
locator.registerLazySingleton(() => ImagePickerService());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/app/app.locator.dart';
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
import 'package:yimaru_app/models/user_model.dart';
|
import 'package:yimaru_app/models/user_model.dart';
|
||||||
import 'package:yimaru_app/services/secure_storage_service.dart';
|
import 'package:yimaru_app/services/secure_storage_service.dart';
|
||||||
|
|
||||||
class AuthenticationService {
|
class AuthenticationService with ListenableServiceMixin {
|
||||||
final _secureService = locator<SecureStorageService>();
|
final _secureService = locator<SecureStorageService>();
|
||||||
|
|
||||||
|
AuthenticationService() {
|
||||||
|
listenToReactiveValues([_user]);
|
||||||
|
}
|
||||||
|
|
||||||
UserModel? _user;
|
UserModel? _user;
|
||||||
|
|
||||||
UserModel? get user => _user;
|
UserModel? get user => _user;
|
||||||
|
|
@ -35,11 +40,12 @@ class AuthenticationService {
|
||||||
Future<void> saveUserName(Map<String, dynamic> data) async {
|
Future<void> saveUserName(Map<String, dynamic> data) async {
|
||||||
await _secureService.setString('firstName', data['firstName']);
|
await _secureService.setString('firstName', data['firstName']);
|
||||||
_user = UserModel(
|
_user = UserModel(
|
||||||
firstName: await _secureService.getString('firstName'),
|
|
||||||
userId: _user?.userId,
|
userId: _user?.userId,
|
||||||
accessToken: _user?.accessToken,
|
accessToken: _user?.accessToken,
|
||||||
refreshToken: _user?.refreshToken,
|
refreshToken: _user?.refreshToken,
|
||||||
profileCompleted: _user?.profileCompleted);
|
profileCompleted: _user?.profileCompleted,
|
||||||
|
firstName: await _secureService.getString('firstName'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveBasicUserData(Map<String, dynamic> data) async {
|
Future<void> saveBasicUserData(Map<String, dynamic> data) async {
|
||||||
|
|
@ -69,7 +75,19 @@ class AuthenticationService {
|
||||||
profileCompleted: await _secureService.getBool('profileCompleted'));
|
profileCompleted: await _secureService.getBool('profileCompleted'));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveProfileImage() async {}
|
Future<void> saveProfileImage(String image) async {
|
||||||
|
await _secureService.setString('profileImage', image);
|
||||||
|
_user = UserModel(
|
||||||
|
userId: _user?.userId,
|
||||||
|
firstName: _user?.firstName,
|
||||||
|
accessToken: _user?.accessToken,
|
||||||
|
refreshToken: _user?.refreshToken,
|
||||||
|
profileCompleted: _user?.profileCompleted,
|
||||||
|
profileImage: await _secureService.getString('profileImage'),
|
||||||
|
);
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> saveFullName(Map<String, dynamic> data) async {
|
Future<void> saveFullName(Map<String, dynamic> data) async {
|
||||||
await _secureService.setBool('profileCompleted', true);
|
await _secureService.setBool('profileCompleted', true);
|
||||||
|
|
|
||||||
|
|
@ -139,14 +139,6 @@ class DioService {
|
||||||
final response = await _refreshDio.post(
|
final response = await _refreshDio.post(
|
||||||
'$baseUrl/$kRefreshTokenUrl',
|
'$baseUrl/$kRefreshTokenUrl',
|
||||||
data: data,
|
data: data,
|
||||||
options: Options(
|
|
||||||
followRedirects: false,
|
|
||||||
validateStatus: (status) => true,
|
|
||||||
headers: {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await _authenticationService.saveTokens(
|
await _authenticationService.saveTokens(
|
||||||
|
|
@ -156,8 +148,9 @@ class DioService {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await _authenticationService.logOut();
|
print('Token refresh exception ${e.toString()}');
|
||||||
await _navigationService.replaceWithLoginView();
|
// await _authenticationService.logOut();
|
||||||
|
// await _navigationService.replaceWithLoginView();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,56 @@
|
||||||
class ImagePickerService {}
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
import 'package:yimaru_app/services/permission_handler_service.dart';
|
||||||
|
|
||||||
|
import '../app/app.locator.dart';
|
||||||
|
import '../ui/common/ui_helpers.dart';
|
||||||
|
|
||||||
|
class ImagePickerService {
|
||||||
|
final _permissionHandler = locator<PermissionHandlerService>();
|
||||||
|
|
||||||
|
final ImagePicker _picker = ImagePicker();
|
||||||
|
|
||||||
|
Future<String?> gallery() async {
|
||||||
|
try {
|
||||||
|
PermissionStatus status =
|
||||||
|
await _permissionHandler.requestPermission(Permission.mediaLibrary);
|
||||||
|
|
||||||
|
if (status == PermissionStatus.granted) {
|
||||||
|
final XFile? pickedFile = await _picker.pickImage(
|
||||||
|
source: ImageSource.gallery, maxWidth: 600, maxHeight: 600);
|
||||||
|
|
||||||
|
if (pickedFile == null) {
|
||||||
|
showErrorToast('Please select a picture');
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return pickedFile.path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> camera() async {
|
||||||
|
try {
|
||||||
|
PermissionStatus status =
|
||||||
|
await _permissionHandler.requestPermission(Permission.camera);
|
||||||
|
|
||||||
|
if (status == PermissionStatus.granted) {
|
||||||
|
final XFile? pickedFile = await _picker.pickImage(
|
||||||
|
source: ImageSource.camera, maxWidth: 600, maxHeight: 600);
|
||||||
|
|
||||||
|
if (pickedFile == null) {
|
||||||
|
showErrorToast('Please take a picture');
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return pickedFile.path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
31
lib/services/permission_handler_service.dart
Normal file
31
lib/services/permission_handler_service.dart
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
|
import '../ui/common/ui_helpers.dart';
|
||||||
|
|
||||||
|
class PermissionHandlerService {
|
||||||
|
Future<PermissionStatus> requestPermission(
|
||||||
|
Permission requestedPermission) async {
|
||||||
|
if (requestedPermission == Permission.camera) {
|
||||||
|
return await request(Permission.camera);
|
||||||
|
}
|
||||||
|
if (requestedPermission == Permission.storage) {
|
||||||
|
return await request(Permission.storage);
|
||||||
|
}
|
||||||
|
if (requestedPermission == Permission.mediaLibrary) {
|
||||||
|
return await request(Permission.mediaLibrary);
|
||||||
|
}
|
||||||
|
return PermissionStatus.denied;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<PermissionStatus> request(Permission permission) async {
|
||||||
|
if (await permission.isDenied) {
|
||||||
|
final PermissionStatus status = await permission.request();
|
||||||
|
|
||||||
|
if (status.isDenied || status.isPermanentlyDenied) {
|
||||||
|
showErrorToast('Permission Denied');
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
return PermissionStatus.granted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,3 +8,6 @@ enum ProgressStatuses { pending, started, completed }
|
||||||
|
|
||||||
// Levels
|
// Levels
|
||||||
enum ProficiencyLevels { a1, a2, b1, b2, none }
|
enum ProficiencyLevels { a1, a2, b1, b2, none }
|
||||||
|
|
||||||
|
// State object
|
||||||
|
enum StateObjects{profileImage}
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,12 @@ TextStyle style18P600 = const TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TextStyle style18W600 = const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
color: kcWhite,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
);
|
||||||
|
|
||||||
TextStyle style12R700 = const TextStyle(
|
TextStyle style12R700 = const TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
|
|
|
||||||
|
|
@ -171,11 +171,6 @@ class AssessmentViewModel extends BaseViewModel {
|
||||||
|
|
||||||
// Complete profile
|
// Complete profile
|
||||||
|
|
||||||
Future<void> saveProfileCompleted() async {
|
|
||||||
Map<String, dynamic> data = {'firstName': _userData['firstName']};
|
|
||||||
await _authenticationService.saveFullName(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> completeProfile() async =>
|
Future<void> completeProfile() async =>
|
||||||
await runBusyFuture<Map<String, dynamic>>(_completeProfile());
|
await runBusyFuture<Map<String, dynamic>>(_completeProfile());
|
||||||
|
|
||||||
|
|
@ -185,7 +180,6 @@ class AssessmentViewModel extends BaseViewModel {
|
||||||
await _apiService.updateProfile(data: _userData, user: user);
|
await _apiService.updateProfile(data: _userData, user: user);
|
||||||
if (response['status'] == ResponseStatus.success) {
|
if (response['status'] == ResponseStatus.success) {
|
||||||
showSuccessToast(response['message']);
|
showSuccessToast(response['message']);
|
||||||
await saveProfileCompleted();
|
|
||||||
await replaceWithHome();
|
await replaceWithHome();
|
||||||
} else {
|
} else {
|
||||||
showErrorToast(response['message']);
|
showErrorToast(response['message']);
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,7 @@ class HomeView extends StackedView<HomeViewModel> {
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(HomeViewModel viewModel) => viewModel.isBusy
|
Widget _buildScaffoldWrapper(HomeViewModel viewModel) => viewModel.isBusy
|
||||||
? const StartupView(
|
? const StartupView(label: 'Checking user info')
|
||||||
label: 'Checking user info',
|
|
||||||
)
|
|
||||||
: _buildScaffold(viewModel);
|
: _buildScaffold(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffold(HomeViewModel viewModel) => Scaffold(
|
Widget _buildScaffold(HomeViewModel viewModel) => Scaffold(
|
||||||
|
|
|
||||||
|
|
@ -93,18 +93,10 @@ class HomeViewModel extends BaseViewModel {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> getProfileStatus() async {
|
Future<void> getProfileStatus() async =>
|
||||||
Map<String, dynamic> response =
|
await runBusyFuture(_getProfileStatus());
|
||||||
await runBusyFuture<Map<String, dynamic>>(_getProfileStatus());
|
|
||||||
if (response['status'] == ResponseStatus.success && !response['data']) {
|
|
||||||
await replaceWithOnboarding();
|
|
||||||
} else if (response['status'] == ResponseStatus.success &&
|
|
||||||
response['data']) {
|
|
||||||
await saveProfileStatus(response['data']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Map<String, dynamic>> _getProfileStatus() async {
|
Future<void> _getProfileStatus() async {
|
||||||
Map<String, dynamic> response = {};
|
Map<String, dynamic> response = {};
|
||||||
|
|
||||||
UserModel? user = await _authenticationService.getUser();
|
UserModel? user = await _authenticationService.getUser();
|
||||||
|
|
@ -118,6 +110,11 @@ class HomeViewModel extends BaseViewModel {
|
||||||
response = {'data': true, 'status': ResponseStatus.success};
|
response = {'data': true, 'status': ResponseStatus.success};
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
if (response['status'] == ResponseStatus.success && !response['data']) {
|
||||||
|
await replaceWithOnboarding();
|
||||||
|
} else if (response['status'] == ResponseStatus.success &&
|
||||||
|
response['data']) {
|
||||||
|
await saveProfileStatus(response['data']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,16 @@ import 'package:yimaru_app/ui/common/enmus.dart';
|
||||||
|
|
||||||
import '../../../app/app.locator.dart';
|
import '../../../app/app.locator.dart';
|
||||||
|
|
||||||
class LearnViewModel extends BaseViewModel {
|
class LearnViewModel extends ReactiveViewModel {
|
||||||
final _navigationService = locator<NavigationService>();
|
final _navigationService = locator<NavigationService>();
|
||||||
final _authenticationService = locator<AuthenticationService>();
|
final _authenticationService = locator<AuthenticationService>();
|
||||||
|
|
||||||
late final UserModel? _user = _authenticationService.user;
|
@override
|
||||||
|
List<ListenableServiceMixin> get listenableServices =>
|
||||||
|
[_authenticationService];
|
||||||
|
|
||||||
UserModel? get user => _user;
|
// Current user
|
||||||
|
UserModel? get user => _authenticationService.user;
|
||||||
|
|
||||||
final List<Map<String, dynamic>> _learnLevels = [
|
final List<Map<String, dynamic>> _learnLevels = [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,37 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/custom_circular_progress_indicator.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/profile_card.dart';
|
import 'package:yimaru_app/ui/widgets/profile_card.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/profile_image.dart';
|
import 'package:yimaru_app/ui/widgets/profile_image.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/view_profile_button.dart';
|
import 'package:yimaru_app/ui/widgets/view_profile_button.dart';
|
||||||
|
|
||||||
import '../../widgets/custom_elevated_button.dart';
|
import '../../widgets/custom_elevated_button.dart';
|
||||||
|
import '../../widgets/image_picker_option.dart';
|
||||||
import 'profile_viewmodel.dart';
|
import 'profile_viewmodel.dart';
|
||||||
|
|
||||||
class ProfileView extends StackedView<ProfileViewModel> {
|
class ProfileView extends StackedView<ProfileViewModel> {
|
||||||
const ProfileView({Key? key}) : super(key: key);
|
const ProfileView({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
Future<void> _showImagePicker(
|
||||||
|
{required BuildContext context,
|
||||||
|
required ProfileViewModel viewModel}) async =>
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) =>
|
||||||
|
_showImagePickerDialog(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
AlertDialog _showImagePickerDialog(
|
||||||
|
{required BuildContext context,
|
||||||
|
required ProfileViewModel viewModel}) =>
|
||||||
|
AlertDialog(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
content: _buildImagePicker(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ProfileViewModel viewModelBuilder(
|
ProfileViewModel viewModelBuilder(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
|
|
@ -24,30 +44,45 @@ class ProfileView extends StackedView<ProfileViewModel> {
|
||||||
ProfileViewModel viewModel,
|
ProfileViewModel viewModel,
|
||||||
Widget? child,
|
Widget? child,
|
||||||
) =>
|
) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(context: context, viewModel: viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(ProfileViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(
|
||||||
|
{required BuildContext context,
|
||||||
|
required ProfileViewModel viewModel}) =>
|
||||||
|
Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(context: context, viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(ProfileViewModel viewModel) =>
|
Widget _buildScaffold(
|
||||||
SafeArea(child: _buildBodyWrapper(viewModel));
|
{required BuildContext context,
|
||||||
|
required ProfileViewModel viewModel}) =>
|
||||||
|
SafeArea(
|
||||||
|
child: _buildBodyWrapper(context: context, viewModel: viewModel));
|
||||||
|
|
||||||
Widget _buildBodyWrapper(ProfileViewModel viewModel) => SingleChildScrollView(
|
Widget _buildBodyWrapper(
|
||||||
child: _buildBody(viewModel),
|
{required BuildContext context,
|
||||||
|
required ProfileViewModel viewModel}) =>
|
||||||
|
SingleChildScrollView(
|
||||||
|
child: _buildBody(context: context, viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBody(ProfileViewModel viewModel) => Padding(
|
Widget _buildBody(
|
||||||
|
{required BuildContext context,
|
||||||
|
required ProfileViewModel viewModel}) =>
|
||||||
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildColumn(viewModel),
|
child: _buildColumn(context: context, viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildColumn(ProfileViewModel viewModel) => Column(
|
Widget _buildColumn(
|
||||||
|
{required BuildContext context,
|
||||||
|
required ProfileViewModel viewModel}) =>
|
||||||
|
Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildNotificationIconWrapper(),
|
_buildNotificationIconWrapper(),
|
||||||
_buildProfileSection(viewModel),
|
_buildProfileSection(context: context, viewModel: viewModel),
|
||||||
verticalSpaceSmall,
|
verticalSpaceSmall,
|
||||||
_buildViewProfileButton(viewModel),
|
_buildViewProfileButton(viewModel),
|
||||||
verticalSpaceLarge,
|
verticalSpaceLarge,
|
||||||
|
|
@ -66,20 +101,42 @@ class ProfileView extends StackedView<ProfileViewModel> {
|
||||||
color: kcDarkGrey,
|
color: kcDarkGrey,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildProfileSection(ProfileViewModel viewModel) => Column(
|
Widget _buildProfileSection(
|
||||||
|
{required BuildContext context,
|
||||||
|
required ProfileViewModel viewModel}) =>
|
||||||
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: _buildProfileSectionChildren(viewModel),
|
children: _buildProfileSectionChildren(
|
||||||
|
context: context, viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildProfileSectionChildren(ProfileViewModel viewModel) => [
|
List<Widget> _buildProfileSectionChildren(
|
||||||
_buildProfileImage(viewModel),
|
{required BuildContext context,
|
||||||
|
required ProfileViewModel viewModel}) =>
|
||||||
|
[
|
||||||
|
_buildProfileImage(context: context, viewModel: viewModel),
|
||||||
verticalSpaceSmall,
|
verticalSpaceSmall,
|
||||||
_buildProfileName(viewModel),
|
_buildProfileName(viewModel),
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildProfileImage(ProfileViewModel viewModel) => ProfileImage(
|
|
||||||
|
Widget _buildProfileImage(
|
||||||
|
{required BuildContext context,
|
||||||
|
required ProfileViewModel viewModel}) =>
|
||||||
|
ProfileImage(
|
||||||
profileImage: viewModel.user?.profileImage,
|
profileImage: viewModel.user?.profileImage,
|
||||||
|
loading: viewModel.busy(StateObjects.profileImage) ? true:false,
|
||||||
|
onTap: () async =>
|
||||||
|
await _showImagePicker(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildImagePicker(
|
||||||
|
{required BuildContext context,
|
||||||
|
required ProfileViewModel viewModel}) =>
|
||||||
|
ImagePickerOption(
|
||||||
|
onCameraTap: () async => await viewModel.openCamera(),
|
||||||
|
onGalleryTap: () async => await viewModel.openGallery(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildProfileName(ProfileViewModel viewModel) => Text(
|
Widget _buildProfileName(ProfileViewModel viewModel) => Text(
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,58 @@
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:stacked_services/stacked_services.dart';
|
import 'package:stacked_services/stacked_services.dart';
|
||||||
import 'package:yimaru_app/app/app.router.dart';
|
import 'package:yimaru_app/app/app.router.dart';
|
||||||
|
import 'package:yimaru_app/services/image_picker_service.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||||
|
|
||||||
import '../../../app/app.locator.dart';
|
import '../../../app/app.locator.dart';
|
||||||
import '../../../models/user_model.dart';
|
import '../../../models/user_model.dart';
|
||||||
import '../../../services/authentication_service.dart';
|
import '../../../services/authentication_service.dart';
|
||||||
|
|
||||||
class ProfileViewModel extends BaseViewModel {
|
class ProfileViewModel extends ReactiveViewModel {
|
||||||
final _navigationService = locator<NavigationService>();
|
final _navigationService = locator<NavigationService>();
|
||||||
|
|
||||||
|
final _imagePickerService = locator<ImagePickerService>();
|
||||||
|
|
||||||
final _authenticationService = locator<AuthenticationService>();
|
final _authenticationService = locator<AuthenticationService>();
|
||||||
|
|
||||||
late final UserModel? _user = _authenticationService.user;
|
@override
|
||||||
|
List<ListenableServiceMixin> get listenableServices =>
|
||||||
|
[_authenticationService];
|
||||||
|
|
||||||
UserModel? get user => _user;
|
// Current user
|
||||||
|
UserModel? get user => _authenticationService.user;
|
||||||
|
|
||||||
|
// Image picker
|
||||||
|
Future<void> openCamera() async => runBusyFuture(_openCamera(),busyObject: StateObjects.profileImage);
|
||||||
|
|
||||||
|
Future<void> _openCamera()async{
|
||||||
|
String? image = await _imagePickerService.camera();
|
||||||
|
if (image != null) {
|
||||||
|
await _authenticationService.saveProfileImage(image);
|
||||||
|
}
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> openGallery() async => runBusyFuture(_openGallery(),busyObject: StateObjects.profileImage);
|
||||||
|
|
||||||
|
|
||||||
|
Future<void> _openGallery() async {
|
||||||
|
String? image = await _imagePickerService.gallery();
|
||||||
|
if (image != null) {
|
||||||
|
await _authenticationService.saveProfileImage(image);
|
||||||
|
}
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logout
|
||||||
Future<void> logOut() async {
|
Future<void> logOut() async {
|
||||||
await _authenticationService.logOut();
|
await _authenticationService.logOut();
|
||||||
await _navigationService.replaceWithLoginView();
|
await _navigationService.replaceWithLoginView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
void pop() => _navigationService.back();
|
||||||
|
|
||||||
Future<void> navigateToProfileDetail() async =>
|
Future<void> navigateToProfileDetail() async =>
|
||||||
await _navigationService.navigateToProfileDetailView();
|
await _navigationService.navigateToProfileDetailView();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,5 +82,5 @@ class CustomLargeRadioButton extends StatelessWidget {
|
||||||
Widget _buildSelectedCheckBox() => Checkbox(
|
Widget _buildSelectedCheckBox() => Checkbox(
|
||||||
value: selected,
|
value: selected,
|
||||||
activeColor: kcPrimaryColor,
|
activeColor: kcPrimaryColor,
|
||||||
onChanged: (value) => onTap);
|
onChanged: onTap != null ? (value) => onTap!() : null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
82
lib/ui/widgets/image_picker_option.dart
Normal file
82
lib/ui/widgets/image_picker_option.dart
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
|
||||||
|
class ImagePickerOption extends StatelessWidget {
|
||||||
|
final GestureTapCallback? onCameraTap;
|
||||||
|
final GestureTapCallback? onGalleryTap;
|
||||||
|
|
||||||
|
const ImagePickerOption({super.key, this.onCameraTap, this.onGalleryTap});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => _buildContainer();
|
||||||
|
|
||||||
|
Widget _buildContainer() => Container(
|
||||||
|
height: 200,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: kcBackgroundColor,
|
||||||
|
shape: BoxShape.rectangle,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(32.0)),
|
||||||
|
),
|
||||||
|
child: _buildCameraOptionWrapper(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildCameraOptionWrapper() => Center(
|
||||||
|
child: _buildCameraOption(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildCameraOption() => Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: _buildCameraOptionChildren(),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildCameraOptionChildren() =>
|
||||||
|
[_buildCameraButton(), _buildGalleryButton()];
|
||||||
|
|
||||||
|
Widget _buildCameraButton() => GestureDetector(
|
||||||
|
onTap: onCameraTap,
|
||||||
|
child: _buildCamera(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildCamera() => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: _buildCameraChildren(),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildCameraChildren() =>
|
||||||
|
[_buildCameraIcon(), verticalSpaceTiny, _buildCameraTitle()];
|
||||||
|
|
||||||
|
Widget _buildCameraIcon() => const Icon(
|
||||||
|
Icons.camera_alt_rounded,
|
||||||
|
size: 60,
|
||||||
|
color: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildCameraTitle() => Text(
|
||||||
|
'Camera',
|
||||||
|
style: style18P600,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildGalleryButton() => GestureDetector(
|
||||||
|
onTap: onGalleryTap,
|
||||||
|
child: _buildGallery(),
|
||||||
|
);
|
||||||
|
Widget _buildGallery() => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: _buildGalleryChildren(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildGalleryIcon() => const Icon(
|
||||||
|
Icons.photo,
|
||||||
|
size: 60,
|
||||||
|
color: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildGalleryText() => Text(
|
||||||
|
'Gallery',
|
||||||
|
style: style18P600,
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildGalleryChildren() =>
|
||||||
|
[_buildGalleryIcon(), verticalSpaceTiny, _buildGalleryText()];
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
|
@ -39,7 +41,9 @@ class LearnAppBar extends StatelessWidget {
|
||||||
radius: 25,
|
radius: 25,
|
||||||
backgroundColor: kcPrimaryColor,
|
backgroundColor: kcPrimaryColor,
|
||||||
backgroundImage: profileImage != null
|
backgroundImage: profileImage != null
|
||||||
? CachedNetworkImageProvider(profileImage!)
|
? FileImage(
|
||||||
|
File(profileImage!),
|
||||||
|
)
|
||||||
: null,
|
: null,
|
||||||
child: _buildImageBuilder(),
|
child: _buildImageBuilder(),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,26 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
|
||||||
class ProfileImage extends StatelessWidget {
|
class ProfileImage extends StatelessWidget {
|
||||||
|
final bool loading;
|
||||||
final String? profileImage;
|
final String? profileImage;
|
||||||
const ProfileImage({super.key, required this.profileImage});
|
final GestureTapCallback? onTap;
|
||||||
|
|
||||||
|
const ProfileImage(
|
||||||
|
{super.key,
|
||||||
|
this.onTap,
|
||||||
|
this.loading = false,
|
||||||
|
required this.profileImage});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => _buildSizedBox();
|
Widget build(BuildContext context) => _buildSizedBox();
|
||||||
|
|
||||||
Widget _buildSizedBox() => SizedBox(
|
Widget _buildSizedBox() => SizedBox(
|
||||||
height: 125,
|
|
||||||
width: 125,
|
width: 125,
|
||||||
|
height: 125,
|
||||||
child: _buildStack(),
|
child: _buildStack(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -27,14 +36,21 @@ class ProfileImage extends StatelessWidget {
|
||||||
Widget _buildProfileImage() => CircleAvatar(
|
Widget _buildProfileImage() => CircleAvatar(
|
||||||
radius: 50,
|
radius: 50,
|
||||||
backgroundColor: kcPrimaryColor,
|
backgroundColor: kcPrimaryColor,
|
||||||
backgroundImage: profileImage != null
|
backgroundImage: loading
|
||||||
? CachedNetworkImageProvider(profileImage!)
|
? null
|
||||||
|
: profileImage != null
|
||||||
|
? FileImage(
|
||||||
|
File(profileImage!),
|
||||||
|
)
|
||||||
: null,
|
: null,
|
||||||
child: _buildImageBuilder(),
|
child: _buildImageBuilder(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget? _buildImageBuilder() =>
|
Widget? _buildImageBuilder() => loading
|
||||||
profileImage == null ? _buildPersonIcon() : null;
|
? null
|
||||||
|
: profileImage == null
|
||||||
|
? _buildPersonIcon()
|
||||||
|
: null;
|
||||||
|
|
||||||
Widget _buildPersonIcon() => const Icon(
|
Widget _buildPersonIcon() => const Icon(
|
||||||
Icons.person,
|
Icons.person,
|
||||||
|
|
@ -44,6 +60,11 @@ class ProfileImage extends StatelessWidget {
|
||||||
|
|
||||||
Widget _buildCameraButtonWrapper() => Align(
|
Widget _buildCameraButtonWrapper() => Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: _buildCameraTapDetector(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildCameraTapDetector() => GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
child: _buildCameraButton(),
|
child: _buildCameraButton(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,13 @@
|
||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <file_selector_linux/file_selector_plugin.h>
|
||||||
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
|
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
||||||
|
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
||||||
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
file_selector_linux
|
||||||
flutter_secure_storage_linux
|
flutter_secure_storage_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,14 @@ import Foundation
|
||||||
|
|
||||||
import battery_plus
|
import battery_plus
|
||||||
import connectivity_plus
|
import connectivity_plus
|
||||||
|
import file_selector_macos
|
||||||
import flutter_secure_storage_darwin
|
import flutter_secure_storage_darwin
|
||||||
import sqflite_darwin
|
import sqflite_darwin
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
BatteryPlusMacosPlugin.register(with: registry.registrar(forPlugin: "BatteryPlusMacosPlugin"))
|
BatteryPlusMacosPlugin.register(with: registry.registrar(forPlugin: "BatteryPlusMacosPlugin"))
|
||||||
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
||||||
|
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||||
FlutterSecureStorageDarwinPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageDarwinPlugin"))
|
FlutterSecureStorageDarwinPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageDarwinPlugin"))
|
||||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
160
pubspec.lock
160
pubspec.lock
|
|
@ -225,6 +225,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.2"
|
version: "3.1.2"
|
||||||
|
cross_file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cross_file
|
||||||
|
sha256: "701dcfc06da0882883a2657c445103380e53e647060ad8d9dfb710c100996608"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.5+1"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -321,6 +329,38 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.1"
|
version: "7.0.1"
|
||||||
|
file_selector_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_linux
|
||||||
|
sha256: "2567f398e06ac72dcf2e98a0c95df2a9edd03c2c2e0cacd4780f20cdf56263a0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.4"
|
||||||
|
file_selector_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_macos
|
||||||
|
sha256: "5e0bbe9c312416f1787a68259ea1505b52f258c587f12920422671807c4d618a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.5"
|
||||||
|
file_selector_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_platform_interface
|
||||||
|
sha256: "35e0bd61ebcdb91a3505813b055b09b79dfdc7d0aee9c09a7ba59ae4bb13dc85"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.7.0"
|
||||||
|
file_selector_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file_selector_windows
|
||||||
|
sha256: "62197474ae75893a62df75939c777763d39c2bc5f73ce5b88497208bc269abfd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.3+5"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -366,6 +406,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.3"
|
version: "2.0.3"
|
||||||
|
flutter_plugin_android_lifecycle:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_plugin_android_lifecycle
|
||||||
|
sha256: ee8068e0e1cd16c4a82714119918efdeed33b3ba7772c54b5d094ab53f9b7fd1
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.33"
|
||||||
flutter_secure_storage:
|
flutter_secure_storage:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -552,6 +600,70 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.1"
|
||||||
|
image_picker:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: image_picker
|
||||||
|
sha256: "784210112be18ea55f69d7076e2c656a4e24949fa9e76429fe53af0c0f4fa320"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
image_picker_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_android
|
||||||
|
sha256: "297e42bd236c4ac4b091d4277292159b3280545e030cae2be3d503f9ecf7e6a1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.13+12"
|
||||||
|
image_picker_for_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_for_web
|
||||||
|
sha256: "66257a3191ab360d23a55c8241c91a6e329d31e94efa7be9cf7a212e65850214"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.1"
|
||||||
|
image_picker_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_ios
|
||||||
|
sha256: "956c16a42c0c708f914021666ffcd8265dde36e673c9fa68c81f7d085d9774ad"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.13+3"
|
||||||
|
image_picker_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_linux
|
||||||
|
sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.2"
|
||||||
|
image_picker_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_macos
|
||||||
|
sha256: "86f0f15a309de7e1a552c12df9ce5b59fe927e71385329355aec4776c6a8ec91"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.2+1"
|
||||||
|
image_picker_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_platform_interface
|
||||||
|
sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.11.1"
|
||||||
|
image_picker_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: image_picker_windows
|
||||||
|
sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.2"
|
||||||
in_app_update:
|
in_app_update:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -832,6 +944,54 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0+3"
|
version: "3.1.0+3"
|
||||||
|
permission_handler:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: permission_handler
|
||||||
|
sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "12.0.1"
|
||||||
|
permission_handler_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_android
|
||||||
|
sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "13.0.1"
|
||||||
|
permission_handler_apple:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_apple
|
||||||
|
sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.4.7"
|
||||||
|
permission_handler_html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_html
|
||||||
|
sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.3+5"
|
||||||
|
permission_handler_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_platform_interface
|
||||||
|
sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.3.0"
|
||||||
|
permission_handler_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: permission_handler_windows
|
||||||
|
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.1"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ dependencies:
|
||||||
iconsax: ^0.0.8
|
iconsax: ^0.0.8
|
||||||
flutter_svg: ^2.2.3
|
flutter_svg: ^2.2.3
|
||||||
stacked_shared: any
|
stacked_shared: any
|
||||||
|
image_picker: ^1.2.1
|
||||||
battery_plus: ^7.0.0
|
battery_plus: ^7.0.0
|
||||||
storage_info: ^1.0.0
|
storage_info: ^1.0.0
|
||||||
flutter_html: ^3.0.0
|
flutter_html: ^3.0.0
|
||||||
|
|
@ -27,6 +28,7 @@ dependencies:
|
||||||
stacked_services: ^1.1.0
|
stacked_services: ^1.1.0
|
||||||
omni_datetime_picker: any
|
omni_datetime_picker: any
|
||||||
json_serializable: ^6.8.0
|
json_serializable: ^6.8.0
|
||||||
|
permission_handler: ^12.0.1
|
||||||
cached_network_image: ^3.4.1
|
cached_network_image: ^3.4.1
|
||||||
flutter_secure_storage: ^10.0.0
|
flutter_secure_storage: ^10.0.0
|
||||||
flutter_timer_countdown: ^1.0.7
|
flutter_timer_countdown: ^1.0.7
|
||||||
|
|
|
||||||
155
test/helpers/test_helpers.dart
Normal file
155
test/helpers/test_helpers.dart
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
import 'package:mockito/annotations.dart';
|
||||||
|
import 'package:mockito/mockito.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
import 'package:stacked_services/stacked_services.dart';
|
||||||
|
import 'package:yimaru_app/services/authentication_service.dart';
|
||||||
|
import 'package:yimaru_app/services/api_service.dart';
|
||||||
|
import 'package:yimaru_app/services/secure_storage_service.dart';
|
||||||
|
import 'package:yimaru_app/services/dio_service.dart';
|
||||||
|
import 'package:yimaru_app/services/status_checker_service.dart';
|
||||||
|
import 'package:yimaru_app/services/permission_handler_service.dart';
|
||||||
|
import 'package:yimaru_app/services/image_picker_service.dart';
|
||||||
|
// @stacked-import
|
||||||
|
|
||||||
|
import 'test_helpers.mocks.dart';
|
||||||
|
|
||||||
|
@GenerateMocks(
|
||||||
|
[],
|
||||||
|
customMocks: [
|
||||||
|
MockSpec<NavigationService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
|
MockSpec<BottomSheetService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
|
MockSpec<DialogService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
|
MockSpec<AuthenticationService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
|
MockSpec<ApiService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
|
MockSpec<SecureStorageService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
|
MockSpec<DioService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
|
MockSpec<StatusCheckerService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
|
MockSpec<PermissionHandlerService>(
|
||||||
|
onMissingStub: OnMissingStub.returnDefault),
|
||||||
|
MockSpec<ImagePickerService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
|
// @stacked-mock-spec
|
||||||
|
],
|
||||||
|
)
|
||||||
|
void registerServices() {
|
||||||
|
getAndRegisterNavigationService();
|
||||||
|
getAndRegisterBottomSheetService();
|
||||||
|
getAndRegisterDialogService();
|
||||||
|
getAndRegisterAuthenticationService();
|
||||||
|
getAndRegisterApiService();
|
||||||
|
getAndRegisterSecureStorageService();
|
||||||
|
getAndRegisterDioService();
|
||||||
|
getAndRegisterStatusCheckerService();
|
||||||
|
getAndRegisterPermissionHandlerService();
|
||||||
|
getAndRegisterImagePickerService();
|
||||||
|
// @stacked-mock-register
|
||||||
|
}
|
||||||
|
|
||||||
|
MockNavigationService getAndRegisterNavigationService() {
|
||||||
|
_removeRegistrationIfExists<NavigationService>();
|
||||||
|
final service = MockNavigationService();
|
||||||
|
locator.registerSingleton<NavigationService>(service);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
MockBottomSheetService getAndRegisterBottomSheetService<T>({
|
||||||
|
SheetResponse<T>? showCustomSheetResponse,
|
||||||
|
}) {
|
||||||
|
_removeRegistrationIfExists<BottomSheetService>();
|
||||||
|
final service = MockBottomSheetService();
|
||||||
|
|
||||||
|
when(
|
||||||
|
service.showCustomSheet<T, T>(
|
||||||
|
enableDrag: anyNamed('enableDrag'),
|
||||||
|
enterBottomSheetDuration: anyNamed('enterBottomSheetDuration'),
|
||||||
|
exitBottomSheetDuration: anyNamed('exitBottomSheetDuration'),
|
||||||
|
ignoreSafeArea: anyNamed('ignoreSafeArea'),
|
||||||
|
isScrollControlled: anyNamed('isScrollControlled'),
|
||||||
|
barrierDismissible: anyNamed('barrierDismissible'),
|
||||||
|
additionalButtonTitle: anyNamed('additionalButtonTitle'),
|
||||||
|
variant: anyNamed('variant'),
|
||||||
|
title: anyNamed('title'),
|
||||||
|
hasImage: anyNamed('hasImage'),
|
||||||
|
imageUrl: anyNamed('imageUrl'),
|
||||||
|
showIconInMainButton: anyNamed('showIconInMainButton'),
|
||||||
|
mainButtonTitle: anyNamed('mainButtonTitle'),
|
||||||
|
showIconInSecondaryButton: anyNamed('showIconInSecondaryButton'),
|
||||||
|
secondaryButtonTitle: anyNamed('secondaryButtonTitle'),
|
||||||
|
showIconInAdditionalButton: anyNamed('showIconInAdditionalButton'),
|
||||||
|
takesInput: anyNamed('takesInput'),
|
||||||
|
barrierColor: anyNamed('barrierColor'),
|
||||||
|
barrierLabel: anyNamed('barrierLabel'),
|
||||||
|
customData: anyNamed('customData'),
|
||||||
|
data: anyNamed('data'),
|
||||||
|
description: anyNamed('description'),
|
||||||
|
),
|
||||||
|
).thenAnswer(
|
||||||
|
(realInvocation) =>
|
||||||
|
Future.value(showCustomSheetResponse ?? SheetResponse<T>()),
|
||||||
|
);
|
||||||
|
|
||||||
|
locator.registerSingleton<BottomSheetService>(service);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
MockDialogService getAndRegisterDialogService() {
|
||||||
|
_removeRegistrationIfExists<DialogService>();
|
||||||
|
final service = MockDialogService();
|
||||||
|
locator.registerSingleton<DialogService>(service);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
MockAuthenticationService getAndRegisterAuthenticationService() {
|
||||||
|
_removeRegistrationIfExists<AuthenticationService>();
|
||||||
|
final service = MockAuthenticationService();
|
||||||
|
locator.registerSingleton<AuthenticationService>(service);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
MockApiService getAndRegisterApiService() {
|
||||||
|
_removeRegistrationIfExists<ApiService>();
|
||||||
|
final service = MockApiService();
|
||||||
|
locator.registerSingleton<ApiService>(service);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
MockSecureStorageService getAndRegisterSecureStorageService() {
|
||||||
|
_removeRegistrationIfExists<SecureStorageService>();
|
||||||
|
final service = MockSecureStorageService();
|
||||||
|
locator.registerSingleton<SecureStorageService>(service);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
MockDioService getAndRegisterDioService() {
|
||||||
|
_removeRegistrationIfExists<DioService>();
|
||||||
|
final service = MockDioService();
|
||||||
|
locator.registerSingleton<DioService>(service);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
MockStatusCheckerService getAndRegisterStatusCheckerService() {
|
||||||
|
_removeRegistrationIfExists<StatusCheckerService>();
|
||||||
|
final service = MockStatusCheckerService();
|
||||||
|
locator.registerSingleton<StatusCheckerService>(service);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
MockPermissionHandlerService getAndRegisterPermissionHandlerService() {
|
||||||
|
_removeRegistrationIfExists<PermissionHandlerService>();
|
||||||
|
final service = MockPermissionHandlerService();
|
||||||
|
locator.registerSingleton<PermissionHandlerService>(service);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
MockImagePickerService getAndRegisterImagePickerService() {
|
||||||
|
_removeRegistrationIfExists<ImagePickerService>();
|
||||||
|
final service = MockImagePickerService();
|
||||||
|
locator.registerSingleton<ImagePickerService>(service);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
// @stacked-mock-create
|
||||||
|
|
||||||
|
void _removeRegistrationIfExists<T extends Object>() {
|
||||||
|
if (locator.isRegistered<T>()) {
|
||||||
|
locator.unregister<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
1238
test/helpers/test_helpers.mocks.dart
Normal file
1238
test/helpers/test_helpers.mocks.dart
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -4,7 +4,7 @@ import 'package:yimaru_app/app/app.locator.dart';
|
||||||
import '../helpers/test_helpers.dart';
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('FailureViewModel Tests -', () {
|
group('ApiServiceTest -', () {
|
||||||
setUp(() => registerServices());
|
setUp(() => registerServices());
|
||||||
tearDown(() => locator.reset());
|
tearDown(() => locator.reset());
|
||||||
});
|
});
|
||||||
|
|
@ -4,7 +4,7 @@ import 'package:yimaru_app/app/app.locator.dart';
|
||||||
import '../helpers/test_helpers.dart';
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('LearnLessonViewModel Tests -', () {
|
group('AuthenticationServiceTest -', () {
|
||||||
setUp(() => registerServices());
|
setUp(() => registerServices());
|
||||||
tearDown(() => locator.reset());
|
tearDown(() => locator.reset());
|
||||||
});
|
});
|
||||||
11
test/services/dio_service_test.dart
Normal file
11
test/services/dio_service_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('DioServiceTest -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/services/permission_handler_service_test.dart
Normal file
11
test/services/permission_handler_service_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('PermissionHandlerServiceTest -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/services/secure_storage_service_test.dart
Normal file
11
test/services/secure_storage_service_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('SecureStorageServiceTest -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/services/status_checker_service_test.dart
Normal file
11
test/services/status_checker_service_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('StatusCheckerServiceTest -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/account_privacy_viewmodel_test.dart
Normal file
11
test/viewmodels/account_privacy_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('AccountPrivacyViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/assessment_viewmodel_test.dart
Normal file
11
test/viewmodels/assessment_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('AssessmentViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/call_support_viewmodel_test.dart
Normal file
11
test/viewmodels/call_support_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('CallSupportViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/downloads_viewmodel_test.dart
Normal file
11
test/viewmodels/downloads_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('DownloadsViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/full_name_view_viewmodel_test.dart
Normal file
11
test/viewmodels/full_name_view_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('FullNameViewViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
36
test/viewmodels/home_viewmodel_test.dart
Normal file
36
test/viewmodels/home_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:mockito/mockito.dart';
|
||||||
|
import 'package:yimaru_app/app/app.bottomsheets.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_strings.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/home/home_viewmodel.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
HomeViewModel getModel() => HomeViewModel();
|
||||||
|
|
||||||
|
group('HomeViewmodelTest -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
|
||||||
|
group('showBottomSheet -', () {
|
||||||
|
test(
|
||||||
|
'When called, should show custom bottom sheet using notice variant',
|
||||||
|
() {
|
||||||
|
final bottomSheetService = getAndRegisterBottomSheetService();
|
||||||
|
|
||||||
|
final model = getModel();
|
||||||
|
model.showBottomSheet();
|
||||||
|
verify(
|
||||||
|
bottomSheetService.showCustomSheet(
|
||||||
|
variant: BottomSheetType.notice,
|
||||||
|
title: ksHomeBottomSheetTitle,
|
||||||
|
description: ksHomeBottomSheetDescription,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/language_viewmodel_test.dart
Normal file
11
test/viewmodels/language_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('LanguageViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/learn_level_viewmodel_test.dart
Normal file
11
test/viewmodels/learn_level_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('LearnLevelViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/learn_module_viewmodel_test.dart
Normal file
11
test/viewmodels/learn_module_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('LearnModuleViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/learn_viewmodel_test.dart
Normal file
11
test/viewmodels/learn_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('LearnViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/login_viewmodel_test.dart
Normal file
11
test/viewmodels/login_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('LoginViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/onboarding_viewmodel_test.dart
Normal file
11
test/viewmodels/onboarding_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('OnboardingViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/ongoing_progress_viewmodel_test.dart
Normal file
11
test/viewmodels/ongoing_progress_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('OngoingProgressViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/privacy_policy_viewmodel_test.dart
Normal file
11
test/viewmodels/privacy_policy_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('PrivacyPolicyViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/profile_detail_viewmodel_test.dart
Normal file
11
test/viewmodels/profile_detail_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('ProfileDetailViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/profile_viewmodel_test.dart
Normal file
11
test/viewmodels/profile_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('ProfileViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/progress_viewmodel_test.dart
Normal file
11
test/viewmodels/progress_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('ProgressViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/register_viewmodel_test.dart
Normal file
11
test/viewmodels/register_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('RegisterViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/settings_viewmodel_test.dart
Normal file
11
test/viewmodels/settings_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('SettingsViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/startup_viewmodel_test.dart
Normal file
11
test/viewmodels/startup_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('StartupViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/support_viewmodel_test.dart
Normal file
11
test/viewmodels/support_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('SupportViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/telegram_support_viewmodel_test.dart
Normal file
11
test/viewmodels/telegram_support_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('TelegramSupportViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/terms_and_conditions_viewmodel_test.dart
Normal file
11
test/viewmodels/terms_and_conditions_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('TermsAndConditionsViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/viewmodels/welcome_viewmodel_test.dart
Normal file
11
test/viewmodels/welcome_viewmodel_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:yimaru_app/app/app.locator.dart';
|
||||||
|
|
||||||
|
import '../helpers/test_helpers.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('WelcomeViewModel Tests -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -8,13 +8,19 @@
|
||||||
|
|
||||||
#include <battery_plus/battery_plus_windows_plugin.h>
|
#include <battery_plus/battery_plus_windows_plugin.h>
|
||||||
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||||
|
#include <file_selector_windows/file_selector_windows.h>
|
||||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
|
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
BatteryPlusWindowsPluginRegisterWithRegistrar(
|
BatteryPlusWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("BatteryPlusWindowsPlugin"));
|
registry->GetRegistrarForPlugin("BatteryPlusWindowsPlugin"));
|
||||||
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
||||||
|
FileSelectorWindowsRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
|
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
battery_plus
|
battery_plus
|
||||||
connectivity_plus
|
connectivity_plus
|
||||||
|
file_selector_windows
|
||||||
flutter_secure_storage_windows
|
flutter_secure_storage_windows
|
||||||
|
permission_handler_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user