Merge branch 'release/1.0.3'
-fix(learn): Integrate learn lessons according to the new hierarchy.
This commit is contained in:
commit
ee981ceba9
File diff suppressed because one or more lines are too long
|
|
@ -52,6 +52,7 @@ import 'package:yimaru_app/services/audio_player_service.dart';
|
|||
import 'package:yimaru_app/services/voice_recorder_service.dart';
|
||||
import 'package:yimaru_app/ui/views/course_practice_question/course_practice_question_view.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_subcategory/learn_subcategory_view.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_submodule/learn_submodule_view.dart';
|
||||
// @stacked-import
|
||||
|
||||
@StackedApp(
|
||||
|
|
@ -92,6 +93,7 @@ import 'package:yimaru_app/ui/views/learn_subcategory/learn_subcategory_view.dar
|
|||
MaterialRoute(page: CourseView),
|
||||
MaterialRoute(page: CoursePracticeQuestionView),
|
||||
MaterialRoute(page: LearnSubcategoryView),
|
||||
MaterialRoute(page: LearnSubmoduleView),
|
||||
// @stacked-route
|
||||
],
|
||||
dependencies: [
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
52
lib/models/lesson.dart
Normal file
52
lib/models/lesson.dart
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'lesson.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class Lesson {
|
||||
final int? id;
|
||||
|
||||
final String? title;
|
||||
|
||||
final String? thumbnail;
|
||||
|
||||
final String? description;
|
||||
|
||||
@JsonKey(name: 'is_active')
|
||||
final bool? isActive;
|
||||
|
||||
@JsonKey(name: 'sub_module_id')
|
||||
final int? subModuleId;
|
||||
|
||||
@JsonKey(name: 'teaching_text')
|
||||
final String? teachingText;
|
||||
|
||||
@JsonKey(name: 'display_order')
|
||||
final int? displayOrder;
|
||||
|
||||
@JsonKey(name: 'teaching_video_url')
|
||||
final String? teachingVideoUrl;
|
||||
|
||||
@JsonKey(name: 'teaching_image_url')
|
||||
final String? teachingImageUrl;
|
||||
|
||||
@JsonKey(name: 'teaching_audio_url')
|
||||
final String? teachingAudioUrl;
|
||||
|
||||
const Lesson(
|
||||
{this.id,
|
||||
this.title,
|
||||
this.isActive,
|
||||
this.thumbnail,
|
||||
this.subModuleId,
|
||||
this.description,
|
||||
this.teachingText,
|
||||
this.displayOrder,
|
||||
this.teachingAudioUrl,
|
||||
this.teachingImageUrl,
|
||||
this.teachingVideoUrl});
|
||||
|
||||
factory Lesson.fromJson(Map<String, dynamic> json) => _$LessonFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$LessonToJson(this);
|
||||
}
|
||||
35
lib/models/lesson.g.dart
Normal file
35
lib/models/lesson.g.dart
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'lesson.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
Lesson _$LessonFromJson(Map<String, dynamic> json) => Lesson(
|
||||
id: (json['id'] as num?)?.toInt(),
|
||||
title: json['title'] as String?,
|
||||
isActive: json['is_active'] as bool?,
|
||||
thumbnail: json['thumbnail'] as String?,
|
||||
subModuleId: (json['sub_module_id'] as num?)?.toInt(),
|
||||
description: json['description'] as String?,
|
||||
teachingText: json['teaching_text'] as String?,
|
||||
displayOrder: (json['display_order'] as num?)?.toInt(),
|
||||
teachingAudioUrl: json['teaching_audio_url'] as String?,
|
||||
teachingImageUrl: json['teaching_image_url'] as String?,
|
||||
teachingVideoUrl: json['teaching_video_url'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$LessonToJson(Lesson instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'title': instance.title,
|
||||
'thumbnail': instance.thumbnail,
|
||||
'description': instance.description,
|
||||
'is_active': instance.isActive,
|
||||
'sub_module_id': instance.subModuleId,
|
||||
'teaching_text': instance.teachingText,
|
||||
'display_order': instance.displayOrder,
|
||||
'teaching_video_url': instance.teachingVideoUrl,
|
||||
'teaching_image_url': instance.teachingImageUrl,
|
||||
'teaching_audio_url': instance.teachingAudioUrl,
|
||||
};
|
||||
44
lib/models/submodule.dart
Normal file
44
lib/models/submodule.dart
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'submodule.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class Submodule {
|
||||
final int? id;
|
||||
|
||||
final String? tips;
|
||||
|
||||
final String? title;
|
||||
|
||||
final String? thumbnail;
|
||||
|
||||
final String? description;
|
||||
|
||||
@JsonKey(name: 'module_id')
|
||||
final int? moduleId;
|
||||
|
||||
@JsonKey(name: 'is_active')
|
||||
final bool? isActive;
|
||||
|
||||
@JsonKey(name: 'display_order')
|
||||
final int? displayOrder;
|
||||
|
||||
@JsonKey(name: 'legacy_sub_course_id')
|
||||
final int? legacySubCourseId;
|
||||
|
||||
const Submodule(
|
||||
{this.id,
|
||||
this.title,
|
||||
this.tips,
|
||||
this.moduleId,
|
||||
this.isActive,
|
||||
this.thumbnail,
|
||||
this.description,
|
||||
this.displayOrder,
|
||||
this.legacySubCourseId});
|
||||
|
||||
factory Submodule.fromJson(Map<String, dynamic> json) =>
|
||||
_$SubmoduleFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$SubmoduleToJson(this);
|
||||
}
|
||||
31
lib/models/submodule.g.dart
Normal file
31
lib/models/submodule.g.dart
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'submodule.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
Submodule _$SubmoduleFromJson(Map<String, dynamic> json) => Submodule(
|
||||
id: (json['id'] as num?)?.toInt(),
|
||||
title: json['title'] as String?,
|
||||
tips: json['tips'] as String?,
|
||||
moduleId: (json['module_id'] as num?)?.toInt(),
|
||||
isActive: json['is_active'] as bool?,
|
||||
thumbnail: json['thumbnail'] as String?,
|
||||
description: json['description'] as String?,
|
||||
displayOrder: (json['display_order'] as num?)?.toInt(),
|
||||
legacySubCourseId: (json['legacy_sub_course_id'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$SubmoduleToJson(Submodule instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'tips': instance.tips,
|
||||
'title': instance.title,
|
||||
'thumbnail': instance.thumbnail,
|
||||
'description': instance.description,
|
||||
'module_id': instance.moduleId,
|
||||
'is_active': instance.isActive,
|
||||
'display_order': instance.displayOrder,
|
||||
'legacy_sub_course_id': instance.legacySubCourseId,
|
||||
};
|
||||
|
|
@ -13,7 +13,9 @@ import 'package:yimaru_app/services/dio_service.dart';
|
|||
import 'package:yimaru_app/ui/common/app_constants.dart';
|
||||
|
||||
import '../app/app.locator.dart';
|
||||
import '../models/lesson.dart';
|
||||
import '../models/module.dart';
|
||||
import '../models/submodule.dart';
|
||||
import '../ui/common/enmus.dart';
|
||||
|
||||
class ApiService {
|
||||
|
|
@ -674,4 +676,52 @@ class ApiService {
|
|||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get submodules
|
||||
Future<List<Submodule>> getSubmodules(int id) async {
|
||||
try {
|
||||
List<Submodule> submodules = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/api/$kApiVersionUrl/$kCourseManagementUrl/$kModulesUrl/$id/$kSubmodulesUrl');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['sub_modules'] as List;
|
||||
submodules = decodedData.map(
|
||||
(e) {
|
||||
return Submodule.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return submodules;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get lessons
|
||||
Future<List<Lesson>> getLessons(int id) async {
|
||||
try {
|
||||
List<Lesson> lessons = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/api/$kApiVersionUrl/$kCourseManagementUrl/$kSubmodulesUrl/$id/$kLessonsUrl');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data'] as List;
|
||||
lessons = decodedData.map(
|
||||
(e) {
|
||||
return Lesson.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return lessons;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ String kCoursesUrl = 'courses';
|
|||
|
||||
String kModulesUrl = 'modules';
|
||||
|
||||
String kLessonsUrl = 'lessons';
|
||||
|
||||
String kRegisterUrl = 'register';
|
||||
|
||||
String kCategoryUrl = 'categories';
|
||||
|
|
@ -24,6 +26,8 @@ String kResendOtpUrl = 'resend-otp';
|
|||
|
||||
String kGetUserUrl = 'user-profile';
|
||||
|
||||
String kSubmodulesUrl = 'sub-modules';
|
||||
|
||||
String kSubcoursesUrl = 'sub-courses';
|
||||
|
||||
String kCompleteLessonUrl = 'complete';
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ enum StateObjects {
|
|||
verifyOtp,
|
||||
resendOtp,
|
||||
learnLevels,
|
||||
learnLessons,
|
||||
learnModules,
|
||||
learnCourses,
|
||||
profileImage,
|
||||
|
|
@ -40,6 +41,7 @@ enum StateObjects {
|
|||
loginWithGoogle,
|
||||
loadLessonVideo,
|
||||
loadCourseVideo,
|
||||
learnSubmodules,
|
||||
requestResetCode,
|
||||
courseCategories,
|
||||
profileCompletion,
|
||||
|
|
|
|||
|
|
@ -41,8 +41,7 @@ class CourseCategoryViewModel extends ReactiveViewModel {
|
|||
// Remote api call
|
||||
|
||||
// Course categories
|
||||
Future<void> getCategories() async =>
|
||||
await runBusyFuture(_getCategories(),
|
||||
Future<void> getCategories() async => await runBusyFuture(_getCategories(),
|
||||
busyObject: StateObjects.courseCategories);
|
||||
|
||||
Future<void> _getCategories() async {
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ class CourseLessonDetailViewModel extends BaseViewModel {
|
|||
busyObject: StateObjects.loadCourseVideo);
|
||||
|
||||
Future<void> _initializePlayer(CourseLesson lesson) async {
|
||||
print('URL: $kSampleVideoUrl');
|
||||
_videoPlayerController =
|
||||
VideoPlayerController.networkUrl(Uri.parse(kSampleVideoUrl));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,31 +1,30 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/submodule.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
import 'package:yimaru_app/ui/widgets/learn_lesson_tile.dart';
|
||||
import 'package:yimaru_app/ui/widgets/module_progress.dart';
|
||||
import 'package:yimaru_app/ui/widgets/motivation_card.dart';
|
||||
|
||||
import '../../../models/lesson.dart';
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
import '../../widgets/custom_elevated_button.dart';
|
||||
import '../../widgets/custom_circular_progress_indicator.dart';
|
||||
import '../../widgets/small_app_bar.dart';
|
||||
import 'learn_lesson_viewmodel.dart';
|
||||
|
||||
class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
||||
final String title;
|
||||
final String topics;
|
||||
final String subtitle;
|
||||
final String description;
|
||||
final List<Map<String, dynamic>> practices;
|
||||
final Submodule submodule;
|
||||
|
||||
const LearnLessonView({Key? key, required this.submodule}) : super(key: key);
|
||||
|
||||
|
||||
@override
|
||||
void onViewModelReady(LearnLessonViewModel viewModel) async {
|
||||
await viewModel.getLessons(submodule.id ?? 0);
|
||||
super.onViewModelReady(viewModel);
|
||||
}
|
||||
|
||||
const LearnLessonView(
|
||||
{Key? key,
|
||||
required this.title,
|
||||
required this.topics,
|
||||
required this.subtitle,
|
||||
required this.practices,
|
||||
required this.description})
|
||||
: super(key: key);
|
||||
|
||||
Widget getPadding(context) {
|
||||
double half = screenHeight(context) / 2;
|
||||
|
|
@ -117,95 +116,63 @@ class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
|||
verticalSpaceTiny,
|
||||
_buildSubtitle(),
|
||||
verticalSpaceSmall,
|
||||
_buildTopics(),
|
||||
verticalSpaceSmall,
|
||||
// _buildModuleProgress(),
|
||||
// verticalSpaceMedium,
|
||||
// _buildContinueButton(),
|
||||
// verticalSpaceMedium,
|
||||
// _buildMotivationCard(),
|
||||
// verticalSpaceMedium,
|
||||
//_buildHeader(),
|
||||
//verticalSpaceMedium,
|
||||
// _buildListView(viewModel),
|
||||
getPadding(context),
|
||||
_buildStartButton(viewModel),
|
||||
verticalSpaceSmall,
|
||||
_buildPracticeButton(viewModel)
|
||||
_buildModuleProgress(),
|
||||
verticalSpaceMedium,
|
||||
verticalSpaceMedium,
|
||||
_buildMotivationCard(),
|
||||
verticalSpaceMedium,
|
||||
_buildHeader(),
|
||||
verticalSpaceMedium,
|
||||
_buildListViewBuilder(viewModel),
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
title,
|
||||
submodule.title ?? '',
|
||||
style: style16DG600,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
subtitle,
|
||||
submodule.description ?? '',
|
||||
style: style14DG600,
|
||||
);
|
||||
|
||||
Widget _buildTopics() => Text(
|
||||
topics,
|
||||
style: style14DG500,
|
||||
);
|
||||
|
||||
Widget _buildModuleProgress() => const ModuleProgress();
|
||||
|
||||
Widget _buildStartButton(LearnLessonViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
borderRadius: 12,
|
||||
text: 'Start $title',
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
onTap: () async => await viewModel.navigateToLearnLessonDetail(
|
||||
title: title, practices: practices, description: description),
|
||||
);
|
||||
|
||||
Widget _buildPracticeButton(LearnLessonViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
borderRadius: 12,
|
||||
text: 'Practice',
|
||||
backgroundColor: kcWhite,
|
||||
borderColor: kcPrimaryColor,
|
||||
foregroundColor: kcPrimaryColor,
|
||||
onTap: () async =>
|
||||
await viewModel.navigateToLearnPractice(practices));
|
||||
|
||||
Widget _buildMotivationCard() => const MotivationCard();
|
||||
|
||||
Widget _buildHeader() => Text(
|
||||
title,
|
||||
'Lessons in this module',
|
||||
style: style18DG700,
|
||||
);
|
||||
|
||||
|
||||
Widget _buildListViewBuilder(LearnLessonViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.learnLessons)
|
||||
? _buildProgressIndicator()
|
||||
: _buildListView(viewModel);
|
||||
|
||||
Widget _buildProgressIndicator() => const Center(
|
||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||
);
|
||||
|
||||
|
||||
Widget _buildListView(LearnLessonViewModel viewModel) => ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.lessons.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildTile(
|
||||
title: viewModel.lessons[index]['title'],
|
||||
status: viewModel.lessons[index]['status'],
|
||||
thumbnail: viewModel.lessons[index]['thumbnail'],
|
||||
onLessonTap: () async => await viewModel.navigateToLearnLessonDetail(
|
||||
title: title, practices: practices, description: description),
|
||||
// onPracticeTap: () async => await viewModel.navigateToLearnPractice(),
|
||||
lesson: viewModel.lessons[index],
|
||||
onLessonTap: () async => await viewModel.navigateToLearnLessonDetail(viewModel.lessons[index]),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildTile({
|
||||
required String title,
|
||||
required String thumbnail,
|
||||
GestureTapCallback? onLessonTap,
|
||||
required ProgressStatuses status,
|
||||
GestureTapCallback? onPracticeTap,
|
||||
required Lesson lesson,
|
||||
required GestureTapCallback? onLessonTap,
|
||||
}) =>
|
||||
LearnLessonTile(
|
||||
title: title,
|
||||
status: status,
|
||||
thumbnail: thumbnail,
|
||||
lesson: lesson,
|
||||
onLessonTap: onLessonTap,
|
||||
onPracticeTap: onPracticeTap,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,47 +4,42 @@ import 'package:yimaru_app/app/app.router.dart';
|
|||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../models/lesson.dart';
|
||||
import '../../../services/api_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
|
||||
class LearnLessonViewModel extends BaseViewModel {
|
||||
// Dependency injection
|
||||
final _apiService = locator<ApiService>();
|
||||
|
||||
final _statusChecker = locator<StatusCheckerService>();
|
||||
|
||||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
// Lessons
|
||||
final List<Map<String, dynamic>> _lessons = [
|
||||
{
|
||||
'title': '1.1 Introducing Yourself',
|
||||
'status': ProgressStatuses.completed,
|
||||
'thumbnail': 'assets/images/image_1.png',
|
||||
},
|
||||
{
|
||||
'status': ProgressStatuses.completed,
|
||||
'thumbnail': 'assets/images/image_1.png',
|
||||
'title': '1.2 Talking About Your Surroundings',
|
||||
},
|
||||
{
|
||||
'status': ProgressStatuses.pending,
|
||||
'title': '1.1 Introducing Yourself',
|
||||
'thumbnail': 'assets/images/image_1.png',
|
||||
},
|
||||
];
|
||||
// Learn lessons
|
||||
List<Lesson> _lessons = [];
|
||||
|
||||
List<Map<String, dynamic>> get lessons => _lessons;
|
||||
List<Lesson> get lessons => _lessons;
|
||||
|
||||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
Future<void> navigateToLearnLessonDetail(
|
||||
{required String title,
|
||||
required List<Map<String, dynamic>> practices,
|
||||
required String description}) async =>
|
||||
await _navigationService.navigateToLearnLessonDetailView(
|
||||
title: title, practices: practices, description: description);
|
||||
Future<void> navigateToLearnLessonDetail(Lesson lesson) async =>
|
||||
await _navigationService.navigateToLearnLessonDetailView(lesson: lesson);
|
||||
|
||||
Future<void> navigateToLearnPractice(
|
||||
List<Map<String, dynamic>> practices) async =>
|
||||
await _navigationService.navigateToLearnPracticeView(
|
||||
practices: practices,
|
||||
title: 'Let’s Practice',
|
||||
buttonLabel: 'Begin Lesson Practice',
|
||||
subtitle: 'Let’s quickly review what you’ve learned in this lesson!',
|
||||
);
|
||||
// Remote api call
|
||||
|
||||
// Learn modules
|
||||
Future<void> getLessons(int id) async => await runBusyFuture(_getLessons(id),
|
||||
busyObject: StateObjects.learnLessons);
|
||||
|
||||
Future<void> _getLessons(int id) async {
|
||||
if (_lessons.isEmpty) {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
_lessons = await _apiService.getLessons(id);
|
||||
_lessons.sort(
|
||||
(a, b) => (a.displayOrder ?? 0).compareTo(b.displayOrder ?? 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import 'package:chewie/chewie.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:vimeo_video_player/vimeo_video_player.dart';
|
||||
import 'package:yimaru_app/ui/widgets/empty_video_player.dart';
|
||||
|
||||
import '../../../models/lesson.dart';
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../common/enmus.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
|
|
@ -11,20 +12,14 @@ import '../../widgets/small_app_bar.dart';
|
|||
import 'learn_lesson_detail_viewmodel.dart';
|
||||
|
||||
class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
||||
final String title;
|
||||
final String description;
|
||||
final List<Map<String, dynamic>> practices;
|
||||
final Lesson lesson;
|
||||
|
||||
const LearnLessonDetailView(
|
||||
{Key? key,
|
||||
required this.title,
|
||||
required this.practices,
|
||||
required this.description})
|
||||
const LearnLessonDetailView({Key? key, required this.lesson})
|
||||
: super(key: key);
|
||||
|
||||
Future<void> _navigate(LearnLessonDetailViewModel viewModel) async {
|
||||
await viewModel.pause();
|
||||
await viewModel.navigateToLearnPractice(practices);
|
||||
// await viewModel.navigateToLearnPractice(practices);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -34,12 +29,6 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
|||
super.onDispose(viewModel);
|
||||
}
|
||||
|
||||
@override
|
||||
void onViewModelReady(LearnLessonDetailViewModel viewModel) async {
|
||||
await viewModel.initializePlayer();
|
||||
super.onViewModelReady(viewModel);
|
||||
}
|
||||
|
||||
@override
|
||||
LearnLessonDetailViewModel viewModelBuilder(BuildContext context) =>
|
||||
LearnLessonDetailViewModel();
|
||||
|
|
@ -125,7 +114,7 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
|||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
title,
|
||||
lesson.title ?? '',
|
||||
style: style16DG600,
|
||||
);
|
||||
|
||||
|
|
@ -134,21 +123,21 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
|||
height: 200,
|
||||
color: kcBlack,
|
||||
width: double.maxFinite,
|
||||
child: _buildVideoPlayerState(viewModel),
|
||||
child: _buildVideoPlayer(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildVideoPlayerState(LearnLessonDetailViewModel viewModel) =>
|
||||
viewModel.chewieController != null &&
|
||||
viewModel.videoPlayerController != null &&
|
||||
!viewModel.busy(StateObjects.loadLessonVideo)
|
||||
? _buildVideoPlayer(viewModel)
|
||||
: _buildEmptyVideoPlayer();
|
||||
|
||||
|
||||
Widget _buildVideoPlayer(LearnLessonDetailViewModel viewModel) =>
|
||||
_buildChewiePlayer(viewModel);
|
||||
_buildVimeoPlayer(viewModel);
|
||||
|
||||
Widget _buildChewiePlayer(LearnLessonDetailViewModel viewModel) =>
|
||||
Chewie(controller: viewModel.chewieController!);
|
||||
Widget _buildVimeoPlayer(LearnLessonDetailViewModel viewModel) =>
|
||||
VimeoVideoPlayer(
|
||||
isAutoPlay: true,
|
||||
onInAppWebViewCreated: (controller) =>
|
||||
viewModel.initializePlayer(controller),
|
||||
videoId: lesson.teachingVideoUrl?.split('/').last ?? '',
|
||||
);
|
||||
|
||||
Widget _buildEmptyVideoPlayer() => const EmptyVideoPlayer();
|
||||
|
||||
|
|
@ -158,7 +147,7 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
|||
);
|
||||
|
||||
Widget _buildDescription() => Text(
|
||||
description,
|
||||
lesson.description ?? '',
|
||||
style: style14DG600,
|
||||
);
|
||||
|
||||
|
|
@ -175,7 +164,7 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
|||
Widget _buildContinueButton(LearnLessonDetailViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Practice',
|
||||
text: 'Lessons',
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import 'package:chewie/chewie.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
import 'package:yimaru_app/app/app.router.dart';
|
||||
import 'package:yimaru_app/ui/common/app_constants.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
|
|
@ -20,6 +19,10 @@ class LearnLessonDetailViewModel extends BaseViewModel {
|
|||
|
||||
ChewieController? get chewieController => _chewieController;
|
||||
|
||||
InAppWebViewController? _webViewController;
|
||||
|
||||
InAppWebViewController? get webViewController => _webViewController;
|
||||
|
||||
VideoPlayerController? _videoPlayerController;
|
||||
|
||||
VideoPlayerController? get videoPlayerController => _videoPlayerController;
|
||||
|
|
@ -34,32 +37,22 @@ class LearnLessonDetailViewModel extends BaseViewModel {
|
|||
await _chewieController?.pause();
|
||||
}
|
||||
|
||||
Future<void> initializePlayer() async =>
|
||||
await runBusyFuture(_initializePlayer(),
|
||||
busyObject: StateObjects.loadLessonVideo);
|
||||
|
||||
Future<void> _initializePlayer() async {
|
||||
_videoPlayerController =
|
||||
VideoPlayerController.networkUrl(Uri.parse(kSampleVideoUrl));
|
||||
|
||||
await _videoPlayerController?.initialize();
|
||||
|
||||
if (_videoPlayerController != null) {
|
||||
_chewieController = ChewieController(
|
||||
looping: true,
|
||||
autoPlay: true,
|
||||
showOptions: true,
|
||||
showControls: true,
|
||||
aspectRatio: 16 / 9,
|
||||
autoInitialize: true,
|
||||
allowedScreenSleep: false,
|
||||
videoPlayerController: _videoPlayerController!,
|
||||
materialProgressColors: buildChewieProgressIndicator);
|
||||
void initializePlayer(InAppWebViewController controller){
|
||||
_webViewController = controller;
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
// rebuildUi();
|
||||
void onLoadVideoStart() {
|
||||
setBusyForObject(StateObjects.loadLessonVideo, true);
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
void onLoadVideoComplete() {
|
||||
setBusyForObject(StateObjects.loadLessonVideo, false);
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
|
||||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ class LearnLevelView extends StackedView<LearnLevelViewModel> {
|
|||
);
|
||||
|
||||
Widget _buildAppBar(LearnLevelViewModel viewModel) => SmallAppBar(
|
||||
title: 'Levels',
|
||||
onTap: viewModel.pop,
|
||||
showBackButton: true,
|
||||
);
|
||||
|
|
@ -82,7 +83,8 @@ class LearnLevelView extends StackedView<LearnLevelViewModel> {
|
|||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildTile(
|
||||
level: viewModel.levels[index],
|
||||
onTap: () async => await viewModel.navigateToModule( viewModel.levels[index]),
|
||||
onTap: () async =>
|
||||
await viewModel.navigateToModule(viewModel.levels[index]),
|
||||
),
|
||||
separatorBuilder: (context, index) => verticalSpaceSmall,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/level.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
import 'package:yimaru_app/ui/widgets/learn_module_tile.dart';
|
||||
import 'package:yimaru_app/ui/widgets/overall_learn_progress.dart';
|
||||
|
||||
import '../../../models/module.dart';
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../common/enmus.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
import '../../widgets/custom_circular_progress_indicator.dart';
|
||||
import '../../widgets/small_app_bar.dart';
|
||||
import 'learn_module_viewmodel.dart';
|
||||
|
||||
|
|
@ -57,6 +58,7 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
|
|||
);
|
||||
|
||||
Widget _buildAppBar(LearnModuleViewModel viewModel) => SmallAppBar(
|
||||
title: 'Modules',
|
||||
onTap: viewModel.pop,
|
||||
showBackButton: true,
|
||||
);
|
||||
|
|
@ -79,10 +81,10 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
|
|||
verticalSpaceMedium,
|
||||
_buildTitle(),
|
||||
_buildSubtitle(),
|
||||
verticalSpaceMedium,
|
||||
verticalSpaceLarge,
|
||||
_buildOverallProgress(),
|
||||
verticalSpaceMedium,
|
||||
_buildListView(viewModel)
|
||||
_buildListViewBuilder(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
|
|
@ -95,7 +97,18 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
|
|||
style: style14DG400,
|
||||
);
|
||||
|
||||
Widget _buildOverallProgress() => const OverallLearnProgress();
|
||||
Widget _buildOverallProgress() => OverallLearnProgress(
|
||||
color: kcPrimaryColor.withOpacity(0.1),
|
||||
);
|
||||
|
||||
Widget _buildListViewBuilder(LearnModuleViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.learnModules)
|
||||
? _buildProgressIndicator()
|
||||
: _buildListView(viewModel);
|
||||
|
||||
Widget _buildProgressIndicator() => const Center(
|
||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||
);
|
||||
|
||||
Widget _buildListView(LearnModuleViewModel viewModel) => ListView.builder(
|
||||
shrinkWrap: true,
|
||||
|
|
@ -103,18 +116,17 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
|
|||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildTile(
|
||||
module: viewModel.modules[index],
|
||||
onLessonTap: () {},
|
||||
onPracticeTap: () {}),
|
||||
onModuleTap: () async => await viewModel
|
||||
.navigateToLearnSubmodule(viewModel.modules[index]),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildTile({
|
||||
required Module module,
|
||||
required GestureTapCallback onLessonTap,
|
||||
required GestureTapCallback onPracticeTap,
|
||||
required GestureTapCallback onModuleTap,
|
||||
}) =>
|
||||
LearnModuleTile(
|
||||
module: module,
|
||||
onLessonTap: onLessonTap,
|
||||
onPracticeTap: onPracticeTap,
|
||||
onModuleTap: onModuleTap,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,27 +24,8 @@ class LearnModuleViewModel extends BaseViewModel {
|
|||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
Future<void> navigateToLearnLesson(
|
||||
{required String title,
|
||||
required String topics,
|
||||
required String subtitle,
|
||||
required String description,
|
||||
required List<Map<String, dynamic>> practices}) async =>
|
||||
await _navigationService.navigateToLearnLessonView(
|
||||
title: title,
|
||||
topics: topics,
|
||||
subtitle: subtitle,
|
||||
practices: practices,
|
||||
description: description);
|
||||
|
||||
Future<void> navigateToLearnPractice(
|
||||
List<Map<String, dynamic>> practices) async =>
|
||||
await _navigationService.navigateToLearnPracticeView(
|
||||
practices: practices,
|
||||
title: 'Let’s Practice',
|
||||
buttonLabel: 'Begin Lesson Practice',
|
||||
subtitle: 'Let’s quickly review what you’ve learned in this lesson!',
|
||||
);
|
||||
Future<void> navigateToLearnSubmodule(Module module) async =>
|
||||
await _navigationService.navigateToLearnSubmoduleView(module: module);
|
||||
|
||||
// Remote api call
|
||||
|
||||
|
|
|
|||
146
lib/ui/views/learn_submodule/learn_submodule_view.dart
Normal file
146
lib/ui/views/learn_submodule/learn_submodule_view.dart
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/module.dart';
|
||||
import 'package:yimaru_app/models/submodule.dart';
|
||||
import 'package:yimaru_app/ui/widgets/course_module_banner.dart';
|
||||
import 'package:yimaru_app/ui/widgets/learn_submodule_tile.dart';
|
||||
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../common/enmus.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
import '../../widgets/custom_circular_progress_indicator.dart';
|
||||
import '../../widgets/custom_elevated_button.dart';
|
||||
import '../../widgets/overall_learn_progress.dart';
|
||||
import '../../widgets/small_app_bar.dart';
|
||||
import 'learn_submodule_viewmodel.dart';
|
||||
|
||||
class LearnSubmoduleView extends StackedView<LearnSubmoduleViewModel> {
|
||||
final Module module;
|
||||
|
||||
@override
|
||||
void onViewModelReady(LearnSubmoduleViewModel viewModel) async {
|
||||
await viewModel.getSubmodules(module.id ?? 0);
|
||||
super.onViewModelReady(viewModel);
|
||||
}
|
||||
|
||||
const LearnSubmoduleView({Key? key, required this.module}) : super(key: key);
|
||||
|
||||
@override
|
||||
LearnSubmoduleViewModel viewModelBuilder(BuildContext context) =>
|
||||
LearnSubmoduleViewModel();
|
||||
|
||||
@override
|
||||
Widget builder(
|
||||
BuildContext context,
|
||||
LearnSubmoduleViewModel viewModel,
|
||||
Widget? child,
|
||||
) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(LearnSubmoduleViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(LearnSubmoduleViewModel viewModel) =>
|
||||
SafeArea(child: _buildBody(viewModel));
|
||||
|
||||
Widget _buildBody(LearnSubmoduleViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildColumn(LearnSubmoduleViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildModulesColumnWrapper(viewModel),
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(LearnSubmoduleViewModel viewModel) => SmallAppBar(
|
||||
title: 'Submodules',
|
||||
onTap: viewModel.pop,
|
||||
showBackButton: true,
|
||||
);
|
||||
|
||||
Widget _buildModulesColumnWrapper(LearnSubmoduleViewModel viewModel) =>
|
||||
Expanded(child: _buildLevelsColumnScrollView(viewModel));
|
||||
|
||||
Widget _buildLevelsColumnScrollView(LearnSubmoduleViewModel viewModel) =>
|
||||
SingleChildScrollView(
|
||||
child: _buildLevelsColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildLevelsColumn(LearnSubmoduleViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: _buildLevelsColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildLevelsColumnChildren(LearnSubmoduleViewModel viewModel) =>
|
||||
[
|
||||
verticalSpaceMedium,
|
||||
_buildTitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildCourseModuleBanner(),
|
||||
verticalSpaceMedium,
|
||||
_buildOverallProgress(),
|
||||
verticalSpaceTiny,
|
||||
_buildContinueButton(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildListViewBuilder(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
module.title ?? '',
|
||||
style: style18P600,
|
||||
);
|
||||
|
||||
Widget _buildCourseModuleBanner() => const CourseModuleBanner();
|
||||
|
||||
Widget _buildOverallProgress() => const OverallLearnProgress(
|
||||
color: Colors.transparent,
|
||||
);
|
||||
|
||||
Widget _buildContinueButton(LearnSubmoduleViewModel viewModel) =>
|
||||
const CustomElevatedButton(
|
||||
height: 55,
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
text: 'Continue Submodule',
|
||||
backgroundColor: kcPrimaryColor);
|
||||
|
||||
Widget _buildListViewBuilder(LearnSubmoduleViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.learnSubmodules)
|
||||
? _buildProgressIndicator()
|
||||
: _buildListView(viewModel);
|
||||
|
||||
Widget _buildProgressIndicator() => const Center(
|
||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||
);
|
||||
|
||||
Widget _buildListView(LearnSubmoduleViewModel viewModel) => ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.submodules.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildTile(
|
||||
submodule: viewModel.submodules[index],
|
||||
onPracticeTap: () {},
|
||||
onLessonTap: () async => await viewModel
|
||||
.navigateToLearnLessons(viewModel.submodules[index]),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildTile({
|
||||
required Submodule submodule,
|
||||
required GestureTapCallback onLessonTap,
|
||||
required GestureTapCallback onPracticeTap,
|
||||
}) =>
|
||||
LearnSubmoduleTile(
|
||||
submodule: submodule,
|
||||
onLessonTap: onLessonTap,
|
||||
onPracticeTap: onPracticeTap,
|
||||
);
|
||||
}
|
||||
46
lib/ui/views/learn_submodule/learn_submodule_viewmodel.dart
Normal file
46
lib/ui/views/learn_submodule/learn_submodule_viewmodel.dart
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:yimaru_app/app/app.router.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../models/submodule.dart';
|
||||
import '../../../services/api_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
import '../../common/enmus.dart';
|
||||
|
||||
class LearnSubmoduleViewModel extends BaseViewModel {
|
||||
// Dependency injection
|
||||
final _apiService = locator<ApiService>();
|
||||
|
||||
final _statusChecker = locator<StatusCheckerService>();
|
||||
|
||||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
// Learn submodule
|
||||
List<Submodule> _submodules = [];
|
||||
|
||||
List<Submodule> get submodules => _submodules;
|
||||
|
||||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
Future<void> navigateToLearnLessons(Submodule submodule) async =>
|
||||
await _navigationService.navigateToLearnLessonView(submodule: submodule);
|
||||
|
||||
// Remote api call
|
||||
|
||||
// Learn modules
|
||||
Future<void> getSubmodules(int id) async =>
|
||||
await runBusyFuture(_getSubmodules(id),
|
||||
busyObject: StateObjects.learnSubmodules);
|
||||
|
||||
Future<void> _getSubmodules(int id) async {
|
||||
if (_submodules.isEmpty) {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
_submodules = await _apiService.getSubmodules(id);
|
||||
_submodules.sort(
|
||||
(a, b) => (a.displayOrder ?? 0).compareTo(b.displayOrder ?? 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ class CourseModuleBanner extends StatelessWidget {
|
|||
Widget build(BuildContext context) => _buildContainer();
|
||||
|
||||
Widget _buildContainer() => Container(
|
||||
height: 150,
|
||||
height: 125,
|
||||
width: double.maxFinite,
|
||||
padding: const EdgeInsets.all(15),
|
||||
decoration: BoxDecoration(
|
||||
|
|
|
|||
|
|
@ -1,27 +1,17 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:yimaru_app/models/lesson.dart';
|
||||
import 'package:yimaru_app/ui/widgets/mini_thumbnail.dart';
|
||||
|
||||
import '../common/app_colors.dart';
|
||||
import '../common/enmus.dart';
|
||||
import '../common/ui_helpers.dart';
|
||||
import 'custom_elevated_button.dart';
|
||||
import 'custom_linear_progress_indicator.dart';
|
||||
|
||||
class LearnLessonTile extends StatelessWidget {
|
||||
final String title;
|
||||
final String thumbnail;
|
||||
final ProgressStatuses status;
|
||||
final Lesson lesson;
|
||||
final GestureTapCallback? onLessonTap;
|
||||
final GestureTapCallback? onPracticeTap;
|
||||
|
||||
const LearnLessonTile({
|
||||
super.key,
|
||||
this.onLessonTap,
|
||||
this.onPracticeTap,
|
||||
required this.title,
|
||||
required this.status,
|
||||
required this.thumbnail,
|
||||
});
|
||||
const LearnLessonTile({super.key, this.onLessonTap, required this.lesson});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildContainer();
|
||||
|
|
@ -32,50 +22,55 @@ class LearnLessonTile extends StatelessWidget {
|
|||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
border: Border.all(
|
||||
color: ProgressStatuses.pending == status
|
||||
? kcPrimaryColor.withOpacity(0.1)
|
||||
: kcGreen.withOpacity(0.1),
|
||||
color: kcPrimaryColor.withOpacity(0.1),
|
||||
// color: ProgressStatuses.pending == status
|
||||
// ? kcPrimaryColor.withOpacity(0.1)
|
||||
// : kcGreen.withOpacity(0.1),
|
||||
),
|
||||
),
|
||||
child: _buildExpansionTile(),
|
||||
);
|
||||
|
||||
Widget _buildExpansionTile() => ExpansionTile(
|
||||
enabled: true,
|
||||
title: _buildTitle(),
|
||||
textColor: kcDarkGrey,
|
||||
showTrailingIcon: true,
|
||||
trailing: _buildIconState(),
|
||||
// subtitle: _buildContent(),
|
||||
initiallyExpanded: true,
|
||||
trailing: _buildPendingIcon(),
|
||||
collapsedIconColor: kcDarkGrey,
|
||||
collapsedTextColor: kcDarkGrey,
|
||||
leading: _buildLeadingWrapper(),
|
||||
shape: Border.all(color: kcTransparent),
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
enabled: status != ProgressStatuses.pending,
|
||||
backgroundColor: kcGreen.withOpacity(0.1),
|
||||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
backgroundColor: ProgressStatuses.pending == status
|
||||
? kcPrimaryColor.withOpacity(0.1)
|
||||
: kcGreen.withOpacity(0.1),
|
||||
childrenPadding: const EdgeInsets.fromLTRB(15, 15, 15, 15),
|
||||
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||
collapsedBackgroundColor: ProgressStatuses.pending == status
|
||||
? kcPrimaryColor.withOpacity(0.1)
|
||||
: kcGreen.withOpacity(0.1),
|
||||
collapsedBackgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||
childrenPadding: const EdgeInsets.fromLTRB(15, 15, 15, 15),
|
||||
tilePadding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
|
||||
initiallyExpanded: status != ProgressStatuses.completed ? true : false,
|
||||
// enabled: status != ProgressStatuses.pending,
|
||||
// backgroundColor: ProgressStatuses.pending == status
|
||||
// ? kcPrimaryColor.withOpacity(0.1)
|
||||
// : kcGreen.withOpacity(0.1),
|
||||
// collapsedBackgroundColor: ProgressStatuses.pending == status
|
||||
// ? kcPrimaryColor.withOpacity(0.1)
|
||||
// : kcGreen.withOpacity(0.1),
|
||||
// initiallyExpanded: status != ProgressStatuses.completed ? true : false,
|
||||
children: _buildExpansionTileChildren(),
|
||||
);
|
||||
|
||||
Widget _buildLeadingWrapper() => MiniThumbnail(thumbnail: thumbnail);
|
||||
Widget _buildLeadingWrapper() =>
|
||||
MiniThumbnail(thumbnail: lesson.thumbnail ?? 'assets/images/image_1.png');
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
title,
|
||||
lesson.title ?? '',
|
||||
style: style16DG600,
|
||||
);
|
||||
|
||||
Widget _buildIconState() => ProgressStatuses.pending == status
|
||||
? _buildPendingIcon()
|
||||
: _buildCompleteIcon();
|
||||
// Widget _buildIconState() => ProgressStatuses.pending == status
|
||||
// ? _buildPendingIcon()
|
||||
// : _buildCompleteIcon();
|
||||
|
||||
Widget _buildCompleteIcon() => const Icon(
|
||||
Icons.check,
|
||||
|
|
@ -98,58 +93,35 @@ class LearnLessonTile extends StatelessWidget {
|
|||
List<Widget> _buildExpansionTileItemChildren() => [
|
||||
_buildProgress(),
|
||||
horizontalSpaceSmall,
|
||||
_buildProgressText(),
|
||||
verticalSpaceSmall,
|
||||
// _buildProgressText(),
|
||||
// verticalSpaceSmall,
|
||||
_buildActionButtonWrapper()
|
||||
];
|
||||
|
||||
Widget _buildProgress() => CustomLinearProgressIndicator(
|
||||
Widget _buildProgress() => const CustomLinearProgressIndicator(
|
||||
progress: 0,
|
||||
activeColor: kcPrimaryColor,
|
||||
backgroundColor: kcVeryLightGrey,
|
||||
progress: ProgressStatuses.completed == status ? 1 : 0.75,
|
||||
);
|
||||
|
||||
Widget _buildProgressText() => Text(
|
||||
ProgressStatuses.completed == status ? 'Completed' : 'In Progress',
|
||||
style: style14P400,
|
||||
);
|
||||
// Widget _buildProgressText() => Text(
|
||||
// ProgressStatuses.completed == status ? 'Completed' : 'In Progress',
|
||||
// style: style14P400,
|
||||
// );
|
||||
|
||||
Widget _buildActionButtonWrapper() => SizedBox(
|
||||
height: 40,
|
||||
child: _buildActionButtons(),
|
||||
);
|
||||
|
||||
Widget _buildActionButtons() => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: _buildActionButtonChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildActionButtonChildren() => [
|
||||
_buildPracticeButton(),
|
||||
horizontalSpaceSmall,
|
||||
_buildLessonButton(),
|
||||
];
|
||||
|
||||
Widget _buildPracticeButton() => CustomElevatedButton(
|
||||
height: 15,
|
||||
width: 135,
|
||||
text: 'Practice',
|
||||
borderRadius: 12,
|
||||
onTap: onPracticeTap,
|
||||
trailingIcon: Icons.mic,
|
||||
backgroundColor: kcWhite,
|
||||
borderColor: kcPrimaryColor,
|
||||
foregroundColor: kcPrimaryColor,
|
||||
height: 50,
|
||||
child: _buildLessonButton(),
|
||||
);
|
||||
|
||||
Widget _buildLessonButton() => CustomElevatedButton(
|
||||
height: 15,
|
||||
width: 135,
|
||||
text: 'Start',
|
||||
borderRadius: 12,
|
||||
onTap: onLessonTap,
|
||||
width: double.maxFinite,
|
||||
foregroundColor: kcWhite,
|
||||
trailingIcon: Icons.play_arrow,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
text: ProgressStatuses.completed == status ? 'View' : 'Continue',
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,17 +6,14 @@ import 'package:yimaru_app/ui/widgets/finish_practice_sheet.dart';
|
|||
|
||||
import '../../models/module.dart';
|
||||
import '../common/app_colors.dart';
|
||||
import '../common/enmus.dart';
|
||||
import '../common/ui_helpers.dart';
|
||||
import 'custom_elevated_button.dart';
|
||||
|
||||
class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
||||
final Module module;
|
||||
final GestureTapCallback? onLessonTap;
|
||||
final GestureTapCallback? onPracticeTap;
|
||||
final GestureTapCallback? onModuleTap;
|
||||
|
||||
const LearnModuleTile(
|
||||
{super.key, this.onLessonTap, this.onPracticeTap, required this.module});
|
||||
const LearnModuleTile({super.key, this.onModuleTap, required this.module});
|
||||
|
||||
Future<void> _showSheet(
|
||||
{required BuildContext context,
|
||||
|
|
@ -158,7 +155,7 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
|||
required LearnModuleViewModel viewModel}) =>
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: _buildActionButtons(context: context, viewModel: viewModel),
|
||||
child: _buildModuleButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildActionButtons(
|
||||
|
|
@ -166,46 +163,39 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
|||
required LearnModuleViewModel viewModel}) =>
|
||||
Row(
|
||||
children: [
|
||||
_buildLessonButtonWrapper(viewModel),
|
||||
_buildModuleButtonWrapper(viewModel),
|
||||
horizontalSpaceSmall,
|
||||
_buildPracticeButtonWrapper(context: context, viewModel: viewModel)
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildLessonButtonWrapper(LearnModuleViewModel viewModel) => Expanded(
|
||||
child: _buildLessonButton(viewModel),
|
||||
Widget _buildModuleButtonWrapper(LearnModuleViewModel viewModel) => Expanded(
|
||||
child: _buildModuleButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildLessonButton(LearnModuleViewModel viewModel) =>
|
||||
Widget _buildModuleButton(LearnModuleViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 15,
|
||||
borderRadius: 12,
|
||||
onTap: onLessonTap,
|
||||
onTap: onModuleTap,
|
||||
text: 'View Module',
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
// onTap: () async => await viewModel.navigateToLearnLesson(
|
||||
// title: title,
|
||||
// topics: topics,
|
||||
// subtitle: subtitle,
|
||||
// practices: practices,
|
||||
// description: description),
|
||||
);
|
||||
|
||||
Widget _buildPracticeButtonWrapper(
|
||||
{required BuildContext context,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
Expanded(
|
||||
child: _buildPracticeButton(context: context, viewModel: viewModel),
|
||||
child: Container(),
|
||||
);
|
||||
|
||||
Widget _buildPracticeButton(
|
||||
{required BuildContext context,
|
||||
required LearnModuleViewModel viewModel}) =>
|
||||
CustomElevatedButton(
|
||||
const CustomElevatedButton(
|
||||
height: 15,
|
||||
borderRadius: 12,
|
||||
onTap: onPracticeTap,
|
||||
text: 'View Practices',
|
||||
backgroundColor: kcWhite,
|
||||
borderColor: kcPrimaryColor,
|
||||
|
|
|
|||
237
lib/ui/widgets/learn_submodule_tile.dart
Normal file
237
lib/ui/widgets/learn_submodule_tile.dart
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/submodule.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_linear_progress_indicator.dart';
|
||||
import 'package:yimaru_app/ui/widgets/finish_practice_sheet.dart';
|
||||
|
||||
import '../common/app_colors.dart';
|
||||
import '../common/ui_helpers.dart';
|
||||
import '../views/learn_submodule/learn_submodule_viewmodel.dart';
|
||||
import 'custom_elevated_button.dart';
|
||||
|
||||
class LearnSubmoduleTile extends ViewModelWidget<LearnSubmoduleViewModel> {
|
||||
final Submodule submodule;
|
||||
final GestureTapCallback? onLessonTap;
|
||||
final GestureTapCallback? onPracticeTap;
|
||||
|
||||
const LearnSubmoduleTile(
|
||||
{super.key,
|
||||
this.onLessonTap,
|
||||
this.onPracticeTap,
|
||||
required this.submodule});
|
||||
|
||||
Future<void> _showSheet(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) async =>
|
||||
await showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: kcTransparent,
|
||||
builder: (_) => _buildSheet(viewModel),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, LearnSubmoduleViewModel viewModel) =>
|
||||
_buildExpansionTileCard(context: context, viewModel: viewModel);
|
||||
|
||||
Widget _buildExpansionTileCard(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
Container(
|
||||
margin: const EdgeInsets.only(bottom: 15),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
border: Border.all(color: kcVeryLightGrey),
|
||||
),
|
||||
child: _buildTileStack(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildTileStack(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
Stack(
|
||||
children: [
|
||||
_buildExpansionTile(context: context, viewModel: viewModel),
|
||||
// _buildContainerShaderState()
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildExpansionTile(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
ExpansionTile(
|
||||
enabled: true,
|
||||
title: _buildTitle(),
|
||||
textColor: kcDarkGrey,
|
||||
showTrailingIcon: true,
|
||||
initiallyExpanded: true,
|
||||
subtitle: _buildContent(),
|
||||
leading: _buildIconWrapper(),
|
||||
collapsedIconColor: kcDarkGrey,
|
||||
collapsedTextColor: kcDarkGrey,
|
||||
backgroundColor: kcBackgroundColor,
|
||||
shape: Border.all(color: kcTransparent),
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
collapsedBackgroundColor: kcBackgroundColor,
|
||||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||
tilePadding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
childrenPadding: const EdgeInsets.fromLTRB(70, 15, 15, 15),
|
||||
// enabled: status != ProgressStatuses.pending,
|
||||
// showTrailingIcon: status != ProgressStatuses.pending ? true : false,
|
||||
//initiallyExpanded: status == ProgressStatuses.started ? true : false,
|
||||
children:
|
||||
_buildExpansionTileChildren(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildIconWrapper() => CircleAvatar(
|
||||
backgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||
child: _buildIcon(),
|
||||
);
|
||||
|
||||
Widget _buildIcon() => const Icon(
|
||||
Icons.lightbulb_outline,
|
||||
color: kcPrimaryColor,
|
||||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
submodule.title ?? '',
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
style: style16P600,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
);
|
||||
|
||||
Widget _buildContent() => Text(
|
||||
submodule.description ?? '',
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
style: style14DG400,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
);
|
||||
|
||||
List<Widget> _buildExpansionTileChildren(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
[_buildExpansionTileItem(context: context, viewModel: viewModel)];
|
||||
|
||||
Widget _buildExpansionTileItem(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildExpansionTileItemChildren(
|
||||
context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildExpansionTileItemChildren(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
[
|
||||
// _buildProgressRow(),
|
||||
// verticalSpaceSmall,
|
||||
_buildActionButtonWrapper(context: context, viewModel: viewModel)
|
||||
];
|
||||
|
||||
Widget _buildProgressRow() => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: _buildProgressChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildProgressChildren() =>
|
||||
[_buildProgressStatusWrapper(), horizontalSpaceSmall, _buildProgress()];
|
||||
|
||||
Widget _buildProgressStatusWrapper() => Expanded(
|
||||
child: _buildProgressStatus(),
|
||||
);
|
||||
|
||||
Widget _buildProgressStatus() => const CustomLinearProgressIndicator(
|
||||
progress: 0.75,
|
||||
activeColor: kcPrimaryColor,
|
||||
backgroundColor: kcVeryLightGrey);
|
||||
|
||||
Widget _buildProgress() => const Text(
|
||||
'2/3',
|
||||
style: TextStyle(color: kcDarkGrey),
|
||||
);
|
||||
|
||||
Widget _buildActionButtonWrapper(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: _buildActionButtons(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildActionButtons(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
Row(
|
||||
children: [
|
||||
_buildLessonButtonWrapper(viewModel),
|
||||
horizontalSpaceSmall,
|
||||
_buildPracticeButtonWrapper(context: context, viewModel: viewModel)
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildLessonButtonWrapper(LearnSubmoduleViewModel viewModel) =>
|
||||
Expanded(
|
||||
child: _buildLessonButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildLessonButton(LearnSubmoduleViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 15,
|
||||
borderRadius: 12,
|
||||
onTap: onLessonTap,
|
||||
text: 'View Module',
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
// onTap: () async => await viewModel.navigateToLearnLesson(
|
||||
// title: title,
|
||||
// topics: topics,
|
||||
// subtitle: subtitle,
|
||||
// practices: practices,
|
||||
// description: description),
|
||||
);
|
||||
|
||||
Widget _buildPracticeButtonWrapper(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
Expanded(
|
||||
child: _buildPracticeButton(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildPracticeButton(
|
||||
{required BuildContext context,
|
||||
required LearnSubmoduleViewModel viewModel}) =>
|
||||
CustomElevatedButton(
|
||||
height: 15,
|
||||
borderRadius: 12,
|
||||
onTap: onPracticeTap,
|
||||
text: 'View Practices',
|
||||
backgroundColor: kcWhite,
|
||||
borderColor: kcPrimaryColor,
|
||||
foregroundColor: kcPrimaryColor,
|
||||
// onTap: () async => await viewModel.navigateToLearnPractice(practices),
|
||||
);
|
||||
|
||||
Widget _buildSheet(LearnSubmoduleViewModel viewModel) => FinishPracticeSheet(
|
||||
onTap: viewModel.pop,
|
||||
);
|
||||
|
||||
// Widget _buildContainerShaderState() => status == ProgressStatuses.pending
|
||||
// ? _buildContainerShaderWrapper()
|
||||
// : Container();
|
||||
|
||||
Widget _buildContainerShaderWrapper() => Positioned.fill(
|
||||
child: _buildContainerShader(),
|
||||
);
|
||||
|
||||
Widget _buildContainerShader() => Container(
|
||||
decoration: BoxDecoration(
|
||||
color: kcWhite.withOpacity(0.5),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -36,17 +36,17 @@ class ModuleProgress extends StatelessWidget {
|
|||
[_buildProgressInfo(), _buildProgress()];
|
||||
|
||||
Widget _buildProgressInfo() => Text(
|
||||
'60% Progress',
|
||||
'0% Progress',
|
||||
style: style16DG400,
|
||||
);
|
||||
|
||||
Widget _buildProgress() => Text(
|
||||
'2/3',
|
||||
'0/3',
|
||||
style: style14P400,
|
||||
);
|
||||
|
||||
Widget _buildProgressIndicator() => const CustomLinearProgressIndicator(
|
||||
progress: 0.75,
|
||||
progress: 0,
|
||||
activeColor: kcPrimaryColor,
|
||||
backgroundColor: kcVeryLightGrey,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
|||
import 'package:yimaru_app/ui/widgets/custom_linear_progress_indicator.dart';
|
||||
|
||||
class OverallLearnProgress extends StatelessWidget {
|
||||
const OverallLearnProgress({super.key});
|
||||
final Color color;
|
||||
const OverallLearnProgress({super.key, required this.color});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildContainer();
|
||||
|
|
@ -12,8 +13,8 @@ class OverallLearnProgress extends StatelessWidget {
|
|||
Widget _buildContainer() => Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 25),
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
color: kcPrimaryColor.withOpacity(0.1),
|
||||
),
|
||||
child: _buildProgressSection(),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <audioplayers_linux/audioplayers_linux_plugin.h>
|
||||
#include <file_selector_linux/file_selector_plugin.h>
|
||||
#include <flutter_inappwebview_linux/flutter_inappwebview_linux_plugin.h>
|
||||
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||
#include <record_linux/record_linux_plugin.h>
|
||||
|
||||
|
|
@ -18,6 +19,9 @@ 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_inappwebview_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterInappwebviewLinuxPlugin");
|
||||
flutter_inappwebview_linux_plugin_register_with_registrar(flutter_inappwebview_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
||||
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
audioplayers_linux
|
||||
file_selector_linux
|
||||
flutter_inappwebview_linux
|
||||
flutter_secure_storage_linux
|
||||
record_linux
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import connectivity_plus
|
|||
import file_selector_macos
|
||||
import firebase_core
|
||||
import firebase_messaging
|
||||
import flutter_inappwebview_macos
|
||||
import flutter_local_notifications
|
||||
import flutter_secure_storage_darwin
|
||||
import google_sign_in_ios
|
||||
|
|
@ -27,6 +28,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
|
||||
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
|
||||
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||
FlutterSecureStorageDarwinPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageDarwinPlugin"))
|
||||
FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin"))
|
||||
|
|
|
|||
80
pubspec.lock
80
pubspec.lock
|
|
@ -558,6 +558,78 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
flutter_inappwebview:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_inappwebview
|
||||
sha256: "3952d116ee93bad2946401377e7ade87b5ef200e95ecb5ba1affa1b6329a6867"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.0-beta.3"
|
||||
flutter_inappwebview_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_inappwebview_android
|
||||
sha256: "8dfb76bd4e507112c3942c2272eeb01fab2e42be11374e5eb226f58698e7a04b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0-beta.3"
|
||||
flutter_inappwebview_internal_annotations:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_inappwebview_internal_annotations
|
||||
sha256: e30fba942e3debea7b7e6cdd4f0f59ce89dd403a9865193e3221293b6d1544c6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
flutter_inappwebview_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_inappwebview_ios
|
||||
sha256: ae8a78829398771be863aa3c8804a9d40728e1815e66c9c966f86d2cc3ae4fd9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0-beta.3"
|
||||
flutter_inappwebview_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_inappwebview_linux
|
||||
sha256: "2e1a3b09bb911fb5a8bb155cb7f1eb1428a19b6e20363b9db48beef428b8cef5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.0-beta.1"
|
||||
flutter_inappwebview_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_inappwebview_macos
|
||||
sha256: "545148cb5c46475ce669ab21621e9f2ad66e05f8e80b2cf49d4018879ab52393"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0-beta.3"
|
||||
flutter_inappwebview_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_inappwebview_platform_interface
|
||||
sha256: e3522c76e6760d1c0a9ff690e30e1503f226783d3277fa4d26675911977e9766
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0-beta.3"
|
||||
flutter_inappwebview_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_inappwebview_web
|
||||
sha256: e98b8875ccb6a3fd255873318db45c18ab135ed0ed22d20169abad9f5c810eb9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0-beta.3"
|
||||
flutter_inappwebview_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_inappwebview_windows
|
||||
sha256: "902edd6f6326952af822e21aa928f7426d723d45c94c15e6ce3c2d5640d28ad7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.0-beta.3"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
|
@ -1741,6 +1813,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
vimeo_video_player:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: vimeo_video_player
|
||||
sha256: b5dc8ad763489c94136e6080ba3ee89830742a48f5e7b2e28968f54d8c3734ad
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
name: yimaru_app
|
||||
description: A new Flutter project.
|
||||
publish_to: 'none'
|
||||
version: 0.1.3+4
|
||||
version: 0.1.3+5
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.3 <4.0.0'
|
||||
|
|
@ -38,6 +38,7 @@ dependencies:
|
|||
omni_datetime_picker: any
|
||||
json_serializable: ^6.8.0
|
||||
waveform_recorder: ^1.8.0
|
||||
vimeo_video_player: ^1.0.3
|
||||
permission_handler: ^12.0.1
|
||||
firebase_messaging: ^16.1.1
|
||||
cached_network_image: ^3.4.1
|
||||
|
|
@ -46,6 +47,7 @@ dependencies:
|
|||
flutter_secure_storage: ^10.0.0
|
||||
flutter_timer_countdown: ^1.0.7
|
||||
flutter_carousel_widget: ^3.1.0
|
||||
flutter_inappwebview: ^6.2.0-beta.3
|
||||
flutter_local_notifications: ^20.1.0
|
||||
internet_connection_checker_plus: ^2.9.1+2
|
||||
|
||||
|
|
|
|||
|
|
@ -8,40 +8,42 @@ import 'dart:ui' as _i10;
|
|||
|
||||
import 'package:audioplayers/audioplayers.dart' as _i4;
|
||||
import 'package:dio/dio.dart' as _i2;
|
||||
import 'package:firebase_messaging/firebase_messaging.dart' as _i32;
|
||||
import 'package:firebase_messaging/firebase_messaging.dart' as _i34;
|
||||
import 'package:flutter/material.dart' as _i8;
|
||||
import 'package:mockito/mockito.dart' as _i1;
|
||||
import 'package:mockito/src/dummies.dart' as _i7;
|
||||
import 'package:permission_handler/permission_handler.dart' as _i27;
|
||||
import 'package:permission_handler/permission_handler.dart' as _i29;
|
||||
import 'package:stacked_services/stacked_services.dart' as _i6;
|
||||
import 'package:waveform_recorder/waveform_recorder.dart' as _i5;
|
||||
import 'package:yimaru_app/models/category.dart' as _i15;
|
||||
import 'package:yimaru_app/models/course.dart' as _i21;
|
||||
import 'package:yimaru_app/models/course_detail.dart' as _i35;
|
||||
import 'package:yimaru_app/models/course_detail.dart' as _i37;
|
||||
import 'package:yimaru_app/models/course_lesson.dart' as _i18;
|
||||
import 'package:yimaru_app/models/course_progress.dart' as _i17;
|
||||
import 'package:yimaru_app/models/lesson.dart' as _i25;
|
||||
import 'package:yimaru_app/models/level.dart' as _i22;
|
||||
import 'package:yimaru_app/models/module.dart' as _i23;
|
||||
import 'package:yimaru_app/models/practice.dart' as _i19;
|
||||
import 'package:yimaru_app/models/practice_question.dart' as _i20;
|
||||
import 'package:yimaru_app/models/question.dart' as _i14;
|
||||
import 'package:yimaru_app/models/subcategory.dart' as _i16;
|
||||
import 'package:yimaru_app/models/submodule.dart' as _i24;
|
||||
import 'package:yimaru_app/models/user.dart' as _i12;
|
||||
import 'package:yimaru_app/services/api_service.dart' as _i13;
|
||||
import 'package:yimaru_app/services/audio_player_service.dart' as _i36;
|
||||
import 'package:yimaru_app/services/audio_player_service.dart' as _i38;
|
||||
import 'package:yimaru_app/services/authentication_service.dart' as _i11;
|
||||
import 'package:yimaru_app/services/course_service.dart' as _i34;
|
||||
import 'package:yimaru_app/services/dio_service.dart' as _i24;
|
||||
import 'package:yimaru_app/services/google_auth_service.dart' as _i29;
|
||||
import 'package:yimaru_app/services/image_downloader_service.dart' as _i30;
|
||||
import 'package:yimaru_app/services/image_picker_service.dart' as _i28;
|
||||
import 'package:yimaru_app/services/notification_service.dart' as _i31;
|
||||
import 'package:yimaru_app/services/permission_handler_service.dart' as _i26;
|
||||
import 'package:yimaru_app/services/course_service.dart' as _i36;
|
||||
import 'package:yimaru_app/services/dio_service.dart' as _i26;
|
||||
import 'package:yimaru_app/services/google_auth_service.dart' as _i31;
|
||||
import 'package:yimaru_app/services/image_downloader_service.dart' as _i32;
|
||||
import 'package:yimaru_app/services/image_picker_service.dart' as _i30;
|
||||
import 'package:yimaru_app/services/notification_service.dart' as _i33;
|
||||
import 'package:yimaru_app/services/permission_handler_service.dart' as _i28;
|
||||
import 'package:yimaru_app/services/secure_storage_service.dart' as _i3;
|
||||
import 'package:yimaru_app/services/smart_auth_service.dart' as _i33;
|
||||
import 'package:yimaru_app/services/status_checker_service.dart' as _i25;
|
||||
import 'package:yimaru_app/services/voice_recorder_service.dart' as _i37;
|
||||
import 'package:yimaru_app/ui/common/enmus.dart' as _i38;
|
||||
import 'package:yimaru_app/services/smart_auth_service.dart' as _i35;
|
||||
import 'package:yimaru_app/services/status_checker_service.dart' as _i27;
|
||||
import 'package:yimaru_app/services/voice_recorder_service.dart' as _i39;
|
||||
import 'package:yimaru_app/ui/common/enmus.dart' as _i40;
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: avoid_redundant_argument_values
|
||||
|
|
@ -1128,7 +1130,7 @@ class MockApiService extends _i1.Mock implements _i13.ApiService {
|
|||
@override
|
||||
_i9.Future<List<_i15.Category>> getCategories() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getCourseCategories,
|
||||
#getCategories,
|
||||
[],
|
||||
),
|
||||
returnValue: _i9.Future<List<_i15.Category>>.value(<_i15.Category>[]),
|
||||
|
|
@ -1140,7 +1142,7 @@ class MockApiService extends _i1.Mock implements _i13.ApiService {
|
|||
_i9.Future<List<_i16.Subcategory>> getSubcategories(int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getCourseSubcategories,
|
||||
#getSubcategories,
|
||||
[id],
|
||||
),
|
||||
returnValue:
|
||||
|
|
@ -1270,6 +1272,29 @@ class MockApiService extends _i1.Mock implements _i13.ApiService {
|
|||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i23.Module>>.value(<_i23.Module>[]),
|
||||
) as _i9.Future<List<_i23.Module>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i24.Submodule>> getSubmodules(int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getSubmodules,
|
||||
[id],
|
||||
),
|
||||
returnValue: _i9.Future<List<_i24.Submodule>>.value(<_i24.Submodule>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i24.Submodule>>.value(<_i24.Submodule>[]),
|
||||
) as _i9.Future<List<_i24.Submodule>>);
|
||||
|
||||
@override
|
||||
_i9.Future<List<_i25.Lesson>> getLessons(int? id) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getLessons,
|
||||
[id],
|
||||
),
|
||||
returnValue: _i9.Future<List<_i25.Lesson>>.value(<_i25.Lesson>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i25.Lesson>>.value(<_i25.Lesson>[]),
|
||||
) as _i9.Future<List<_i25.Lesson>>);
|
||||
}
|
||||
|
||||
/// A class which mocks [SecureStorageService].
|
||||
|
|
@ -1372,7 +1397,7 @@ class MockSecureStorageService extends _i1.Mock
|
|||
/// A class which mocks [DioService].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockDioService extends _i1.Mock implements _i24.DioService {
|
||||
class MockDioService extends _i1.Mock implements _i26.DioService {
|
||||
@override
|
||||
_i2.Dio get dio => (super.noSuchMethod(
|
||||
Invocation.getter(#dio),
|
||||
|
|
@ -1391,7 +1416,7 @@ class MockDioService extends _i1.Mock implements _i24.DioService {
|
|||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockStatusCheckerService extends _i1.Mock
|
||||
implements _i25.StatusCheckerService {
|
||||
implements _i27.StatusCheckerService {
|
||||
@override
|
||||
_i3.SecureStorageService get storage => (super.noSuchMethod(
|
||||
Invocation.getter(#storage),
|
||||
|
|
@ -1457,40 +1482,40 @@ class MockStatusCheckerService extends _i1.Mock
|
|||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockPermissionHandlerService extends _i1.Mock
|
||||
implements _i26.PermissionHandlerService {
|
||||
implements _i28.PermissionHandlerService {
|
||||
@override
|
||||
_i9.Future<_i27.PermissionStatus> requestPermission(
|
||||
_i27.Permission? requestedPermission) =>
|
||||
_i9.Future<_i29.PermissionStatus> requestPermission(
|
||||
_i29.Permission? requestedPermission) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#requestPermission,
|
||||
[requestedPermission],
|
||||
),
|
||||
returnValue: _i9.Future<_i27.PermissionStatus>.value(
|
||||
_i27.PermissionStatus.denied),
|
||||
returnValueForMissingStub: _i9.Future<_i27.PermissionStatus>.value(
|
||||
_i27.PermissionStatus.denied),
|
||||
) as _i9.Future<_i27.PermissionStatus>);
|
||||
returnValue: _i9.Future<_i29.PermissionStatus>.value(
|
||||
_i29.PermissionStatus.denied),
|
||||
returnValueForMissingStub: _i9.Future<_i29.PermissionStatus>.value(
|
||||
_i29.PermissionStatus.denied),
|
||||
) as _i9.Future<_i29.PermissionStatus>);
|
||||
|
||||
@override
|
||||
_i9.Future<_i27.PermissionStatus> request(_i27.Permission? permission) =>
|
||||
_i9.Future<_i29.PermissionStatus> request(_i29.Permission? permission) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#request,
|
||||
[permission],
|
||||
),
|
||||
returnValue: _i9.Future<_i27.PermissionStatus>.value(
|
||||
_i27.PermissionStatus.denied),
|
||||
returnValueForMissingStub: _i9.Future<_i27.PermissionStatus>.value(
|
||||
_i27.PermissionStatus.denied),
|
||||
) as _i9.Future<_i27.PermissionStatus>);
|
||||
returnValue: _i9.Future<_i29.PermissionStatus>.value(
|
||||
_i29.PermissionStatus.denied),
|
||||
returnValueForMissingStub: _i9.Future<_i29.PermissionStatus>.value(
|
||||
_i29.PermissionStatus.denied),
|
||||
) as _i9.Future<_i29.PermissionStatus>);
|
||||
}
|
||||
|
||||
/// A class which mocks [ImagePickerService].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockImagePickerService extends _i1.Mock
|
||||
implements _i28.ImagePickerService {
|
||||
implements _i30.ImagePickerService {
|
||||
@override
|
||||
_i9.Future<String?> gallery() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
|
|
@ -1515,7 +1540,7 @@ class MockImagePickerService extends _i1.Mock
|
|||
/// A class which mocks [GoogleAuthService].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockGoogleAuthService extends _i1.Mock implements _i29.GoogleAuthService {
|
||||
class MockGoogleAuthService extends _i1.Mock implements _i31.GoogleAuthService {
|
||||
@override
|
||||
int get listenersCount => (super.noSuchMethod(
|
||||
Invocation.getter(#listenersCount),
|
||||
|
|
@ -1585,7 +1610,7 @@ class MockGoogleAuthService extends _i1.Mock implements _i29.GoogleAuthService {
|
|||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockImageDownloaderService extends _i1.Mock
|
||||
implements _i30.ImageDownloaderService {
|
||||
implements _i32.ImageDownloaderService {
|
||||
@override
|
||||
_i9.Future<String> downloader(String? networkImage) => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
|
|
@ -1614,7 +1639,7 @@ class MockImageDownloaderService extends _i1.Mock
|
|||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockNotificationService extends _i1.Mock
|
||||
implements _i31.NotificationService {
|
||||
implements _i33.NotificationService {
|
||||
@override
|
||||
_i9.Future<void> initialize() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
|
|
@ -1636,7 +1661,7 @@ class MockNotificationService extends _i1.Mock
|
|||
) as _i9.Future<void>);
|
||||
|
||||
@override
|
||||
_i9.Future<void> showNotification(_i32.RemoteMessage? message) =>
|
||||
_i9.Future<void> showNotification(_i34.RemoteMessage? message) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#showNotification,
|
||||
|
|
@ -1670,7 +1695,7 @@ class MockNotificationService extends _i1.Mock
|
|||
/// A class which mocks [SmartAuthService].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockSmartAuthService extends _i1.Mock implements _i33.SmartAuthService {
|
||||
class MockSmartAuthService extends _i1.Mock implements _i35.SmartAuthService {
|
||||
@override
|
||||
bool get listenForMultipleSms => (super.noSuchMethod(
|
||||
Invocation.getter(#listenForMultipleSms),
|
||||
|
|
@ -1702,26 +1727,26 @@ class MockSmartAuthService extends _i1.Mock implements _i33.SmartAuthService {
|
|||
/// A class which mocks [CourseService].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockCourseService extends _i1.Mock implements _i34.CourseService {
|
||||
class MockCourseService extends _i1.Mock implements _i36.CourseService {
|
||||
@override
|
||||
_i9.Future<List<_i35.CourseDetail>> getCoursesDetail(int? id) =>
|
||||
_i9.Future<List<_i37.CourseDetail>> getCoursesDetail(int? id) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#getCoursesDetail,
|
||||
[id],
|
||||
),
|
||||
returnValue:
|
||||
_i9.Future<List<_i35.CourseDetail>>.value(<_i35.CourseDetail>[]),
|
||||
_i9.Future<List<_i37.CourseDetail>>.value(<_i37.CourseDetail>[]),
|
||||
returnValueForMissingStub:
|
||||
_i9.Future<List<_i35.CourseDetail>>.value(<_i35.CourseDetail>[]),
|
||||
) as _i9.Future<List<_i35.CourseDetail>>);
|
||||
_i9.Future<List<_i37.CourseDetail>>.value(<_i37.CourseDetail>[]),
|
||||
) as _i9.Future<List<_i37.CourseDetail>>);
|
||||
}
|
||||
|
||||
/// A class which mocks [AudioPlayerService].
|
||||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockAudioPlayerService extends _i1.Mock
|
||||
implements _i36.AudioPlayerService {
|
||||
implements _i38.AudioPlayerService {
|
||||
@override
|
||||
_i4.AudioPlayer get player => (super.noSuchMethod(
|
||||
Invocation.getter(#player),
|
||||
|
|
@ -1845,13 +1870,13 @@ class MockAudioPlayerService extends _i1.Mock
|
|||
///
|
||||
/// See the documentation for Mockito's code generation for more information.
|
||||
class MockVoiceRecorderService extends _i1.Mock
|
||||
implements _i37.VoiceRecorderService {
|
||||
implements _i39.VoiceRecorderService {
|
||||
@override
|
||||
_i38.VoiceRecordingState get recordingState => (super.noSuchMethod(
|
||||
_i40.VoiceRecordingState get recordingState => (super.noSuchMethod(
|
||||
Invocation.getter(#recordingState),
|
||||
returnValue: _i38.VoiceRecordingState.pending,
|
||||
returnValueForMissingStub: _i38.VoiceRecordingState.pending,
|
||||
) as _i38.VoiceRecordingState);
|
||||
returnValue: _i40.VoiceRecordingState.pending,
|
||||
returnValueForMissingStub: _i40.VoiceRecordingState.pending,
|
||||
) as _i40.VoiceRecordingState);
|
||||
|
||||
@override
|
||||
_i5.WaveformRecorderController get waveController => (super.noSuchMethod(
|
||||
|
|
|
|||
11
test/viewmodels/learn_submodule_viewmodel_test.dart
Normal file
11
test/viewmodels/learn_submodule_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('LearnSubmoduleViewModel Tests -', () {
|
||||
setUp(() => registerServices());
|
||||
tearDown(() => locator.reset());
|
||||
});
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||
#include <file_selector_windows/file_selector_windows.h>
|
||||
#include <firebase_core/firebase_core_plugin_c_api.h>
|
||||
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
|
||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||
#include <record_windows/record_windows_plugin_c_api.h>
|
||||
|
|
@ -26,6 +27,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||
FirebaseCorePluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
|
||||
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
|
||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
|||
connectivity_plus
|
||||
file_selector_windows
|
||||
firebase_core
|
||||
flutter_inappwebview_windows
|
||||
flutter_secure_storage_windows
|
||||
permission_handler_windows
|
||||
record_windows
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user