From 43b0af01c603c6ef637762e23b8cb7c04d1d70b3 Mon Sep 17 00:00:00 2001 From: 12henbx Date: Sat, 21 Oct 2023 16:55:11 +0700 Subject: [PATCH 1/9] create material file page, bloc, state, event --- app/android/app/src/main/AndroidManifest.xml | 2 + app/lib/app.dart | 39 +- .../main/presentation/pages/main_page.dart | 2 +- .../menu/presentation/pages/_pages.dart | 10 + .../presentation/pages/material_menu.dart | 363 ++++++++++++++++++ .../repositories/material_file_repo.dart | 36 ++ .../bloc/material_viewer_bloc.dart | 112 ++++++ .../bloc/material_viewer_event.dart | 15 + .../bloc/material_viewer_state.dart | 23 ++ .../viewer/presentation/pages/_pages.dart | 17 + .../presentation/pages/pdf_viewer_page.dart | 240 ++++++++++++ app/lib/services/router_service.dart | 13 +- app/pubspec.lock | 48 +++ app/pubspec.yaml | 2 + .../flutter/generated_plugin_registrant.cc | 3 + app/windows/flutter/generated_plugins.cmake | 1 + 16 files changed, 908 insertions(+), 18 deletions(-) create mode 100644 app/lib/features/material/menu/presentation/pages/_pages.dart create mode 100644 app/lib/features/material/menu/presentation/pages/material_menu.dart create mode 100644 app/lib/features/material/menu/presentation/repositories/material_file_repo.dart create mode 100644 app/lib/features/material/viewer/presentation/bloc/material_viewer_bloc.dart create mode 100644 app/lib/features/material/viewer/presentation/bloc/material_viewer_event.dart create mode 100644 app/lib/features/material/viewer/presentation/bloc/material_viewer_state.dart create mode 100644 app/lib/features/material/viewer/presentation/pages/_pages.dart create mode 100644 app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart diff --git a/app/android/app/src/main/AndroidManifest.xml b/app/android/app/src/main/AndroidManifest.xml index 65d7660..506b1a7 100644 --- a/app/android/app/src/main/AndroidManifest.xml +++ b/app/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,6 @@ + + ( - create: (_) => get() - ..add( - OnboardingAuthEvent(), - ), - ), - BlocProvider(create: (context) => QuizRegistrationCubit()), - ], - child: MaterialApp.router( - theme: ThemeData( - textTheme: GoogleFonts.interTextTheme(), - ), - routerConfig: router, - debugShowCheckedModeBanner: false, + return MaterialApp.router( + theme: ThemeData( + textTheme: GoogleFonts.interTextTheme(), ), + routerConfig: router, + debugShowCheckedModeBanner: false, ); + // MultiBlocProvider( + // providers: [ + // BlocProvider( + // create: (_) => get() + // ..add( + // OnboardingAuthEvent(), + // ), + // ), + // BlocProvider(create: (context) => QuizRegistrationCubit()), + // ], + // child: MaterialApp.router( + // theme: ThemeData( + // textTheme: GoogleFonts.interTextTheme(), + // ), + // routerConfig: router, + // debugShowCheckedModeBanner: false, + // ), + // ); } } diff --git a/app/lib/features/main/presentation/pages/main_page.dart b/app/lib/features/main/presentation/pages/main_page.dart index 8febd95..d564982 100644 --- a/app/lib/features/main/presentation/pages/main_page.dart +++ b/app/lib/features/main/presentation/pages/main_page.dart @@ -51,7 +51,7 @@ class _MainPageState extends State { Button( buttonType: ButtonType.primary, onTap: () async { - await context.push('/construction'); + await context.push('/material'); }, text: 'Lihat Materi', ), diff --git a/app/lib/features/material/menu/presentation/pages/_pages.dart b/app/lib/features/material/menu/presentation/pages/_pages.dart new file mode 100644 index 0000000..59b355d --- /dev/null +++ b/app/lib/features/material/menu/presentation/pages/_pages.dart @@ -0,0 +1,10 @@ + +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +import '../../../../../core/bases/enum/button_type.dart'; +import '../../../../../core/bases/widgets/atoms/button.dart'; +import '../../../../../core/bases/widgets/layout/bebras_scaffold.dart'; +import '../../../../../core/constants/assets.dart'; + +part 'material_menu.dart'; \ No newline at end of file diff --git a/app/lib/features/material/menu/presentation/pages/material_menu.dart b/app/lib/features/material/menu/presentation/pages/material_menu.dart new file mode 100644 index 0000000..81c3000 --- /dev/null +++ b/app/lib/features/material/menu/presentation/pages/material_menu.dart @@ -0,0 +1,363 @@ +part of '_pages.dart'; + +class MaterialMenu extends StatefulWidget { + const MaterialMenu({super.key}); + + @override + State createState() => _MaterialMenuState(); +} + +class _MaterialMenuState extends State { + int value = 0; + Widget CustomRadioButton(String text, int index){ + return OutlinedButton( + onPressed: (){ + setState(() { + value = index; + }); + }, + // shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + // borderSide: BorderSide(color: (value == index) ? Colors.green : Colors.black), + child: Text( + text, + style: TextStyle( + color: (value == index) ? Colors.green : Colors.black, + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return BebrasScaffold( + // avoidBottomInset: false, + body: Padding( + padding: const EdgeInsets.only(left: 0.0, top: 10.0, right: 0.0), + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.all(32), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset( + Assets.bebrasPandaiText, + ), + const SizedBox( + height: 30, + ), + const Text('Latihan yang pernah diikuti'), + const SizedBox( + height: 10, + ), + Container( + height: MediaQuery.of(context).size.height - 320, + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 1), + decoration: BoxDecoration(border: Border.all()), + child: ListView( + // padding: const EdgeInsets.all(8), + children: [ + InkWell( + onTap: () { + print('object'); + context.go('/material/9090'); + }, + child: Container( + height: 80, + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration(border: Border.all()), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Quiz A', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500), + ), + Text( + 'Skor: 90/100', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), + ], + ), + ), + ), + ), + Container( + height: 80, + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration(border: Border.all()), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Quiz A', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500), + ), + Text( + 'Skor: 90/100', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), + ], + ), + ), + ), + Container( + height: 80, + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration(border: Border.all()), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Quiz A', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500), + ), + Text( + 'Skor: 90/100', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), + ], + ), + ), + ), + Container( + height: 80, + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration(border: Border.all()), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Quiz A', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500), + ), + Text( + 'Skor: 90/100', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), + ], + ), + ), + ), + Container( + height: 80, + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration(border: Border.all()), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Quiz A', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500), + ), + Text( + 'Skor: 90/100', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), + ], + ), + ), + ), + Container( + height: 80, + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration(border: Border.all()), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Quiz A', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500), + ), + Text( + 'Skor: 90/100', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), + ], + ), + ), + ), + ], + ), + ), + // Row( + // children: [ + // CustomRadioButton("Single", 1), + // CustomRadioButton("Married", 2), + // CustomRadioButton("Other", 3) + // ], + // ), + // const SizedBox( + // height: 10, + // ), + Container( + height: 70, + width: double.infinity, + decoration: BoxDecoration(border: Border.all()), + child: Column( + children: [ + Row( + children: [ + Container( + alignment: Alignment.center, + height: 34, + width: 147, + // padding: const EdgeInsets.symmetric(horizontal: 1), + decoration: BoxDecoration( + border: Border.all(), + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black, + blurRadius: 2.0, + spreadRadius: 0.0, + offset: Offset(2.0, 2.0), // shadow direction: bottom right + ) + ], + ), + child: Text( + 'siKecil', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), + ), + Container( + alignment: Alignment.center, + height: 34, + width: 147, + // padding: const EdgeInsets.symmetric(horizontal: 1), + decoration: BoxDecoration( + border: Border.all(), + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black, + blurRadius: 2.0, + spreadRadius: 0.0, + offset: Offset(2.0, 2.0), // shadow direction: bottom right + ) + ], + ), + child: Text( + 'Siaga', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), + ), + ], + ), + Row( + children: [ + Container( + alignment: Alignment.center, + height: 34, + width: 147, + // padding: const EdgeInsets.symmetric(horizontal: 1), + decoration: BoxDecoration( + border: Border.all(), + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black, + blurRadius: 2.0, + spreadRadius: 0.0, + offset: Offset(2.0, 2.0), // shadow direction: bottom right + ) + ], + ), + child: Text( + 'Penegak', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), + ), + Container( + alignment: Alignment.center, + height: 34, + width: 147, + // padding: const EdgeInsets.symmetric(horizontal: 1), + decoration: BoxDecoration( + border: Border.all(), + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black, + blurRadius: 2.0, + spreadRadius: 0.0, + offset: Offset(2.0, 2.0), // shadow direction: bottom right + ) + ], + ), + child: Text( + 'Penggalang', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), + ), + ], + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/app/lib/features/material/menu/presentation/repositories/material_file_repo.dart b/app/lib/features/material/menu/presentation/repositories/material_file_repo.dart new file mode 100644 index 0000000..ba4b221 --- /dev/null +++ b/app/lib/features/material/menu/presentation/repositories/material_file_repo.dart @@ -0,0 +1,36 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:injectable/injectable.dart'; + +@Injectable() +class RegisterUserRepository { + final _firecloud = FirebaseFirestore.instance.collection('registered_user'); + + Future create({ + required String userId, + required dynamic email, + required String name, + required String birth_date, + required String school, + required String province, + required String bebras_biro, + }) async { + try { + await _firecloud.doc(userId) + .set({ + "name": name, + "email": email, + "birth_date": birth_date, + "school": school, + "province": province, + "bebras_biro": bebras_biro, + }, + SetOptions(merge: true), + ); + } on FirebaseException catch (e) { + if (kDebugMode) { + print("Failed with error '${e.code}': '${e.message}'"); + } + } catch (e) { + throw Exception(e.toString()); + } + } \ No newline at end of file diff --git a/app/lib/features/material/viewer/presentation/bloc/material_viewer_bloc.dart b/app/lib/features/material/viewer/presentation/bloc/material_viewer_bloc.dart new file mode 100644 index 0000000..955fef8 --- /dev/null +++ b/app/lib/features/material/viewer/presentation/bloc/material_viewer_bloc.dart @@ -0,0 +1,112 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:injectable/injectable.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:path_provider/path_provider.dart'; + +part 'material_viewer_event.dart'; +part 'material_viewer_state.dart'; + +@injectable +@singleton +class MaterialViewerBloc + extends Bloc { + MaterialViewerBloc() : super(MaterialFileInitialization()) { + on(checkIsMaterialFileExist); + on(dialogPermitDownloadFile); + } + + FutureOr checkIsMaterialFileExist( + CheckMaterialFileEvent state, + Emitter emit, + ) async { + bool isFile = await File("/data/user/0/com.example.bebras.bebras_app_ui/app_flutter/pdfAcademy.pdf").exists(); + if(isFile) { + emit(MaterialFileExist()); + } else { + emit(MaterialFileDoesNotExist()); + } + // final creds = await _googleSignIn.signInSilently(); + // + // if (creds != null) { + // emit(UserAuthenticated()); + // } else { + // emit(UserUnauthenticated()); + // } + } + + FutureOr dialogPermitDownloadFile( + DialogDownloadPermitEvent state, + Emitter emit, + ) async { + if(state.userPermission) { + String url = 'https://firebasestorage.googleapis.com/v0/b/toki-bebras-proto.appspot.com/o/buku_bebras%2FBebras-Challenge-2016_Penegak.pdf'; + emit(UserAgreeToDownload()); + try { + if (await _requestPermission(Permission.storage)) { + Directory? directory; + directory = await getExternalStorageDirectory(); + String newPath = ""; + List paths = directory!.path.split("/"); + for (int x = 1; x < paths.length; x++) { + String folder = paths[x]; + if (folder != "Android") { + newPath += "/" + folder; + } else { + break; + } + } + newPath = newPath + "/Materi_Bebras"; + print(newPath); + directory = Directory(newPath); + + File saveFile = File(directory.path + "/pdfAcademy.pdf"); + if (kDebugMode) { + print(saveFile.path); + } + if (!await directory.exists()) { + await directory.create(recursive: true); + } + if (await directory.exists()) { + await Dio().download( + url, + saveFile.path, + ); + emit(MaterialFileDownloaded()); + } + } + // return true; + } catch (e) { + // return false; + } + } else { + emit(UserDisagreeToDownload()); + } + // final creds = await _googleSignIn.signInSilently(); + // + // if (creds != null) { + // emit(UserAuthenticated()); + // } else { + // emit(UserUnauthenticated()); + // } + } +} + +Future _requestPermission(Permission permission) async { + if (await permission.isGranted) { + return true; + } else { + print('lolos234'); + var result = await permission.request(); + if (result == PermissionStatus.granted) { + return true; + } + } + return false; +} diff --git a/app/lib/features/material/viewer/presentation/bloc/material_viewer_event.dart b/app/lib/features/material/viewer/presentation/bloc/material_viewer_event.dart new file mode 100644 index 0000000..6d5379a --- /dev/null +++ b/app/lib/features/material/viewer/presentation/bloc/material_viewer_event.dart @@ -0,0 +1,15 @@ +part of 'material_viewer_bloc.dart'; + +abstract class MaterialViewerEvent extends Equatable { + const MaterialViewerEvent(); + + @override + List get props => []; +} + +class CheckMaterialFileEvent extends MaterialViewerEvent {} + +class DialogDownloadPermitEvent extends MaterialViewerEvent { + final bool userPermission; + const DialogDownloadPermitEvent(this.userPermission); +} diff --git a/app/lib/features/material/viewer/presentation/bloc/material_viewer_state.dart b/app/lib/features/material/viewer/presentation/bloc/material_viewer_state.dart new file mode 100644 index 0000000..6e83896 --- /dev/null +++ b/app/lib/features/material/viewer/presentation/bloc/material_viewer_state.dart @@ -0,0 +1,23 @@ +part of 'material_viewer_bloc.dart'; + +class MaterialViewerState extends Equatable { + final String pdfPath; + const MaterialViewerState({ + this.pdfPath = '', + }); + + @override + List get props => []; +} + +class MaterialFileInitialization extends MaterialViewerState {} + +class UserAgreeToDownload extends MaterialViewerState {} + +class UserDisagreeToDownload extends MaterialViewerState {} + +class MaterialFileExist extends MaterialViewerState {} + +class MaterialFileDoesNotExist extends MaterialViewerState {} + +class MaterialFileDownloaded extends MaterialViewerState {} diff --git a/app/lib/features/material/viewer/presentation/pages/_pages.dart b/app/lib/features/material/viewer/presentation/pages/_pages.dart new file mode 100644 index 0000000..c9fb029 --- /dev/null +++ b/app/lib/features/material/viewer/presentation/pages/_pages.dart @@ -0,0 +1,17 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_pdfview/flutter_pdfview.dart'; +import 'package:path/path.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; + +import '../../../../../services/di.dart'; +import '../bloc/material_viewer_bloc.dart'; + +part 'pdf_viewer_page.dart'; \ No newline at end of file diff --git a/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart b/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart new file mode 100644 index 0000000..2998f46 --- /dev/null +++ b/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart @@ -0,0 +1,240 @@ +part of '_pages.dart'; + +class PdfViewerPage extends StatefulWidget { + final File? file; + final String url; + + const PdfViewerPage({ + Key? key, + this.file, + required this.url, + }) : super(key: key); + + @override + State createState() => _PdfViewerPageState(); +} + +class _PdfViewerPageState extends State { + late final MaterialViewerBloc _materialViewerBloc; + String pathPDF = ""; + String landscapePathPdf = ""; + String remotePDFpath = ""; + String corruptedPathPDF = ""; + + @override + void initState() { + super.initState(); + _materialViewerBloc = get(); + print('oiuvh'); + + // Fetch from local storage + // fromAsset('assets/demo-link.pdf', 'demo.pdf').then((f) { + // setState(() { + // pathPDF = f.path; + // }); + // }); + + // Trigger to fetch from URL + // createFileOfPdfUrl().then((f) { + // setState(() { + // remotePDFpath = f.path; + // }); + // }); + } + + Future createFileOfPdfUrl() async { + Completer completer = Completer(); + print("Start download file from internet!"); + try { + // "https://berlin2017.droidcon.cod.newthinking.net/sites/global.droidcon.cod.newthinking.net/files/media/documents/Flutter%20-%2060FPS%20UI%20of%20the%20future%20%20-%20DroidconDE%2017.pdf"; + // final url = "https://pdfkit.org/docs/guide.pdf"; + final url = "http://www.pdf995.com/samples/pdf.pdf"; + final filename = url.substring(url.lastIndexOf("/") + 1); + var request = await HttpClient().getUrl(Uri.parse(url)); + var response = await request.close(); + var bytes = await consolidateHttpClientResponseBytes(response); + var dir = await getApplicationDocumentsDirectory(); + print("Download files"); + print("${dir.path}/$filename"); + File file = File("${dir.path}/$filename"); + + await file.writeAsBytes(bytes, flush: true); + completer.complete(file); + } catch (e) { + throw Exception('Error parsing asset file!'); + } + + return completer.future; + } + + showAlertDialog(BuildContext context) { + Widget cancelButton = ElevatedButton( + child: Text("Tidak"), + onPressed: () {}, + ); + Widget continueButton = ElevatedButton( + child: Text("Ya"), + onPressed: () { + _materialViewerBloc.add( + DialogDownloadPermitEvent(true), + ); + }, + ); // set up the AlertDialog + AlertDialog alert = AlertDialog( + title: Text("AlertDialog"), + content: Text("File materi tidak ditemukan, ingin mengunduh?"), + actions: [ + cancelButton, + continueButton, + ], + ); // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); + } + + @override + Widget build(BuildContext context) { + // final name = basename(widget.file.path); + return BlocProvider( + create: (context) => MaterialViewerBloc()..add(CheckMaterialFileEvent()), + child: Scaffold( + appBar: AppBar( + backgroundColor: Color(0xffe6f6ff), + title: Text( + 'name', // name + style: TextStyle(color: Colors.black), + ), + actions: [ + IconButton( + onPressed: () async { + await saveFile(widget.url, "sample.pdf"); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'successfully saved to internal storage "PDF_Download" folder', + style: TextStyle(color: Colors.white), + ), + ), + ); + }, + icon: const Icon(Icons.download_rounded), + ), + ], + ), + body: Stack( + children: [ + BlocBuilder( + builder: (BuildContext context, state) { + String filePath = ''; + print('test2'); + print(state.toString()); + if(state is MaterialFileExist) { + filePath = '/data/user/0/com.example.bebras.bebras_app_ui/app_flutter/pdfAcademy.pdf'; + } else if (state is MaterialFileDoesNotExist){ + Future.delayed(Duration.zero, () async { + showAlertDialog(context); + }); + // _materialViewerBloc.add( + // DialogDownloadPermitEvent(true), + // ); + // setState(() { + // // remotePDFpath = f.path; + // }); + filePath = '/'; + } else if (state is MaterialFileDownloaded) { + return PDFView( + filePath: filePath, + onError: (error) { + print(error.toString()); + }, + onPageError: (page, error) { + print('$page: ${error.toString()}'); + }, + // onViewCreated: (PDFViewController pdfViewController) { + // _controller.complete(pdfViewController); + // }, + // onPageChanged: (int page, int total) { + // print('page change: $page/$total'); + // }, + ); + // saveFile(); + } else if (state is UserDisagreeToDownload) { + + } + return Container(); + // PDFView( + // filePath: filePath, + // onError: (error) { + // print(error.toString()); + // }, + // onPageError: (page, error) { + // print('$page: ${error.toString()}'); + // }, + // // onViewCreated: (PDFViewController pdfViewController) { + // // _controller.complete(pdfViewController); + // // }, + // // onPageChanged: (int page, int total) { + // // print('page change: $page/$total'); + // // }, + // ); + }), + ], + ), + ), + ); + } + + Future saveFile(String url, String fileName) async { + try { + if (await _requestPermission(Permission.storage)) { + Directory? directory; + directory = await getExternalStorageDirectory(); + String newPath = ""; + List paths = directory!.path.split("/"); + for (int x = 1; x < paths.length; x++) { + String folder = paths[x]; + if (folder != "Android") { + newPath += "/" + folder; + } else { + break; + } + } + newPath = newPath + "/PDF_Download"; + directory = Directory(newPath); + + File saveFile = File(directory.path + "/$fileName"); + if (kDebugMode) { + print(saveFile.path); + } + if (!await directory.exists()) { + await directory.create(recursive: true); + } + if (await directory.exists()) { + await Dio().download( + url, + saveFile.path, + ); + } + } + return true; + } catch (e) { + return false; + } + } + + Future _requestPermission(Permission permission) async { + if (await permission.isGranted) { + return true; + } else { + var result = await permission.request(); + if (result == PermissionStatus.granted) { + return true; + } + } + return false; + } +} diff --git a/app/lib/services/router_service.dart b/app/lib/services/router_service.dart index e24c062..18a0eba 100644 --- a/app/lib/services/router_service.dart +++ b/app/lib/services/router_service.dart @@ -3,6 +3,8 @@ import 'package:go_router/go_router.dart'; import '../features/authentication/signin/presentation/pages/_pages.dart'; import '../features/error/presentation/pages/_pages.dart'; import '../features/main/presentation/pages/_pages.dart'; +import '../features/material/menu/presentation/pages/_pages.dart'; +import '../features/material/viewer/presentation/pages/_pages.dart'; import '../features/onboarding/presentation/pages/_pages.dart'; import '../features/quiz_registration/presentation/pages/_pages.dart'; @@ -10,7 +12,8 @@ GoRouter router = GoRouter( routes: [ GoRoute( path: '/', - builder: (context, state) => const SplashScreen(), + builder: (context, state) => const MaterialMenu(), + // builder: (context, state) => const SplashScreen(), ), GoRoute( path: '/onboarding', @@ -28,5 +31,13 @@ GoRouter router = GoRouter( path: '/quiz_registration', builder: (context, state) => const QuizRegistrationPage(), ), + GoRoute( + path: '/material', + builder: (context, state) => const MaterialMenu(), + ), + GoRoute( + path: '/material/:id', + builder: (context, state) => const PdfViewerPage(file: null, url: '/abc'), + ), ], ); diff --git a/app/pubspec.lock b/app/pubspec.lock index 49449d1..2aeb121 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -390,6 +390,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0+1" + flutter_pdfview: + dependency: "direct main" + description: + name: flutter_pdfview + sha256: a9055bf920c7095bf08c2781db431ba23577aa5da5a056a7152dc89a18fbec6f + url: "https://pub.dev" + source: hosted + version: "1.3.2" flutter_polyline_points: dependency: "direct main" description: @@ -832,6 +840,46 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: "284a66179cabdf942f838543e10413246f06424d960c92ba95c84439154fcac8" + url: "https://pub.dev" + source: hosted + version: "11.0.1" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: f9fddd3b46109bd69ff3f9efa5006d2d309b7aec0f3c1c5637a60a2d5659e76e + url: "https://pub.dev" + source: hosted + version: "11.1.0" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" + url: "https://pub.dev" + source: hosted + version: "9.1.4" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4" + url: "https://pub.dev" + source: hosted + version: "3.12.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 + url: "https://pub.dev" + source: hosted + version: "0.1.3" petitparser: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 1ac30c7..ef89ec1 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -55,6 +55,8 @@ dependencies: firebase_auth: ^4.9.0 url_launcher: ^6.1.14 flutter_dotenv: ^5.1.0 + flutter_pdfview: ^1.3.2 + permission_handler: ^11.0.1 dev_dependencies: build_runner: null diff --git a/app/windows/flutter/generated_plugin_registrant.cc b/app/windows/flutter/generated_plugin_registrant.cc index 16039c1..2cd4f25 100644 --- a/app/windows/flutter/generated_plugin_registrant.cc +++ b/app/windows/flutter/generated_plugin_registrant.cc @@ -8,6 +8,7 @@ #include #include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { @@ -15,6 +16,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); GeolocatorWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("GeolocatorWindows")); + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/app/windows/flutter/generated_plugins.cmake b/app/windows/flutter/generated_plugins.cmake index e30096f..8ae151b 100644 --- a/app/windows/flutter/generated_plugins.cmake +++ b/app/windows/flutter/generated_plugins.cmake @@ -5,6 +5,7 @@ list(APPEND FLUTTER_PLUGIN_LIST firebase_core geolocator_windows + permission_handler_windows url_launcher_windows ) From d2baf6f912684bbfba7e957be7cfaf8d64cd5f4b Mon Sep 17 00:00:00 2001 From: 12henbx Date: Sat, 21 Oct 2023 22:55:43 +0700 Subject: [PATCH 2/9] create repo for material menu, update bloc code --- app/lib/core/constants/bebrasBookId.dart | 11 + .../presentation/bloc/material_menu_bloc.dart | 34 + .../bloc/material_menu_event.dart | 47 ++ .../bloc/material_menu_state.dart | 23 + .../presentation/modal/materialDocument.dart | 35 + .../menu/presentation/pages/_pages.dart | 4 + .../presentation/pages/material_menu.dart | 655 ++++++++++-------- .../repositories/material_document_repo.dart | 2 +- app/pubspec.yaml | 4 +- 9 files changed, 505 insertions(+), 310 deletions(-) create mode 100644 app/lib/core/constants/bebrasBookId.dart create mode 100644 app/lib/features/material/menu/presentation/bloc/material_menu_bloc.dart create mode 100644 app/lib/features/material/menu/presentation/bloc/material_menu_event.dart create mode 100644 app/lib/features/material/menu/presentation/bloc/material_menu_state.dart create mode 100644 app/lib/features/material/menu/presentation/modal/materialDocument.dart diff --git a/app/lib/core/constants/bebrasBookId.dart b/app/lib/core/constants/bebrasBookId.dart new file mode 100644 index 0000000..2393e47 --- /dev/null +++ b/app/lib/core/constants/bebrasBookId.dart @@ -0,0 +1,11 @@ +List bebrasMaterialIds = [ + "penegak_2016-buku_bebras", + "penegak_2017-buku_bebras", + "penegak_2018-buku_bebras", + "penggalang_2016-buku_bebras", + "penggalang_2017-buku_bebras", + "penggalang_2018-buku_bebras", + "siaga_2016-buku_bebras", + "siaga_2017-buku_bebras", + "siaga_2018-buku_bebras", +]; \ No newline at end of file diff --git a/app/lib/features/material/menu/presentation/bloc/material_menu_bloc.dart b/app/lib/features/material/menu/presentation/bloc/material_menu_bloc.dart new file mode 100644 index 0000000..c5ee065 --- /dev/null +++ b/app/lib/features/material/menu/presentation/bloc/material_menu_bloc.dart @@ -0,0 +1,34 @@ +import 'dart:async'; + +import '../../presentation/repositories/material_document_repo.dart'; + +import 'package:equatable/equatable.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:injectable/injectable.dart'; + +import '../modal/materialDocument.dart'; + +part 'material_menu_event.dart'; +part 'material_menu_state.dart'; + +@injectable +@singleton +class MaterialMenuBloc + extends Bloc { + final MaterialDocumentRepository _materialDocumentRepository; + MaterialMenuBloc(this._materialDocumentRepository) : super(MaterialFileInitialization()) { + on(handleFetchDocument); + } + + FutureOr handleFetchDocument( + FetchMaterialDocumentEvent event, + Emitter emit, + ) async { + var materialDoc = await _materialDocumentRepository.fetchDocument(docId: 'docId'); + emit( + state.copyWith( + materialDocument: MaterialDocument.fromJson(materialDoc), + ), + ); + } +} diff --git a/app/lib/features/material/menu/presentation/bloc/material_menu_event.dart b/app/lib/features/material/menu/presentation/bloc/material_menu_event.dart new file mode 100644 index 0000000..88f0223 --- /dev/null +++ b/app/lib/features/material/menu/presentation/bloc/material_menu_event.dart @@ -0,0 +1,47 @@ +part of 'material_menu_bloc.dart'; + +abstract class MaterialMenuEvent extends Equatable { + const MaterialMenuEvent(); + + @override + List get props => []; +} + +class CheckMaterialFileEvent extends MaterialMenuEvent {} + +class DialogDownloadPermitEvent extends MaterialMenuEvent { + final bool userPermission; + const DialogDownloadPermitEvent(this.userPermission); +} + +class FetchMaterialDocumentEvent extends MaterialMenuEvent { + const FetchMaterialDocumentEvent({ + this.challenge_group, + this.description, + this.file_format, + this.is_printable = true, + this.source_urls, + this.staging_url, + this.title, + this.url, + }); + final String? challenge_group; + final String? description; + final String? file_format; + final bool is_printable; + final List? source_urls; + final String? staging_url; + final String? title; + final String? url; + @override + List get props => [{ + challenge_group, + description, + file_format, + is_printable, + source_urls, + staging_url, + title, + url, + }]; +} diff --git a/app/lib/features/material/menu/presentation/bloc/material_menu_state.dart b/app/lib/features/material/menu/presentation/bloc/material_menu_state.dart new file mode 100644 index 0000000..d0e7275 --- /dev/null +++ b/app/lib/features/material/menu/presentation/bloc/material_menu_state.dart @@ -0,0 +1,23 @@ +part of 'material_menu_bloc.dart'; + +class MaterialMenuState extends Equatable { + final Object? materialDocument; + const MaterialMenuState({ + this.materialDocument = '', + }); + + MaterialMenuState copyWith({ + MaterialDocument? materialDocument, + }) { + return MaterialMenuState( + materialDocument: materialDocument ?? this.materialDocument, + ); + } + + @override + List get props => []; +} + +class MaterialFileInitialization extends MaterialMenuState {} + +class AllMaterialFetchSuccess extends MaterialMenuState {} diff --git a/app/lib/features/material/menu/presentation/modal/materialDocument.dart b/app/lib/features/material/menu/presentation/modal/materialDocument.dart new file mode 100644 index 0000000..aaf9ef1 --- /dev/null +++ b/app/lib/features/material/menu/presentation/modal/materialDocument.dart @@ -0,0 +1,35 @@ +class MaterialDocument { + + final String? challenge_group; + final String? description; + final String? file_format; + final bool is_printable; + final List? source_urls; + final String? staging_url; + final String? title; + final String? url; + + MaterialDocument({ + this.challenge_group, + this.description, + this.file_format, + this.is_printable = false, + this.source_urls, + this.staging_url, + this.title, + this.url + }); + + factory MaterialDocument.fromJson(dynamic json) { + return MaterialDocument( + challenge_group: json.data()['challenge_group'].toString(), + description: json.data()['description'].toString(), + file_format: json.data()['file_format'].toString(), + is_printable: json.data()['is_printable'] as bool, + source_urls: json.data()['source_urls'] as List?, + staging_url: json.data()['staging_url'].toString(), + title: json.data()['title'].toString(), + url: json.data()['url'].toString() + ); + } +} \ No newline at end of file diff --git a/app/lib/features/material/menu/presentation/pages/_pages.dart b/app/lib/features/material/menu/presentation/pages/_pages.dart index 59b355d..c6f0bda 100644 --- a/app/lib/features/material/menu/presentation/pages/_pages.dart +++ b/app/lib/features/material/menu/presentation/pages/_pages.dart @@ -1,10 +1,14 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import '../../../../../core/bases/enum/button_type.dart'; import '../../../../../core/bases/widgets/atoms/button.dart'; import '../../../../../core/bases/widgets/layout/bebras_scaffold.dart'; import '../../../../../core/constants/assets.dart'; +import '../../../../../services/di.dart'; +import '../bloc/material_menu_bloc.dart'; +import '../repositories/material_document_repo.dart'; part 'material_menu.dart'; \ No newline at end of file diff --git a/app/lib/features/material/menu/presentation/pages/material_menu.dart b/app/lib/features/material/menu/presentation/pages/material_menu.dart index 81c3000..f230ef3 100644 --- a/app/lib/features/material/menu/presentation/pages/material_menu.dart +++ b/app/lib/features/material/menu/presentation/pages/material_menu.dart @@ -8,10 +8,23 @@ class MaterialMenu extends StatefulWidget { } class _MaterialMenuState extends State { + late final MaterialDocumentRepository _materialDocumentRepository; + late final MaterialMenuBloc _materialMenuBloc; + + String? selectedValue = null; + + @override + void initState() { + _materialMenuBloc = get(); + _materialMenuBloc.add(); + + super.initState(); + } int value = 0; - Widget CustomRadioButton(String text, int index){ + + Widget CustomRadioButton(String text, int index) { return OutlinedButton( - onPressed: (){ + onPressed: () { setState(() { value = index; }); @@ -21,7 +34,7 @@ class _MaterialMenuState extends State { child: Text( text, style: TextStyle( - color: (value == index) ? Colors.green : Colors.black, + color: (value == index) ? Colors.green : Colors.black, ), ), ); @@ -29,335 +42,361 @@ class _MaterialMenuState extends State { @override Widget build(BuildContext context) { - return BebrasScaffold( - // avoidBottomInset: false, - body: Padding( - padding: const EdgeInsets.only(left: 0.0, top: 10.0, right: 0.0), - child: Stack( - children: [ - Padding( - padding: const EdgeInsets.all(32), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset( - Assets.bebrasPandaiText, - ), - const SizedBox( - height: 30, - ), - const Text('Latihan yang pernah diikuti'), - const SizedBox( - height: 10, - ), - Container( - height: MediaQuery.of(context).size.height - 320, - width: double.infinity, - padding: const EdgeInsets.symmetric(horizontal: 1), - decoration: BoxDecoration(border: Border.all()), - child: ListView( - // padding: const EdgeInsets.all(8), - children: [ - InkWell( - onTap: () { - print('object'); - context.go('/material/9090'); - }, - child: Container( - height: 80, + return BlocProvider( + create: (context) => MaterialMenuBloc(_materialDocumentRepository)..add(FetchMaterialDocumentEvent()), + child: BebrasScaffold( + // avoidBottomInset: false, + body: Padding( + padding: const EdgeInsets.only(left: 0.0, top: 10.0, right: 0.0), + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.all(32), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset( + Assets.bebrasPandaiText, + ), + const SizedBox( + height: 30, + ), + const Text('Latihan yang pernah diikuti'), + const SizedBox( + height: 10, + ), + BlocBuilder( + builder: (BuildContext context, state) { + if(state is AllMaterialFetchSuccess){ + return Container(); + } else { + return Container( + height: MediaQuery.of(context).size.height - 320, width: double.infinity, - padding: const EdgeInsets.symmetric(horizontal: 16), + padding: const EdgeInsets.symmetric(horizontal: 1), decoration: BoxDecoration(border: Border.all()), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Quiz A', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w500), - ), - Text( - 'Skor: 90/100', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), + child: ListView( + // padding: const EdgeInsets.all(8), + children: [ + InkWell( + onTap: () { + print('object'); + context.go('/material/9090'); + }, + child: Container( + height: 80, + width: double.infinity, + padding: + const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration(border: Border.all()), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 10), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Quiz A', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500), + ), + Text( + 'Skor: 90/100', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), + ], + ), + ), ), - ], - ), - ), - ), - ), - Container( - height: 80, - width: double.infinity, - padding: const EdgeInsets.symmetric(horizontal: 16), - decoration: BoxDecoration(border: Border.all()), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Quiz A', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w500), ), - Text( - 'Skor: 90/100', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), + Container( + height: 80, + width: double.infinity, + padding: + const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration(border: Border.all()), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 10), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Quiz A', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500), + ), + Text( + 'Skor: 90/100', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), + ], + ), + ), ), - ], - ), - ), - ), - Container( - height: 80, - width: double.infinity, - padding: const EdgeInsets.symmetric(horizontal: 16), - decoration: BoxDecoration(border: Border.all()), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Quiz A', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w500), + Container( + height: 80, + width: double.infinity, + padding: + const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration(border: Border.all()), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 10), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Quiz A', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500), + ), + Text( + 'Skor: 90/100', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), + ], + ), + ), ), - Text( - 'Skor: 90/100', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), + Container( + height: 80, + width: double.infinity, + padding: + const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration(border: Border.all()), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 10), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Quiz A', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500), + ), + Text( + 'Skor: 90/100', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), + ], + ), + ), ), - ], - ), - ), - ), - Container( - height: 80, - width: double.infinity, - padding: const EdgeInsets.symmetric(horizontal: 16), - decoration: BoxDecoration(border: Border.all()), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Quiz A', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w500), + Container( + height: 80, + width: double.infinity, + padding: + const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration(border: Border.all()), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 10), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Quiz A', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500), + ), + Text( + 'Skor: 90/100', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), + ], + ), + ), ), - Text( - 'Skor: 90/100', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), + Container( + height: 80, + width: double.infinity, + padding: + const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration(border: Border.all()), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 10), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Quiz A', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500), + ), + Text( + 'Skor: 90/100', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), + ], + ), + ), ), ], ), - ), - ), - Container( - height: 80, - width: double.infinity, - padding: const EdgeInsets.symmetric(horizontal: 16), - decoration: BoxDecoration(border: Border.all()), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + ); + } + }, + ), + // Row( + // children: [ + // CustomRadioButton("Single", 1), + // CustomRadioButton("Married", 2), + // CustomRadioButton("Other", 3) + // ], + // ), + // const SizedBox( + // height: 10, + // ), + Container( + height: 70, + width: double.infinity, + decoration: BoxDecoration(border: Border.all()), + child: Column( + children: [ + Row( children: [ - Text( - 'Quiz A', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w500), + Container( + alignment: Alignment.center, + height: 34, + width: 147, + // padding: const EdgeInsets.symmetric(horizontal: 1), + decoration: BoxDecoration( + border: Border.all(), + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black, + blurRadius: 2.0, + spreadRadius: 0.0, + offset: Offset(2.0, + 2.0), // shadow direction: bottom right + ) + ], + ), + child: Text( + 'siKecil', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), ), - Text( - 'Skor: 90/100', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), + Container( + alignment: Alignment.center, + height: 34, + width: 147, + // padding: const EdgeInsets.symmetric(horizontal: 1), + decoration: BoxDecoration( + border: Border.all(), + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black, + blurRadius: 2.0, + spreadRadius: 0.0, + offset: Offset(2.0, + 2.0), // shadow direction: bottom right + ) + ], + ), + child: Text( + 'Siaga', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), ), ], ), - ), - ), - Container( - height: 80, - width: double.infinity, - padding: const EdgeInsets.symmetric(horizontal: 16), - decoration: BoxDecoration(border: Border.all()), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + Row( children: [ - Text( - 'Quiz A', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w500), + Container( + alignment: Alignment.center, + height: 34, + width: 147, + // padding: const EdgeInsets.symmetric(horizontal: 1), + decoration: BoxDecoration( + border: Border.all(), + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black, + blurRadius: 2.0, + spreadRadius: 0.0, + offset: Offset(2.0, + 2.0), // shadow direction: bottom right + ) + ], + ), + child: Text( + 'Penegak', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), ), - Text( - 'Skor: 90/100', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), + Container( + alignment: Alignment.center, + height: 34, + width: 147, + // padding: const EdgeInsets.symmetric(horizontal: 1), + decoration: BoxDecoration( + border: Border.all(), + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black, + blurRadius: 2.0, + spreadRadius: 0.0, + offset: Offset(2.0, + 2.0), // shadow direction: bottom right + ) + ], + ), + child: Text( + 'Penggalang', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400), + ), ), ], ), - ), - ), - ], - ), - ), - // Row( - // children: [ - // CustomRadioButton("Single", 1), - // CustomRadioButton("Married", 2), - // CustomRadioButton("Other", 3) - // ], - // ), - // const SizedBox( - // height: 10, - // ), - Container( - height: 70, - width: double.infinity, - decoration: BoxDecoration(border: Border.all()), - child: Column( - children: [ - Row( - children: [ - Container( - alignment: Alignment.center, - height: 34, - width: 147, - // padding: const EdgeInsets.symmetric(horizontal: 1), - decoration: BoxDecoration( - border: Border.all(), - color: Colors.white, - boxShadow: [ - BoxShadow( - color: Colors.black, - blurRadius: 2.0, - spreadRadius: 0.0, - offset: Offset(2.0, 2.0), // shadow direction: bottom right - ) - ], - ), - child: Text( - 'siKecil', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), - ), - ), - Container( - alignment: Alignment.center, - height: 34, - width: 147, - // padding: const EdgeInsets.symmetric(horizontal: 1), - decoration: BoxDecoration( - border: Border.all(), - color: Colors.white, - boxShadow: [ - BoxShadow( - color: Colors.black, - blurRadius: 2.0, - spreadRadius: 0.0, - offset: Offset(2.0, 2.0), // shadow direction: bottom right - ) - ], - ), - child: Text( - 'Siaga', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), - ), - ), ], ), - Row( - children: [ - Container( - alignment: Alignment.center, - height: 34, - width: 147, - // padding: const EdgeInsets.symmetric(horizontal: 1), - decoration: BoxDecoration( - border: Border.all(), - color: Colors.white, - boxShadow: [ - BoxShadow( - color: Colors.black, - blurRadius: 2.0, - spreadRadius: 0.0, - offset: Offset(2.0, 2.0), // shadow direction: bottom right - ) - ], - ), - child: Text( - 'Penegak', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), - ), - ), - Container( - alignment: Alignment.center, - height: 34, - width: 147, - // padding: const EdgeInsets.symmetric(horizontal: 1), - decoration: BoxDecoration( - border: Border.all(), - color: Colors.white, - boxShadow: [ - BoxShadow( - color: Colors.black, - blurRadius: 2.0, - spreadRadius: 0.0, - offset: Offset(2.0, 2.0), // shadow direction: bottom right - ) - ], - ), - child: Text( - 'Penggalang', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), - ), - ), - ], - ), - ], - ), + ), + ], ), - ], - ), + ), + ], ), - ], - ), - ), - ); + ), + )); } } diff --git a/app/lib/features/material/menu/presentation/repositories/material_document_repo.dart b/app/lib/features/material/menu/presentation/repositories/material_document_repo.dart index ca65c03..020e05a 100644 --- a/app/lib/features/material/menu/presentation/repositories/material_document_repo.dart +++ b/app/lib/features/material/menu/presentation/repositories/material_document_repo.dart @@ -5,7 +5,7 @@ import 'package:injectable/injectable.dart'; import '../modal/materialDocument.dart'; @Injectable() -class RegisterUserRepository { +class MaterialDocumentRepository { final _firecloud = FirebaseFirestore.instance.collection('materials'); Future fetchDocument({ diff --git a/app/pubspec.yaml b/app/pubspec.yaml index ef89ec1..ed1e33e 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -51,12 +51,14 @@ dependencies: auto_size_text: ^3.0.0 flutter_polyline_points: ^1.0.0 google_sign_in: ^6.1.4 - firebase_core: ^2.15.1 + firebase_core: ^2.17.0 firebase_auth: ^4.9.0 url_launcher: ^6.1.14 flutter_dotenv: ^5.1.0 flutter_pdfview: ^1.3.2 permission_handler: ^11.0.1 + cloud_firestore: ^4.9.3 + dropdown_search: ^5.0.6 dev_dependencies: build_runner: null From bad6c796894ed089c5cefca5d86bb155093bfa33 Mon Sep 17 00:00:00 2001 From: 12henbx Date: Sun, 22 Oct 2023 20:33:12 +0700 Subject: [PATCH 3/9] fix material menu page and pdf viewer page, fix download pdf function --- app/android/app/src/main/AndroidManifest.xml | 5 +- .../core/constants/BebrasGroupCategory.dart | 13 + .../presentation/bloc/material_menu_bloc.dart | 2 +- .../menu/presentation/pages/_pages.dart | 8 +- .../presentation/pages/material_menu.dart | 495 +++++------------- .../repositories/material_document_repo.dart | 29 - .../viewer/presentation/pages/_pages.dart | 1 + .../presentation/pages/pdf_viewer_page.dart | 225 ++------ app/lib/services/router_service.dart | 9 +- .../Flutter/GeneratedPluginRegistrant.swift | 2 + app/pubspec.lock | 64 ++- app/pubspec.yaml | 5 + .../flutter/generated_plugin_registrant.cc | 6 + app/windows/flutter/generated_plugins.cmake | 2 + 14 files changed, 280 insertions(+), 586 deletions(-) create mode 100644 app/lib/core/constants/BebrasGroupCategory.dart delete mode 100644 app/lib/features/material/menu/presentation/repositories/material_document_repo.dart diff --git a/app/android/app/src/main/AndroidManifest.xml b/app/android/app/src/main/AndroidManifest.xml index 506b1a7..5d8c11f 100644 --- a/app/android/app/src/main/AndroidManifest.xml +++ b/app/android/app/src/main/AndroidManifest.xml @@ -1,10 +1,13 @@ + + + android:icon="@mipmap/ic_launcher" + android:requestLegacyExternalStorage="true"> bebrasGroupList = [ + BebrasGroupCategory(0, 'sikecil'), + BebrasGroupCategory(1, 'siaga'), + BebrasGroupCategory(2, 'penggalang'), + BebrasGroupCategory(3, 'penegak'), +]; \ No newline at end of file diff --git a/app/lib/features/material/menu/presentation/bloc/material_menu_bloc.dart b/app/lib/features/material/menu/presentation/bloc/material_menu_bloc.dart index c5ee065..271df44 100644 --- a/app/lib/features/material/menu/presentation/bloc/material_menu_bloc.dart +++ b/app/lib/features/material/menu/presentation/bloc/material_menu_bloc.dart @@ -24,7 +24,7 @@ class MaterialMenuBloc FetchMaterialDocumentEvent event, Emitter emit, ) async { - var materialDoc = await _materialDocumentRepository.fetchDocument(docId: 'docId'); + var materialDoc = await _materialDocumentRepository.fetchDocument(docId: ''); emit( state.copyWith( materialDocument: MaterialDocument.fromJson(materialDoc), diff --git a/app/lib/features/material/menu/presentation/pages/_pages.dart b/app/lib/features/material/menu/presentation/pages/_pages.dart index c6f0bda..59cf028 100644 --- a/app/lib/features/material/menu/presentation/pages/_pages.dart +++ b/app/lib/features/material/menu/presentation/pages/_pages.dart @@ -1,14 +1,10 @@ +import 'package:bebras_pandai/core/constants/BebrasGroupCategory.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; -import '../../../../../core/bases/enum/button_type.dart'; -import '../../../../../core/bases/widgets/atoms/button.dart'; import '../../../../../core/bases/widgets/layout/bebras_scaffold.dart'; import '../../../../../core/constants/assets.dart'; -import '../../../../../services/di.dart'; -import '../bloc/material_menu_bloc.dart'; -import '../repositories/material_document_repo.dart'; part 'material_menu.dart'; \ No newline at end of file diff --git a/app/lib/features/material/menu/presentation/pages/material_menu.dart b/app/lib/features/material/menu/presentation/pages/material_menu.dart index f230ef3..e59dc63 100644 --- a/app/lib/features/material/menu/presentation/pages/material_menu.dart +++ b/app/lib/features/material/menu/presentation/pages/material_menu.dart @@ -8,33 +8,44 @@ class MaterialMenu extends StatefulWidget { } class _MaterialMenuState extends State { - late final MaterialDocumentRepository _materialDocumentRepository; - late final MaterialMenuBloc _materialMenuBloc; + final Stream materialsStream = + FirebaseFirestore.instance.collection('learning_material').snapshots(); String? selectedValue = null; - @override - void initState() { - _materialMenuBloc = get(); - _materialMenuBloc.add(); - - super.initState(); - } - int value = 0; + int filterIndex = 0; Widget CustomRadioButton(String text, int index) { - return OutlinedButton( - onPressed: () { + return InkWell( + onTap: () { setState(() { - value = index; + filterIndex = index; }); }, - // shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - // borderSide: BorderSide(color: (value == index) ? Colors.green : Colors.black), - child: Text( - text, - style: TextStyle( - color: (value == index) ? Colors.green : Colors.black, + child: Container( + alignment: Alignment.center, + height: 34, + width: 147, + decoration: BoxDecoration( + border: Border.all(), + color: (filterIndex == index) ? Colors.black54 : Colors.white, + boxShadow: [ + BoxShadow( + color: (filterIndex == index) ? Colors.white : Colors.black, + blurRadius: 2.0, + spreadRadius: 0.0, + offset: (filterIndex == index) + ? Offset(0, 0) + : Offset(2.0, 2.0), // shadow direction: bottom right + ) + ], + ), + child: Text( + text, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: (filterIndex == index) ? Colors.white : Colors.black), ), ), ); @@ -42,361 +53,139 @@ class _MaterialMenuState extends State { @override Widget build(BuildContext context) { - return BlocProvider( - create: (context) => MaterialMenuBloc(_materialDocumentRepository)..add(FetchMaterialDocumentEvent()), - child: BebrasScaffold( - // avoidBottomInset: false, - body: Padding( - padding: const EdgeInsets.only(left: 0.0, top: 10.0, right: 0.0), - child: Stack( - children: [ - Padding( - padding: const EdgeInsets.all(32), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset( - Assets.bebrasPandaiText, - ), - const SizedBox( - height: 30, - ), - const Text('Latihan yang pernah diikuti'), - const SizedBox( - height: 10, - ), - BlocBuilder( - builder: (BuildContext context, state) { - if(state is AllMaterialFetchSuccess){ - return Container(); - } else { - return Container( - height: MediaQuery.of(context).size.height - 320, - width: double.infinity, - padding: const EdgeInsets.symmetric(horizontal: 1), - decoration: BoxDecoration(border: Border.all()), - child: ListView( - // padding: const EdgeInsets.all(8), - children: [ - InkWell( + return BebrasScaffold( + avoidBottomInset: false, + body: Padding( + padding: const EdgeInsets.only(top: 10.0), + child: Stack( + children: [ + Container( + padding: const EdgeInsets.all(32), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset( + Assets.bebrasPandaiText, + ), + const SizedBox( + height: 30, + ), + const Text('Latihan yang pernah diikuti'), + const SizedBox( + height: 10, + ), + StreamBuilder( + stream: materialsStream, + builder: (BuildContext context, + AsyncSnapshot snapshot) { + if (snapshot.hasError) { + return Text('Something went wrong'); + } + + if (snapshot.connectionState == + ConnectionState.waiting) { + return Text("Loading"); + } + + return Container( + height: 360, + decoration: BoxDecoration(border: Border.all()), + child: ListView( + children: snapshot.data!.docs + .map((DocumentSnapshot document) { + Map materialDoc = + document.data()! as Map; + if (materialDoc['challenge_group'] == + bebrasGroupList[filterIndex].bebrasChallengeKey) { + return InkWell( onTap: () { - print('object'); - context.go('/material/9090'); + context.push(Uri( + path: '/material/${document.id}', + queryParameters: { + 'id': document.id, + 'title': materialDoc['title'], + 'description': + materialDoc['description'], + 'pdfUrl': materialDoc['url'], + }).toString()); }, child: Container( height: 80, width: double.infinity, - padding: - const EdgeInsets.symmetric(horizontal: 16), - decoration: BoxDecoration(border: Border.all()), + padding: const EdgeInsets.symmetric( + horizontal: 16), + decoration: + BoxDecoration(border: Border.all()), child: Padding( padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 10), + horizontal: 7, vertical: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - 'Quiz A', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w500), + Container( + width: 140, + child: Text( + materialDoc['title'].toString(), + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600), + ), ), - Text( - 'Skor: 90/100', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), + Container( + width: 60, + child: Text( + 'Terakhir dilihat: 15/09', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400), + ), ), ], ), ), ), - ), - Container( - height: 80, - width: double.infinity, - padding: - const EdgeInsets.symmetric(horizontal: 16), - decoration: BoxDecoration(border: Border.all()), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 10), - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Quiz A', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w500), - ), - Text( - 'Skor: 90/100', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), - ), - ], - ), - ), - ), - Container( - height: 80, - width: double.infinity, - padding: - const EdgeInsets.symmetric(horizontal: 16), - decoration: BoxDecoration(border: Border.all()), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 10), - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Quiz A', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w500), - ), - Text( - 'Skor: 90/100', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), - ), - ], - ), - ), - ), - Container( - height: 80, - width: double.infinity, - padding: - const EdgeInsets.symmetric(horizontal: 16), - decoration: BoxDecoration(border: Border.all()), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 10), - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Quiz A', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w500), - ), - Text( - 'Skor: 90/100', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), - ), - ], - ), - ), - ), - Container( - height: 80, - width: double.infinity, - padding: - const EdgeInsets.symmetric(horizontal: 16), - decoration: BoxDecoration(border: Border.all()), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 10), - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Quiz A', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w500), - ), - Text( - 'Skor: 90/100', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), - ), - ], - ), - ), - ), - Container( - height: 80, - width: double.infinity, - padding: - const EdgeInsets.symmetric(horizontal: 16), - decoration: BoxDecoration(border: Border.all()), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 10), - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Quiz A', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.w500), - ), - Text( - 'Skor: 90/100', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), - ), - ], - ), - ), - ), - ], - ), - ); - } - }, - ), - // Row( - // children: [ - // CustomRadioButton("Single", 1), - // CustomRadioButton("Married", 2), - // CustomRadioButton("Other", 3) - // ], - // ), - // const SizedBox( - // height: 10, - // ), - Container( - height: 70, - width: double.infinity, - decoration: BoxDecoration(border: Border.all()), - child: Column( + ); + } + return Container(); + }).toList(), + ), + ); + }), + const SizedBox( + height: 10, + ), + Container( + height: 70, + width: double.infinity, + decoration: BoxDecoration(border: Border.all()), + child: Column( + children: [ + Row( children: [ - Row( - children: [ - Container( - alignment: Alignment.center, - height: 34, - width: 147, - // padding: const EdgeInsets.symmetric(horizontal: 1), - decoration: BoxDecoration( - border: Border.all(), - color: Colors.white, - boxShadow: [ - BoxShadow( - color: Colors.black, - blurRadius: 2.0, - spreadRadius: 0.0, - offset: Offset(2.0, - 2.0), // shadow direction: bottom right - ) - ], - ), - child: Text( - 'siKecil', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), - ), - ), - Container( - alignment: Alignment.center, - height: 34, - width: 147, - // padding: const EdgeInsets.symmetric(horizontal: 1), - decoration: BoxDecoration( - border: Border.all(), - color: Colors.white, - boxShadow: [ - BoxShadow( - color: Colors.black, - blurRadius: 2.0, - spreadRadius: 0.0, - offset: Offset(2.0, - 2.0), // shadow direction: bottom right - ) - ], - ), - child: Text( - 'Siaga', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), - ), - ), - ], - ), - Row( - children: [ - Container( - alignment: Alignment.center, - height: 34, - width: 147, - // padding: const EdgeInsets.symmetric(horizontal: 1), - decoration: BoxDecoration( - border: Border.all(), - color: Colors.white, - boxShadow: [ - BoxShadow( - color: Colors.black, - blurRadius: 2.0, - spreadRadius: 0.0, - offset: Offset(2.0, - 2.0), // shadow direction: bottom right - ) - ], - ), - child: Text( - 'Penegak', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), - ), - ), - Container( - alignment: Alignment.center, - height: 34, - width: 147, - // padding: const EdgeInsets.symmetric(horizontal: 1), - decoration: BoxDecoration( - border: Border.all(), - color: Colors.white, - boxShadow: [ - BoxShadow( - color: Colors.black, - blurRadius: 2.0, - spreadRadius: 0.0, - offset: Offset(2.0, - 2.0), // shadow direction: bottom right - ) - ], - ), - child: Text( - 'Penggalang', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400), - ), - ), - ], - ), + CustomRadioButton( + "siKecil", bebrasGroupList[0].index), + CustomRadioButton( + "Siaga", bebrasGroupList[1].index), + ], + ), + Row( + children: [ + CustomRadioButton( + "Penggalang", bebrasGroupList[2].index), + CustomRadioButton( + "Penegak", bebrasGroupList[3].index), ], ), - ), - ], + ], + ), ), - ), - ], + ], + ), ), - ), - )); + ], + ), + ), + ); } } diff --git a/app/lib/features/material/menu/presentation/repositories/material_document_repo.dart b/app/lib/features/material/menu/presentation/repositories/material_document_repo.dart deleted file mode 100644 index 020e05a..0000000 --- a/app/lib/features/material/menu/presentation/repositories/material_document_repo.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter/foundation.dart'; -import 'package:injectable/injectable.dart'; - -import '../modal/materialDocument.dart'; - -@Injectable() -class MaterialDocumentRepository { - final _firecloud = FirebaseFirestore.instance.collection('materials'); - - Future fetchDocument({ - required String docId, - }) async { - try { - return await _firecloud.doc(docId) - .get() - .then((DocumentSnapshot documentSnapshot) { - return MaterialDocument.fromJson(documentSnapshot.data()); - }); - } on FirebaseException catch (e) { - if (kDebugMode) { - print("Failed with error '${e.code}': '${e.message}'"); - } - } catch (e) { - throw Exception(e.toString()); - } - return null; - } -} \ No newline at end of file diff --git a/app/lib/features/material/viewer/presentation/pages/_pages.dart b/app/lib/features/material/viewer/presentation/pages/_pages.dart index c9fb029..16779e7 100644 --- a/app/lib/features/material/viewer/presentation/pages/_pages.dart +++ b/app/lib/features/material/viewer/presentation/pages/_pages.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'package:dio/dio.dart'; +import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; diff --git a/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart b/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart index 2998f46..4681ace 100644 --- a/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart +++ b/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart @@ -1,13 +1,17 @@ part of '_pages.dart'; class PdfViewerPage extends StatefulWidget { - final File? file; - final String url; + final String? pdfUrl; + final String? id; + final String? title; + final String? description; const PdfViewerPage({ Key? key, - this.file, - required this.url, + required this.pdfUrl, + this.id, + this.title, + this.description, }) : super(key: key); @override @@ -15,175 +19,55 @@ class PdfViewerPage extends StatefulWidget { } class _PdfViewerPageState extends State { - late final MaterialViewerBloc _materialViewerBloc; + String basePath = "/storage/emulated/0/Android/data/com.toki.bebras_pandai/files/PDF_Download/"; String pathPDF = ""; - String landscapePathPdf = ""; - String remotePDFpath = ""; - String corruptedPathPDF = ""; @override void initState() { super.initState(); - _materialViewerBloc = get(); - print('oiuvh'); - - // Fetch from local storage - // fromAsset('assets/demo-link.pdf', 'demo.pdf').then((f) { - // setState(() { - // pathPDF = f.path; - // }); - // }); - - // Trigger to fetch from URL - // createFileOfPdfUrl().then((f) { - // setState(() { - // remotePDFpath = f.path; - // }); - // }); - } - - Future createFileOfPdfUrl() async { - Completer completer = Completer(); - print("Start download file from internet!"); - try { - // "https://berlin2017.droidcon.cod.newthinking.net/sites/global.droidcon.cod.newthinking.net/files/media/documents/Flutter%20-%2060FPS%20UI%20of%20the%20future%20%20-%20DroidconDE%2017.pdf"; - // final url = "https://pdfkit.org/docs/guide.pdf"; - final url = "http://www.pdf995.com/samples/pdf.pdf"; - final filename = url.substring(url.lastIndexOf("/") + 1); - var request = await HttpClient().getUrl(Uri.parse(url)); - var response = await request.close(); - var bytes = await consolidateHttpClientResponseBytes(response); - var dir = await getApplicationDocumentsDirectory(); - print("Download files"); - print("${dir.path}/$filename"); - File file = File("${dir.path}/$filename"); - - await file.writeAsBytes(bytes, flush: true); - completer.complete(file); - } catch (e) { - throw Exception('Error parsing asset file!'); - } - - return completer.future; - } - - showAlertDialog(BuildContext context) { - Widget cancelButton = ElevatedButton( - child: Text("Tidak"), - onPressed: () {}, - ); - Widget continueButton = ElevatedButton( - child: Text("Ya"), - onPressed: () { - _materialViewerBloc.add( - DialogDownloadPermitEvent(true), - ); - }, - ); // set up the AlertDialog - AlertDialog alert = AlertDialog( - title: Text("AlertDialog"), - content: Text("File materi tidak ditemukan, ingin mengunduh?"), - actions: [ - cancelButton, - continueButton, - ], - ); // show the dialog - showDialog( - context: context, - builder: (BuildContext context) { - return alert; - }, - ); + WidgetsBinding.instance.addPostFrameCallback((_){ + saveFile(widget.pdfUrl.toString(), "${widget.id}.pdf"); + }); } @override Widget build(BuildContext context) { - // final name = basename(widget.file.path); - return BlocProvider( - create: (context) => MaterialViewerBloc()..add(CheckMaterialFileEvent()), - child: Scaffold( - appBar: AppBar( - backgroundColor: Color(0xffe6f6ff), - title: Text( - 'name', // name - style: TextStyle(color: Colors.black), - ), - actions: [ - IconButton( - onPressed: () async { - await saveFile(widget.url, "sample.pdf"); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text( - 'successfully saved to internal storage "PDF_Download" folder', - style: TextStyle(color: Colors.white), - ), - ), - ); - }, - icon: const Icon(Icons.download_rounded), - ), - ], - ), - body: Stack( - children: [ - BlocBuilder( - builder: (BuildContext context, state) { - String filePath = ''; - print('test2'); - print(state.toString()); - if(state is MaterialFileExist) { - filePath = '/data/user/0/com.example.bebras.bebras_app_ui/app_flutter/pdfAcademy.pdf'; - } else if (state is MaterialFileDoesNotExist){ - Future.delayed(Duration.zero, () async { - showAlertDialog(context); - }); - // _materialViewerBloc.add( - // DialogDownloadPermitEvent(true), - // ); - // setState(() { - // // remotePDFpath = f.path; - // }); - filePath = '/'; - } else if (state is MaterialFileDownloaded) { - return PDFView( - filePath: filePath, - onError: (error) { - print(error.toString()); - }, - onPageError: (page, error) { - print('$page: ${error.toString()}'); - }, - // onViewCreated: (PDFViewController pdfViewController) { - // _controller.complete(pdfViewController); - // }, - // onPageChanged: (int page, int total) { - // print('page change: $page/$total'); - // }, - ); - // saveFile(); - } else if (state is UserDisagreeToDownload) { - - } - return Container(); - // PDFView( - // filePath: filePath, - // onError: (error) { - // print(error.toString()); - // }, - // onPageError: (page, error) { - // print('$page: ${error.toString()}'); - // }, - // // onViewCreated: (PDFViewController pdfViewController) { - // // _controller.complete(pdfViewController); - // // }, - // // onPageChanged: (int page, int total) { - // // print('page change: $page/$total'); - // // }, - // ); - }), - ], + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.black54, + title: Text( + widget.title.toString(), // name + style: TextStyle(color: Colors.white), ), + actions: [ + IconButton( + onPressed: () async { + await saveFile(widget.pdfUrl.toString(), "${widget.id}.pdf"); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'successfully saved to internal storage', + style: TextStyle(color: Colors.white), + ), + ), + ); + }, + icon: const Icon(Icons.download_rounded), + ), + ], + ), + body: Stack( + children: [ + PDFView( + filePath: basePath + widget.id.toString() + '.pdf', + onError: (error) { + print(error.toString()); + }, + onPageError: (page, error) { + print('$page: ${error.toString()}'); + }, + ), + ], ), ); } @@ -194,16 +78,7 @@ class _PdfViewerPageState extends State { Directory? directory; directory = await getExternalStorageDirectory(); String newPath = ""; - List paths = directory!.path.split("/"); - for (int x = 1; x < paths.length; x++) { - String folder = paths[x]; - if (folder != "Android") { - newPath += "/" + folder; - } else { - break; - } - } - newPath = newPath + "/PDF_Download"; + newPath = directory!.path + "/PDF_Download"; directory = Directory(newPath); File saveFile = File(directory.path + "/$fileName"); @@ -218,6 +93,10 @@ class _PdfViewerPageState extends State { url, saveFile.path, ); + setState(() { + pathPDF = saveFile.path; + }); + print(pathPDF); } } return true; diff --git a/app/lib/services/router_service.dart b/app/lib/services/router_service.dart index debd9e7..4e04468 100644 --- a/app/lib/services/router_service.dart +++ b/app/lib/services/router_service.dart @@ -13,8 +13,7 @@ GoRouter router = GoRouter( routes: [ GoRoute( path: '/', - builder: (context, state) => const MaterialMenu(), - // builder: (context, state) => const SplashScreen(), + builder: (context, state) => const SplashScreen(), ), GoRoute( path: '/onboarding', @@ -42,7 +41,11 @@ GoRouter router = GoRouter( ), GoRoute( path: '/material/:id', - builder: (context, state) => const PdfViewerPage(file: null, url: '/abc'), + builder: (context, state) => PdfViewerPage( + pdfUrl: state.queryParameters['pdfUrl'], + title: state.queryParameters['title'], + id: state.queryParameters['id'], + ), ), ], ); diff --git a/app/macos/Flutter/GeneratedPluginRegistrant.swift b/app/macos/Flutter/GeneratedPluginRegistrant.swift index 2c93bec..9142ca0 100644 --- a/app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,6 +8,7 @@ import Foundation import cloud_firestore import firebase_auth import firebase_core +import firebase_storage import flutter_local_notifications import geolocator_apple import path_provider_foundation @@ -18,6 +19,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTFirebaseFirestorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseFirestorePlugin")) FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) + FLTFirebaseStoragePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseStoragePlugin")) FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) diff --git a/app/pubspec.lock b/app/pubspec.lock index e7d1b63..6758e74 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: d84d98f1992976775f83083523a34c5d22fea191eec3abb2bd09537fb623c2e0 + sha256: "76c15c4167f820b74abcd8c6fc5e393e1ed5e1207a34e9b22953603e03b3ba6a" url: "https://pub.dev" source: hosted - version: "1.3.7" + version: "1.3.9" analyzer: dependency: transitive description: @@ -173,26 +173,26 @@ packages: dependency: "direct main" description: name: cloud_firestore - sha256: "1179ae4c69e2ea18179d844d70fc6ed2f082a2bbeb7fa62d35a2a24e2992bd4d" + sha256: "019da47850fe2c86266a4b86e82e0d00dcb5bf0718f26fc70bf9df275ee16537" url: "https://pub.dev" source: hosted - version: "4.9.3" + version: "4.11.0" cloud_firestore_platform_interface: dependency: transitive description: name: cloud_firestore_platform_interface - sha256: acdcf0743bbdd0e6b342f3d2033e15d260a2c6f9434dd34b008b8f1c35e62b23 + sha256: "7e08cd322ff98ddfdb6ab8a89b24f895c473992d674bfd3b36865037879baa3f" url: "https://pub.dev" source: hosted - version: "5.16.2" + version: "6.0.1" cloud_firestore_web: dependency: transitive description: name: cloud_firestore_web - sha256: "321bb0732c8d782a49aede96805e59609e05cf98b6c34370faa04103f46a4a3a" + sha256: bd2183128b67d66a6e672c50e47bc70393418f3bd669aad8ac321e88f027b2b5 url: "https://pub.dev" source: hosted - version: "3.7.2" + version: "3.8.1" code_builder: dependency: transitive description: @@ -309,50 +309,74 @@ packages: dependency: "direct main" description: name: firebase_auth - sha256: "6d9be853426ab686d68076b8007ac29b2c31e7d549444a45b5c3fe1abc249fb0" + sha256: "46129e2733336ac77377174c3ca33a68cca2cd5848504aad63028aeb92afb7b2" url: "https://pub.dev" source: hosted - version: "4.9.0" + version: "4.11.1" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface - sha256: "2946cfdc17f925fa9771dd0ba3ce9dd2d019100a8685d0557c161f7786ea9b14" + sha256: b89936896b2cc02496b97e486793fd4bcf8c51beb99d6a7223c0eea2352d404e url: "https://pub.dev" source: hosted - version: "6.18.0" + version: "7.0.1" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - sha256: d8972d754702a3f4881184706b8056e2837d0dae91613a43b988c960b8e0d988 + sha256: "88b7655c9394d723e121fd53f115d876c32260b8c8b499bdc3108341044a8306" url: "https://pub.dev" source: hosted - version: "5.8.0" + version: "5.8.4" firebase_core: dependency: "direct main" description: name: firebase_core - sha256: "95580fa07c8ca3072a2bb1fecd792616a33f8683477d25b7d29d3a6a399e6ece" + sha256: "57bba167105d2315d243a4524939406df688f38a5b6d7a4159382bbbe43cdd00" url: "https://pub.dev" source: hosted - version: "2.17.0" + version: "2.19.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: b63e3be6c96ef5c33bdec1aab23c91eb00696f6452f0519401d640938c94cba2 + sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63 url: "https://pub.dev" source: hosted - version: "4.8.0" + version: "5.0.0" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: e8c408923cd3a25bd342c576a114f2126769cd1a57106a4edeaa67ea4a84e962 + sha256: "0631a2ec971dbc540275e2fa00c3a8a2676f0a7adbc3c197d6fba569db689d97" url: "https://pub.dev" source: hosted - version: "2.8.0" + version: "2.8.1" + firebase_storage: + dependency: "direct main" + description: + name: firebase_storage + sha256: c23dfab4fbdafd014b579e2bcc5b23ca69457d9b8625a64dffb669d098cff6fe + url: "https://pub.dev" + source: hosted + version: "11.3.1" + firebase_storage_platform_interface: + dependency: transitive + description: + name: firebase_storage_platform_interface + sha256: "8650ba68840122f0c7e8a1a529d16580a5d00a614653d7167d7cd044c67fa245" + url: "https://pub.dev" + source: hosted + version: "4.4.9" + firebase_storage_web: + dependency: transitive + description: + name: firebase_storage_web + sha256: "93ac56b2a9722a94e0d752a4aa2dd5ec8c4ceeb8ff07121bb6580198dbede199" + url: "https://pub.dev" + source: hosted + version: "3.6.10" fixnum: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index ed1e33e..9bdd420 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -59,6 +59,7 @@ dependencies: permission_handler: ^11.0.1 cloud_firestore: ^4.9.3 dropdown_search: ^5.0.6 + firebase_storage: ^11.3.1 dev_dependencies: build_runner: null @@ -73,10 +74,14 @@ dev_dependencies: # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec +permission: + git: + url: https://github.com/kamlesh9070/permission # The following section is specific to Flutter packages. flutter: + # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. diff --git a/app/windows/flutter/generated_plugin_registrant.cc b/app/windows/flutter/generated_plugin_registrant.cc index 2cd4f25..8c7ce19 100644 --- a/app/windows/flutter/generated_plugin_registrant.cc +++ b/app/windows/flutter/generated_plugin_registrant.cc @@ -6,12 +6,18 @@ #include "generated_plugin_registrant.h" +#include +#include #include #include #include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + CloudFirestorePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("CloudFirestorePluginCApi")); + FirebaseAuthPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseAuthPluginCApi")); FirebaseCorePluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); GeolocatorWindowsRegisterWithRegistrar( diff --git a/app/windows/flutter/generated_plugins.cmake b/app/windows/flutter/generated_plugins.cmake index 8ae151b..28b7120 100644 --- a/app/windows/flutter/generated_plugins.cmake +++ b/app/windows/flutter/generated_plugins.cmake @@ -3,6 +3,8 @@ # list(APPEND FLUTTER_PLUGIN_LIST + cloud_firestore + firebase_auth firebase_core geolocator_windows permission_handler_windows From 82b52257d96343d18c1197dbcf6af836d9d5855d Mon Sep 17 00:00:00 2001 From: 12henbx Date: Sun, 22 Oct 2023 23:52:29 +0700 Subject: [PATCH 4/9] delete unused code --- .../presentation/bloc/material_menu_bloc.dart | 34 ------ .../bloc/material_menu_event.dart | 47 -------- .../bloc/material_menu_state.dart | 23 ---- .../presentation/modal/materialDocument.dart | 35 ------ .../bloc/material_viewer_bloc.dart | 112 ------------------ .../bloc/material_viewer_event.dart | 15 --- .../bloc/material_viewer_state.dart | 23 ---- .../viewer/presentation/pages/_pages.dart | 6 - .../presentation/pages/pdf_viewer_page.dart | 4 +- 9 files changed, 3 insertions(+), 296 deletions(-) delete mode 100644 app/lib/features/material/menu/presentation/bloc/material_menu_bloc.dart delete mode 100644 app/lib/features/material/menu/presentation/bloc/material_menu_event.dart delete mode 100644 app/lib/features/material/menu/presentation/bloc/material_menu_state.dart delete mode 100644 app/lib/features/material/menu/presentation/modal/materialDocument.dart delete mode 100644 app/lib/features/material/viewer/presentation/bloc/material_viewer_bloc.dart delete mode 100644 app/lib/features/material/viewer/presentation/bloc/material_viewer_event.dart delete mode 100644 app/lib/features/material/viewer/presentation/bloc/material_viewer_state.dart diff --git a/app/lib/features/material/menu/presentation/bloc/material_menu_bloc.dart b/app/lib/features/material/menu/presentation/bloc/material_menu_bloc.dart deleted file mode 100644 index 271df44..0000000 --- a/app/lib/features/material/menu/presentation/bloc/material_menu_bloc.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'dart:async'; - -import '../../presentation/repositories/material_document_repo.dart'; - -import 'package:equatable/equatable.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:injectable/injectable.dart'; - -import '../modal/materialDocument.dart'; - -part 'material_menu_event.dart'; -part 'material_menu_state.dart'; - -@injectable -@singleton -class MaterialMenuBloc - extends Bloc { - final MaterialDocumentRepository _materialDocumentRepository; - MaterialMenuBloc(this._materialDocumentRepository) : super(MaterialFileInitialization()) { - on(handleFetchDocument); - } - - FutureOr handleFetchDocument( - FetchMaterialDocumentEvent event, - Emitter emit, - ) async { - var materialDoc = await _materialDocumentRepository.fetchDocument(docId: ''); - emit( - state.copyWith( - materialDocument: MaterialDocument.fromJson(materialDoc), - ), - ); - } -} diff --git a/app/lib/features/material/menu/presentation/bloc/material_menu_event.dart b/app/lib/features/material/menu/presentation/bloc/material_menu_event.dart deleted file mode 100644 index 88f0223..0000000 --- a/app/lib/features/material/menu/presentation/bloc/material_menu_event.dart +++ /dev/null @@ -1,47 +0,0 @@ -part of 'material_menu_bloc.dart'; - -abstract class MaterialMenuEvent extends Equatable { - const MaterialMenuEvent(); - - @override - List get props => []; -} - -class CheckMaterialFileEvent extends MaterialMenuEvent {} - -class DialogDownloadPermitEvent extends MaterialMenuEvent { - final bool userPermission; - const DialogDownloadPermitEvent(this.userPermission); -} - -class FetchMaterialDocumentEvent extends MaterialMenuEvent { - const FetchMaterialDocumentEvent({ - this.challenge_group, - this.description, - this.file_format, - this.is_printable = true, - this.source_urls, - this.staging_url, - this.title, - this.url, - }); - final String? challenge_group; - final String? description; - final String? file_format; - final bool is_printable; - final List? source_urls; - final String? staging_url; - final String? title; - final String? url; - @override - List get props => [{ - challenge_group, - description, - file_format, - is_printable, - source_urls, - staging_url, - title, - url, - }]; -} diff --git a/app/lib/features/material/menu/presentation/bloc/material_menu_state.dart b/app/lib/features/material/menu/presentation/bloc/material_menu_state.dart deleted file mode 100644 index d0e7275..0000000 --- a/app/lib/features/material/menu/presentation/bloc/material_menu_state.dart +++ /dev/null @@ -1,23 +0,0 @@ -part of 'material_menu_bloc.dart'; - -class MaterialMenuState extends Equatable { - final Object? materialDocument; - const MaterialMenuState({ - this.materialDocument = '', - }); - - MaterialMenuState copyWith({ - MaterialDocument? materialDocument, - }) { - return MaterialMenuState( - materialDocument: materialDocument ?? this.materialDocument, - ); - } - - @override - List get props => []; -} - -class MaterialFileInitialization extends MaterialMenuState {} - -class AllMaterialFetchSuccess extends MaterialMenuState {} diff --git a/app/lib/features/material/menu/presentation/modal/materialDocument.dart b/app/lib/features/material/menu/presentation/modal/materialDocument.dart deleted file mode 100644 index aaf9ef1..0000000 --- a/app/lib/features/material/menu/presentation/modal/materialDocument.dart +++ /dev/null @@ -1,35 +0,0 @@ -class MaterialDocument { - - final String? challenge_group; - final String? description; - final String? file_format; - final bool is_printable; - final List? source_urls; - final String? staging_url; - final String? title; - final String? url; - - MaterialDocument({ - this.challenge_group, - this.description, - this.file_format, - this.is_printable = false, - this.source_urls, - this.staging_url, - this.title, - this.url - }); - - factory MaterialDocument.fromJson(dynamic json) { - return MaterialDocument( - challenge_group: json.data()['challenge_group'].toString(), - description: json.data()['description'].toString(), - file_format: json.data()['file_format'].toString(), - is_printable: json.data()['is_printable'] as bool, - source_urls: json.data()['source_urls'] as List?, - staging_url: json.data()['staging_url'].toString(), - title: json.data()['title'].toString(), - url: json.data()['url'].toString() - ); - } -} \ No newline at end of file diff --git a/app/lib/features/material/viewer/presentation/bloc/material_viewer_bloc.dart b/app/lib/features/material/viewer/presentation/bloc/material_viewer_bloc.dart deleted file mode 100644 index 955fef8..0000000 --- a/app/lib/features/material/viewer/presentation/bloc/material_viewer_bloc.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:dio/dio.dart'; -import 'package:equatable/equatable.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:injectable/injectable.dart'; -import 'package:permission_handler/permission_handler.dart'; -import 'package:path_provider/path_provider.dart'; - -part 'material_viewer_event.dart'; -part 'material_viewer_state.dart'; - -@injectable -@singleton -class MaterialViewerBloc - extends Bloc { - MaterialViewerBloc() : super(MaterialFileInitialization()) { - on(checkIsMaterialFileExist); - on(dialogPermitDownloadFile); - } - - FutureOr checkIsMaterialFileExist( - CheckMaterialFileEvent state, - Emitter emit, - ) async { - bool isFile = await File("/data/user/0/com.example.bebras.bebras_app_ui/app_flutter/pdfAcademy.pdf").exists(); - if(isFile) { - emit(MaterialFileExist()); - } else { - emit(MaterialFileDoesNotExist()); - } - // final creds = await _googleSignIn.signInSilently(); - // - // if (creds != null) { - // emit(UserAuthenticated()); - // } else { - // emit(UserUnauthenticated()); - // } - } - - FutureOr dialogPermitDownloadFile( - DialogDownloadPermitEvent state, - Emitter emit, - ) async { - if(state.userPermission) { - String url = 'https://firebasestorage.googleapis.com/v0/b/toki-bebras-proto.appspot.com/o/buku_bebras%2FBebras-Challenge-2016_Penegak.pdf'; - emit(UserAgreeToDownload()); - try { - if (await _requestPermission(Permission.storage)) { - Directory? directory; - directory = await getExternalStorageDirectory(); - String newPath = ""; - List paths = directory!.path.split("/"); - for (int x = 1; x < paths.length; x++) { - String folder = paths[x]; - if (folder != "Android") { - newPath += "/" + folder; - } else { - break; - } - } - newPath = newPath + "/Materi_Bebras"; - print(newPath); - directory = Directory(newPath); - - File saveFile = File(directory.path + "/pdfAcademy.pdf"); - if (kDebugMode) { - print(saveFile.path); - } - if (!await directory.exists()) { - await directory.create(recursive: true); - } - if (await directory.exists()) { - await Dio().download( - url, - saveFile.path, - ); - emit(MaterialFileDownloaded()); - } - } - // return true; - } catch (e) { - // return false; - } - } else { - emit(UserDisagreeToDownload()); - } - // final creds = await _googleSignIn.signInSilently(); - // - // if (creds != null) { - // emit(UserAuthenticated()); - // } else { - // emit(UserUnauthenticated()); - // } - } -} - -Future _requestPermission(Permission permission) async { - if (await permission.isGranted) { - return true; - } else { - print('lolos234'); - var result = await permission.request(); - if (result == PermissionStatus.granted) { - return true; - } - } - return false; -} diff --git a/app/lib/features/material/viewer/presentation/bloc/material_viewer_event.dart b/app/lib/features/material/viewer/presentation/bloc/material_viewer_event.dart deleted file mode 100644 index 6d5379a..0000000 --- a/app/lib/features/material/viewer/presentation/bloc/material_viewer_event.dart +++ /dev/null @@ -1,15 +0,0 @@ -part of 'material_viewer_bloc.dart'; - -abstract class MaterialViewerEvent extends Equatable { - const MaterialViewerEvent(); - - @override - List get props => []; -} - -class CheckMaterialFileEvent extends MaterialViewerEvent {} - -class DialogDownloadPermitEvent extends MaterialViewerEvent { - final bool userPermission; - const DialogDownloadPermitEvent(this.userPermission); -} diff --git a/app/lib/features/material/viewer/presentation/bloc/material_viewer_state.dart b/app/lib/features/material/viewer/presentation/bloc/material_viewer_state.dart deleted file mode 100644 index 6e83896..0000000 --- a/app/lib/features/material/viewer/presentation/bloc/material_viewer_state.dart +++ /dev/null @@ -1,23 +0,0 @@ -part of 'material_viewer_bloc.dart'; - -class MaterialViewerState extends Equatable { - final String pdfPath; - const MaterialViewerState({ - this.pdfPath = '', - }); - - @override - List get props => []; -} - -class MaterialFileInitialization extends MaterialViewerState {} - -class UserAgreeToDownload extends MaterialViewerState {} - -class UserDisagreeToDownload extends MaterialViewerState {} - -class MaterialFileExist extends MaterialViewerState {} - -class MaterialFileDoesNotExist extends MaterialViewerState {} - -class MaterialFileDownloaded extends MaterialViewerState {} diff --git a/app/lib/features/material/viewer/presentation/pages/_pages.dart b/app/lib/features/material/viewer/presentation/pages/_pages.dart index 16779e7..b16b0bc 100644 --- a/app/lib/features/material/viewer/presentation/pages/_pages.dart +++ b/app/lib/features/material/viewer/presentation/pages/_pages.dart @@ -2,17 +2,11 @@ import 'dart:async'; import 'dart:io'; import 'package:dio/dio.dart'; -import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_pdfview/flutter_pdfview.dart'; -import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; -import '../../../../../services/di.dart'; -import '../bloc/material_viewer_bloc.dart'; part 'pdf_viewer_page.dart'; \ No newline at end of file diff --git a/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart b/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart index 4681ace..47dc292 100644 --- a/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart +++ b/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart @@ -58,8 +58,10 @@ class _PdfViewerPageState extends State { ), body: Stack( children: [ + pathPDF == '' ? + LinearProgressIndicator() : PDFView( - filePath: basePath + widget.id.toString() + '.pdf', + filePath: pathPDF, // basePath + widget.id.toString() + '.pdf', onError: (error) { print(error.toString()); }, From 6a9c4367e4b79f7fe0107a105e28b083e0b52f79 Mon Sep 17 00:00:00 2001 From: 12henbx Date: Mon, 23 Oct 2023 00:07:00 +0700 Subject: [PATCH 5/9] fix pdf viewer not responsive to state --- app/lib/app.dart | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/app/lib/app.dart b/app/lib/app.dart index 6bde314..df79ed7 100644 --- a/app/lib/app.dart +++ b/app/lib/app.dart @@ -17,30 +17,23 @@ class App extends StatelessWidget { DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]); - return MaterialApp.router( - theme: ThemeData( - textTheme: GoogleFonts.interTextTheme(), + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (_) => get() + ..add( + OnboardingAuthEvent(), + ), + ), + BlocProvider(create: (context) => QuizRegistrationCubit()), + ], + child: MaterialApp.router( + theme: ThemeData( + textTheme: GoogleFonts.interTextTheme(), + ), + routerConfig: router, + debugShowCheckedModeBanner: false, ), - routerConfig: router, - debugShowCheckedModeBanner: false, ); - // MultiBlocProvider( - // providers: [ - // BlocProvider( - // create: (_) => get() - // ..add( - // OnboardingAuthEvent(), - // ), - // ), - // BlocProvider(create: (context) => QuizRegistrationCubit()), - // ], - // child: MaterialApp.router( - // theme: ThemeData( - // textTheme: GoogleFonts.interTextTheme(), - // ), - // routerConfig: router, - // debugShowCheckedModeBanner: false, - // ), - // ); } } From be58b2c3ad563743a39f3ae824b31094c6e661ed Mon Sep 17 00:00:00 2001 From: 12henbx Date: Mon, 23 Oct 2023 13:41:24 +0700 Subject: [PATCH 6/9] fix offline mode pdf viewer, adjust design button filter --- .../material/menu/presentation/pages/material_menu.dart | 8 +++++--- .../viewer/presentation/pages/pdf_viewer_page.dart | 7 ++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/lib/features/material/menu/presentation/pages/material_menu.dart b/app/lib/features/material/menu/presentation/pages/material_menu.dart index e59dc63..7d191c2 100644 --- a/app/lib/features/material/menu/presentation/pages/material_menu.dart +++ b/app/lib/features/material/menu/presentation/pages/material_menu.dart @@ -62,7 +62,7 @@ class _MaterialMenuState extends State { Container( padding: const EdgeInsets.all(32), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ Image.asset( Assets.bebrasPandaiText, @@ -70,7 +70,9 @@ class _MaterialMenuState extends State { const SizedBox( height: 30, ), - const Text('Latihan yang pernah diikuti'), + Container( + width: double.infinity, + child: const Text('Latihan yang pernah diikuti')), const SizedBox( height: 10, ), @@ -157,7 +159,7 @@ class _MaterialMenuState extends State { ), Container( height: 70, - width: double.infinity, + width: 296, // double.infinity, decoration: BoxDecoration(border: Border.all()), child: Column( children: [ diff --git a/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart b/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart index 47dc292..3c10734 100644 --- a/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart +++ b/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart @@ -25,6 +25,11 @@ class _PdfViewerPageState extends State { @override void initState() { super.initState(); + if (File(basePath + widget.id.toString() + ".pdf").existsSync()) { + setState(() { + pathPDF = basePath + widget.id.toString() + ".pdf"; + }); + } WidgetsBinding.instance.addPostFrameCallback((_){ saveFile(widget.pdfUrl.toString(), "${widget.id}.pdf"); }); @@ -61,7 +66,7 @@ class _PdfViewerPageState extends State { pathPDF == '' ? LinearProgressIndicator() : PDFView( - filePath: pathPDF, // basePath + widget.id.toString() + '.pdf', + filePath: pathPDF, onError: (error) { print(error.toString()); }, From f29ea1604540783d6dc7127d52b77b3adeb2e34a Mon Sep 17 00:00:00 2001 From: 12henbx Date: Tue, 24 Oct 2023 09:00:30 +0700 Subject: [PATCH 7/9] adjust link to fetch pdf from gstorage bucket --- .../menu/presentation/pages/_pages.dart | 1 + .../presentation/pages/material_menu.dart | 3 +- .../viewer/presentation/pages/_pages.dart | 1 + .../presentation/pages/pdf_viewer_page.dart | 53 +++++++++++++------ 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/app/lib/features/material/menu/presentation/pages/_pages.dart b/app/lib/features/material/menu/presentation/pages/_pages.dart index 59cf028..99caa0c 100644 --- a/app/lib/features/material/menu/presentation/pages/_pages.dart +++ b/app/lib/features/material/menu/presentation/pages/_pages.dart @@ -1,6 +1,7 @@ import 'package:bebras_pandai/core/constants/BebrasGroupCategory.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; diff --git a/app/lib/features/material/menu/presentation/pages/material_menu.dart b/app/lib/features/material/menu/presentation/pages/material_menu.dart index 7d191c2..1c635a7 100644 --- a/app/lib/features/material/menu/presentation/pages/material_menu.dart +++ b/app/lib/features/material/menu/presentation/pages/material_menu.dart @@ -11,6 +11,7 @@ class _MaterialMenuState extends State { final Stream materialsStream = FirebaseFirestore.instance.collection('learning_material').snapshots(); + String? selectedValue = null; int filterIndex = 0; @@ -108,7 +109,7 @@ class _MaterialMenuState extends State { 'title': materialDoc['title'], 'description': materialDoc['description'], - 'pdfUrl': materialDoc['url'], + 'pdfUrl': materialDoc['gsReference'], }).toString()); }, child: Container( diff --git a/app/lib/features/material/viewer/presentation/pages/_pages.dart b/app/lib/features/material/viewer/presentation/pages/_pages.dart index b16b0bc..b0f23c8 100644 --- a/app/lib/features/material/viewer/presentation/pages/_pages.dart +++ b/app/lib/features/material/viewer/presentation/pages/_pages.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'package:dio/dio.dart'; +import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_pdfview/flutter_pdfview.dart'; diff --git a/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart b/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart index 3c10734..bf3de81 100644 --- a/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart +++ b/app/lib/features/material/viewer/presentation/pages/pdf_viewer_page.dart @@ -20,19 +20,23 @@ class PdfViewerPage extends StatefulWidget { class _PdfViewerPageState extends State { String basePath = "/storage/emulated/0/Android/data/com.toki.bebras_pandai/files/PDF_Download/"; - String pathPDF = ""; + String localPathPdf = ""; + String remotePathPdf = ""; @override void initState() { super.initState(); if (File(basePath + widget.id.toString() + ".pdf").existsSync()) { setState(() { - pathPDF = basePath + widget.id.toString() + ".pdf"; + localPathPdf = basePath + widget.id.toString() + ".pdf"; }); } - WidgetsBinding.instance.addPostFrameCallback((_){ - saveFile(widget.pdfUrl.toString(), "${widget.id}.pdf"); + WidgetsBinding.instance.addPostFrameCallback((_) { + fetchUrlPdfFile(widget.pdfUrl.toString()); }); + // WidgetsBinding.instance.addPostFrameCallback((_){ + // saveFile(widget.pdfUrl.toString(), "${widget.id}.pdf"); + // }); } @override @@ -47,15 +51,19 @@ class _PdfViewerPageState extends State { actions: [ IconButton( onPressed: () async { - await saveFile(widget.pdfUrl.toString(), "${widget.id}.pdf"); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text( - 'successfully saved to internal storage', - style: TextStyle(color: Colors.white), + try { + await saveFile(remotePathPdf, "${widget.id}.pdf"); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'successfully saved to internal storage', + style: TextStyle(color: Colors.white), + ), ), - ), - ); + ); + } catch (e) { + print(e); + } }, icon: const Icon(Icons.download_rounded), ), @@ -63,10 +71,10 @@ class _PdfViewerPageState extends State { ), body: Stack( children: [ - pathPDF == '' ? + localPathPdf == '' ? LinearProgressIndicator() : PDFView( - filePath: pathPDF, + filePath: localPathPdf, onError: (error) { print(error.toString()); }, @@ -101,9 +109,9 @@ class _PdfViewerPageState extends State { saveFile.path, ); setState(() { - pathPDF = saveFile.path; + localPathPdf = saveFile.path; }); - print(pathPDF); + print(localPathPdf); } } return true; @@ -123,4 +131,17 @@ class _PdfViewerPageState extends State { } return false; } + + Future fetchUrlPdfFile(String pathReference) async { + try { + final storageRef = FirebaseStorage.instance.ref(); + String url = await storageRef.child(pathReference).getDownloadURL(); + setState(() { + remotePathPdf = url; + }); + saveFile(url, "${widget.id}.pdf"); + } catch (e) { + debugPrint(e.toString()); + } + } } From 090c7623f756be00ad790e737aebb366c9f6c4e7 Mon Sep 17 00:00:00 2001 From: 12henbx Date: Tue, 24 Oct 2023 11:04:43 +0700 Subject: [PATCH 8/9] move filter button to above material list --- .../presentation/pages/material_menu.dart | 80 +++++++++++-------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/app/lib/features/material/menu/presentation/pages/material_menu.dart b/app/lib/features/material/menu/presentation/pages/material_menu.dart index 1c635a7..995a85b 100644 --- a/app/lib/features/material/menu/presentation/pages/material_menu.dart +++ b/app/lib/features/material/menu/presentation/pages/material_menu.dart @@ -11,7 +11,6 @@ class _MaterialMenuState extends State { final Stream materialsStream = FirebaseFirestore.instance.collection('learning_material').snapshots(); - String? selectedValue = null; int filterIndex = 0; @@ -25,8 +24,8 @@ class _MaterialMenuState extends State { }, child: Container( alignment: Alignment.center, - height: 34, - width: 147, + height: 40, + width: 130, decoration: BoxDecoration( border: Border.all(), color: (filterIndex == index) ? Colors.black54 : Colors.white, @@ -71,6 +70,50 @@ class _MaterialMenuState extends State { const SizedBox( height: 30, ), + Container( + height: 40, + width: 296, // double.infinity, + decoration: BoxDecoration(border: Border.all()), + child: ListView( + scrollDirection: Axis.horizontal, + children: [ + CustomRadioButton( + "siKecil", bebrasGroupList[0].index), + CustomRadioButton("Siaga", bebrasGroupList[1].index), + CustomRadioButton( + "Penggalang", bebrasGroupList[2].index), + CustomRadioButton( + "Penegak", bebrasGroupList[3].index), + ]), + ), + // Container( + // height: 70, + // width: 296, // double.infinity, + // decoration: BoxDecoration(border: Border.all()), + // child: Column( + // children: [ + // Row( + // children: [ + // CustomRadioButton( + // "siKecil", bebrasGroupList[0].index), + // CustomRadioButton( + // "Siaga", bebrasGroupList[1].index), + // ], + // ), + // Row( + // children: [ + // CustomRadioButton( + // "Penggalang", bebrasGroupList[2].index), + // CustomRadioButton( + // "Penegak", bebrasGroupList[3].index), + // ], + // ), + // ], + // ), + // ), + const SizedBox( + height: 10, + ), Container( width: double.infinity, child: const Text('Latihan yang pernah diikuti')), @@ -99,7 +142,8 @@ class _MaterialMenuState extends State { Map materialDoc = document.data()! as Map; if (materialDoc['challenge_group'] == - bebrasGroupList[filterIndex].bebrasChallengeKey) { + bebrasGroupList[filterIndex] + .bebrasChallengeKey) { return InkWell( onTap: () { context.push(Uri( @@ -155,34 +199,6 @@ class _MaterialMenuState extends State { ), ); }), - const SizedBox( - height: 10, - ), - Container( - height: 70, - width: 296, // double.infinity, - decoration: BoxDecoration(border: Border.all()), - child: Column( - children: [ - Row( - children: [ - CustomRadioButton( - "siKecil", bebrasGroupList[0].index), - CustomRadioButton( - "Siaga", bebrasGroupList[1].index), - ], - ), - Row( - children: [ - CustomRadioButton( - "Penggalang", bebrasGroupList[2].index), - CustomRadioButton( - "Penegak", bebrasGroupList[3].index), - ], - ), - ], - ), - ), ], ), ), From a601d42db342fc8baaf69323021b7c343730fb39 Mon Sep 17 00:00:00 2001 From: 12henbx <33747503+12henbx@users.noreply.github.com> Date: Tue, 24 Oct 2023 16:41:16 +0700 Subject: [PATCH 9/9] Update app/lib/features/material/menu/presentation/pages/material_menu.dart Edit title list text Co-authored-by: Mirza Widihananta --- .../material/menu/presentation/pages/material_menu.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/features/material/menu/presentation/pages/material_menu.dart b/app/lib/features/material/menu/presentation/pages/material_menu.dart index 995a85b..34e610b 100644 --- a/app/lib/features/material/menu/presentation/pages/material_menu.dart +++ b/app/lib/features/material/menu/presentation/pages/material_menu.dart @@ -116,7 +116,7 @@ class _MaterialMenuState extends State { ), Container( width: double.infinity, - child: const Text('Latihan yang pernah diikuti')), + child: const Text('Daftar Materi')), const SizedBox( height: 10, ),