├── .gradle ├── 5.2.1 │ ├── gc.properties │ ├── fileChanges │ │ └── last-build.bin │ └── fileHashes │ │ └── fileHashes.lock ├── vcs-1 │ └── gc.properties └── buildOutputCleanup │ ├── cache.properties │ └── buildOutputCleanup.lock ├── ios ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-50x50@1x.png │ │ │ ├── Icon-App-50x50@2x.png │ │ │ ├── Icon-App-57x57@1x.png │ │ │ ├── Icon-App-57x57@2x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-72x72@1x.png │ │ │ ├── Icon-App-72x72@2x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── .gitignore └── Podfile ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ └── Icon-512.png ├── manifest.json └── index.html ├── assets ├── ss │ ├── ss1.png │ ├── ss2.png │ ├── ss3.png │ ├── ss4.png │ ├── ss5.png │ ├── ss6.png │ ├── ss7.png │ └── ss8.png ├── icons │ └── icon.png └── images │ ├── aqua.JPG │ ├── aqua-chibi.png │ └── tenkinokowp.png ├── lib ├── constants │ ├── base_url.dart │ └── base_color.dart ├── bloc │ ├── popular_bloc │ │ ├── bloc.dart │ │ ├── popular_event.dart │ │ ├── popular_state.dart │ │ └── popular_bloc.dart │ ├── genre_list_bloc │ │ ├── bloc.dart │ │ ├── genre_list_event.dart │ │ ├── genre_list_state.dart │ │ └── genre_list_bloc.dart │ ├── manga_list_bloc │ │ ├── bloc.dart │ │ ├── manga_list_event.dart │ │ ├── manga_list_state.dart │ │ └── manga_list_bloc.dart │ ├── recomended_bloc │ │ ├── bloc.dart │ │ ├── recomended_event.dart │ │ ├── recomended_state.dart │ │ └── recomended_bloc.dart │ ├── search_bloc │ │ ├── bloc.dart │ │ ├── search_bloc_event.dart │ │ ├── search_bloc_state.dart │ │ └── search_bloc_bloc.dart │ ├── chapter_bloc │ │ ├── bloc.dart │ │ ├── chapter_bloc_event.dart │ │ ├── chapter_bloc_state.dart │ │ └── chapter_bloc_bloc.dart │ ├── manhuamanhwa │ │ ├── bloc.dart │ │ ├── manhuamanhwa_event.dart │ │ ├── manhuamanhwa_state.dart │ │ └── manhuamanhwa_bloc.dart │ ├── manga_detail_bloc │ │ ├── bloc.dart │ │ ├── manga_detail_event.dart │ │ ├── manga_detail_state.dart │ │ └── manga_detail_bloc.dart │ ├── mangabygenre_bloc │ │ ├── bloc.dart │ │ ├── manga_by_genre_event.dart │ │ ├── manga_by_genre_state.dart │ │ └── manga_by_genre_bloc.dart │ └── manhua_bloc │ │ ├── manhua_event.dart │ │ ├── manhua_state.dart │ │ └── manhua_bloc.dart ├── helper │ ├── hive │ │ ├── hive_chapter_model.dart │ │ ├── hive_manga_model.dart │ │ ├── hive_chapter_opened_model.dart │ │ ├── hive_service.dart │ │ ├── hive_chapter_model.g.dart │ │ ├── hive_chapter_opened_model.g.dart │ │ └── hive_manga_model.g.dart │ ├── color_manga_type.dart │ └── routes.dart ├── components │ ├── bottom_loader.dart │ ├── my_shimmer.dart │ ├── loading_dialog.dart │ ├── my_body.dart │ ├── image_cache_loading.dart │ ├── build_error.dart │ ├── item_small.dart │ ├── splash_screen.dart │ ├── bottom_nav_bar.dart │ └── item_big.dart ├── service │ ├── api_service.dart │ └── base_service.dart ├── repositories │ ├── recommended_repo.dart │ ├── manga_list_repo.dart │ ├── chapter_repo.dart │ ├── genre_list_repo.dart │ ├── manga_detail_repo.dart │ ├── search_repo.dart │ ├── popular_repo.dart │ └── manhua_manhwa_repo.dart ├── models │ ├── popular_model.dart │ ├── search_model.dart │ ├── manhua_manhwa_model.dart │ ├── genre_list_model.dart │ ├── chapter_model.dart │ ├── manga_list_model.dart │ ├── recomended_model.dart │ └── manga_detail_model.dart ├── screens │ ├── lainnya_screen │ │ ├── detail_lainnya.dart │ │ ├── privacy.dart │ │ └── tos.dart │ ├── result_screen │ │ ├── search_screen.dart │ │ ├── result_screen.dart │ │ ├── terpopuler_screen.dart │ │ └── manga_by_genre_screen.dart │ ├── detail_screen │ │ └── index_detail.dart │ ├── home_screens │ │ ├── terbaru.dart │ │ ├── genre_list.dart │ │ ├── terpopular.dart │ │ ├── home_screen.dart │ │ └── my_carousel.dart │ ├── chapter_screen │ │ └── index_chapter.dart │ ├── list_manga_screen │ │ ├── index_manga_list.dart │ │ ├── manhwa_category.dart │ │ ├── semuanya_category.dart │ │ └── manhua_category.dart │ └── tersimpan_screen │ │ └── tersimpan_screen.dart └── main.dart ├── android ├── gradle.properties ├── .gitignore ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── launcher_icon.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── launcher_icon.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── launcher_icon.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── launcher_icon.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── launcher_icon.png │ │ │ │ ├── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ └── values │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── febryardiansyah │ │ │ │ │ └── mangamint │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── build.gradle ├── .metadata ├── .gitignore ├── LICENSE ├── test └── widget_test.dart ├── README.md └── pubspec.yaml /.gradle/5.2.1/gc.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gradle/vcs-1/gc.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gradle/5.2.1/fileChanges/last-build.bin: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/web/favicon.png -------------------------------------------------------------------------------- /assets/ss/ss1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/assets/ss/ss1.png -------------------------------------------------------------------------------- /assets/ss/ss2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/assets/ss/ss2.png -------------------------------------------------------------------------------- /assets/ss/ss3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/assets/ss/ss3.png -------------------------------------------------------------------------------- /assets/ss/ss4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/assets/ss/ss4.png -------------------------------------------------------------------------------- /assets/ss/ss5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/assets/ss/ss5.png -------------------------------------------------------------------------------- /assets/ss/ss6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/assets/ss/ss6.png -------------------------------------------------------------------------------- /assets/ss/ss7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/assets/ss/ss7.png -------------------------------------------------------------------------------- /assets/ss/ss8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/assets/ss/ss8.png -------------------------------------------------------------------------------- /.gradle/buildOutputCleanup/cache.properties: -------------------------------------------------------------------------------- 1 | #Sun Jul 26 11:28:37 WIB 2020 2 | gradle.version=5.2.1 3 | -------------------------------------------------------------------------------- /lib/constants/base_url.dart: -------------------------------------------------------------------------------- 1 | 2 | const String BaseUrl = 'https://manga-api-rosy.vercel.app/api/'; 3 | -------------------------------------------------------------------------------- /assets/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/assets/icons/icon.png -------------------------------------------------------------------------------- /assets/images/aqua.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/assets/images/aqua.JPG -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/web/icons/Icon-512.png -------------------------------------------------------------------------------- /assets/images/aqua-chibi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/assets/images/aqua-chibi.png -------------------------------------------------------------------------------- /assets/images/tenkinokowp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/assets/images/tenkinokowp.png -------------------------------------------------------------------------------- /lib/bloc/popular_bloc/bloc.dart: -------------------------------------------------------------------------------- 1 | export 'popular_bloc.dart'; 2 | export 'popular_event.dart'; 3 | export 'popular_state.dart'; -------------------------------------------------------------------------------- /lib/bloc/genre_list_bloc/bloc.dart: -------------------------------------------------------------------------------- 1 | export 'genre_list_bloc.dart'; 2 | export 'genre_list_event.dart'; 3 | export 'genre_list_state.dart'; -------------------------------------------------------------------------------- /lib/bloc/manga_list_bloc/bloc.dart: -------------------------------------------------------------------------------- 1 | export 'manga_list_bloc.dart'; 2 | export 'manga_list_event.dart'; 3 | export 'manga_list_state.dart'; -------------------------------------------------------------------------------- /lib/bloc/recomended_bloc/bloc.dart: -------------------------------------------------------------------------------- 1 | export 'recomended_bloc.dart'; 2 | export 'recomended_event.dart'; 3 | export 'recomended_state.dart'; -------------------------------------------------------------------------------- /lib/bloc/search_bloc/bloc.dart: -------------------------------------------------------------------------------- 1 | export 'search_bloc_bloc.dart'; 2 | export 'search_bloc_event.dart'; 3 | export 'search_bloc_state.dart'; -------------------------------------------------------------------------------- /.gradle/5.2.1/fileHashes/fileHashes.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/.gradle/5.2.1/fileHashes/fileHashes.lock -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /lib/bloc/chapter_bloc/bloc.dart: -------------------------------------------------------------------------------- 1 | export 'chapter_bloc_bloc.dart'; 2 | export 'chapter_bloc_event.dart'; 3 | export 'chapter_bloc_state.dart'; -------------------------------------------------------------------------------- /lib/bloc/manhuamanhwa/bloc.dart: -------------------------------------------------------------------------------- 1 | export 'manhuamanhwa_bloc.dart'; 2 | export 'manhuamanhwa_event.dart'; 3 | export 'manhuamanhwa_state.dart'; -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /lib/bloc/manga_detail_bloc/bloc.dart: -------------------------------------------------------------------------------- 1 | export 'manga_detail_bloc.dart'; 2 | export 'manga_detail_event.dart'; 3 | export 'manga_detail_state.dart'; -------------------------------------------------------------------------------- /lib/bloc/mangabygenre_bloc/bloc.dart: -------------------------------------------------------------------------------- 1 | export 'manga_by_genre_bloc.dart'; 2 | export 'manga_by_genre_event.dart'; 3 | export 'manga_by_genre_state.dart'; -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /.gradle/buildOutputCleanup/buildOutputCleanup.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/.gradle/buildOutputCleanup/buildOutputCleanup.lock -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/android/app/src/main/res/mipmap-hdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/android/app/src/main/res/mipmap-mdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/febryardiansyah/manga_mint/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/febryardiansyah/mangamint/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.febryardiansyah.mangamint 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /lib/bloc/manhua_bloc/manhua_event.dart: -------------------------------------------------------------------------------- 1 | part of 'manhua_bloc.dart'; 2 | 3 | @immutable 4 | abstract class ManhuaEvent extends Equatable{ 5 | @override 6 | List get props => []; 7 | } 8 | class InitialFetchManhua extends ManhuaEvent{} 9 | class FetchManhua extends ManhuaEvent{} 10 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/helper/hive/hive_chapter_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | 3 | part 'hive_chapter_model.g.dart'; 4 | 5 | @HiveType() 6 | class HiveChapterModel{ 7 | @HiveField(0) 8 | String endpoint; 9 | @HiveField(1) 10 | int index; 11 | 12 | HiveChapterModel({this.endpoint, this.index}); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /lib/bloc/genre_list_bloc/genre_list_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class GenreListEvent extends Equatable { 4 | const GenreListEvent(); 5 | @override 6 | List get props => []; 7 | } 8 | class FetchGenreList extends GenreListEvent{} 9 | class RefreshGenreList extends GenreListEvent{} -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/bloc/recomended_bloc/recomended_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class RecomendedEvent extends Equatable { 4 | const RecomendedEvent(); 5 | @override 6 | List get props => []; 7 | } 8 | class FetchRecommended extends RecomendedEvent{} 9 | class RefreshRecommended extends RecomendedEvent{} 10 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 8abf0a6d8c0d94fc5a6a6761025fcf035870d8c0 8 | channel: master 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /lib/bloc/popular_bloc/popular_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class PopularEvent extends Equatable { 4 | const PopularEvent(); 5 | @override 6 | List get props => []; 7 | } 8 | 9 | class FetchPopular extends PopularEvent{} 10 | class InitialFetchPopular extends PopularEvent{} 11 | class RefreshPopular extends PopularEvent{} -------------------------------------------------------------------------------- /lib/bloc/manga_detail_bloc/manga_detail_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class MangaDetailEvent extends Equatable { 4 | const MangaDetailEvent(); 5 | @override 6 | List get props => []; 7 | } 8 | 9 | class FetchMangaDetail extends MangaDetailEvent{ 10 | final String endpoint; 11 | FetchMangaDetail(this.endpoint); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /lib/constants/base_color.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class BaseColor{ 4 | static final Color red = Color(0xffE8505B); 5 | static final Color black = Color(0xff333333); 6 | static final Color green = Color(0xff27AE60); 7 | static final Color orange = Color(0xffF2994A); 8 | static final Color grey1 = Color(0xff828282); 9 | static final Color grey2 = Color(0xffC4C4C4); 10 | } -------------------------------------------------------------------------------- /lib/bloc/chapter_bloc/chapter_bloc_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class ChapterBlocEvent extends Equatable { 4 | const ChapterBlocEvent(); 5 | @override 6 | List get props => []; 7 | } 8 | class FetchChapter extends ChapterBlocEvent{ 9 | final String endpoint; 10 | 11 | FetchChapter({this.endpoint}); 12 | @override 13 | List get props => [endpoint]; 14 | } -------------------------------------------------------------------------------- /lib/helper/color_manga_type.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:mangamint/constants/base_color.dart'; 3 | 4 | Color mangaTypeColor(type) { 5 | switch (type) { 6 | case 'Manga': 7 | return BaseColor.red; 8 | case 'Manhua': 9 | return BaseColor.green; 10 | case 'Manhwa': 11 | return BaseColor.orange; 12 | default: 13 | return BaseColor.grey1; 14 | } 15 | } -------------------------------------------------------------------------------- /lib/helper/hive/hive_manga_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | 3 | part 'hive_manga_model.g.dart'; 4 | 5 | @HiveType() 6 | class HiveMangaModel{ 7 | @HiveField(0) 8 | String manga_endpoint; 9 | @HiveField(1) 10 | String thumb; 11 | @HiveField(2) 12 | String type; 13 | @HiveField(3) 14 | String title; 15 | 16 | HiveMangaModel({this.manga_endpoint, this.thumb, this.type, this.title}); 17 | 18 | } -------------------------------------------------------------------------------- /lib/bloc/search_bloc/search_bloc_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:meta/meta.dart'; 3 | 4 | @immutable 5 | abstract class SearchBlocEvent extends Equatable{ 6 | @override 7 | List get props => []; 8 | } 9 | class FetchSearch extends SearchBlocEvent{ 10 | String query; 11 | 12 | FetchSearch({this.query}); 13 | @override 14 | List get props => [query]; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /lib/components/bottom_loader.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter_spinkit/flutter_spinkit.dart'; 3 | import 'package:mangamint/constants/base_color.dart'; 4 | 5 | class BottomLoader extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | return Center( 9 | child: SpinKitCircle( 10 | color: BaseColor.red, 11 | ), 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/helper/hive/hive_chapter_opened_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | 3 | part 'hive_chapter_opened_model.g.dart'; 4 | 5 | @HiveType() 6 | class HiveChapterOpenedModel{ 7 | @HiveField(0) 8 | int lastChapter; 9 | @HiveField(1) 10 | String manga_endpoint; 11 | @HiveField(2) 12 | String chapter_endpoint; 13 | 14 | HiveChapterOpenedModel({this.lastChapter, this.manga_endpoint,this.chapter_endpoint}); 15 | 16 | } -------------------------------------------------------------------------------- /lib/bloc/manga_list_bloc/manga_list_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:meta/meta.dart'; 3 | 4 | @immutable 5 | abstract class MangaListEvent extends Equatable{ 6 | const MangaListEvent(); 7 | @override 8 | List get props => []; 9 | } 10 | 11 | class FetchManga extends MangaListEvent{} 12 | class InitialFetchMangaEvent extends MangaListEvent{} 13 | class RefreshMangaEvent extends MangaListEvent{} -------------------------------------------------------------------------------- /lib/service/api_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:http/http.dart' as http; 4 | 5 | class ApiService{ 6 | static http.Client api = http.Client(); 7 | 8 | static FuturegetResponse(url)async{ 9 | var response = await http.get(url); 10 | 11 | if(response.statusCode ==200){ 12 | var res = json.decode(response.body); 13 | return res; 14 | }else{ 15 | throw Exception('failed fetch'); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/components/my_shimmer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:mangamint/constants/base_color.dart'; 4 | import 'package:shimmer/shimmer.dart'; 5 | 6 | class MyShimmer extends StatelessWidget { 7 | final Widget child; 8 | 9 | const MyShimmer({Key key, this.child}) : super(key: key); 10 | @override 11 | Widget build(BuildContext context) { 12 | return Shimmer.fromColors(child:child ,baseColor: BaseColor.grey2, 13 | highlightColor: Colors.white,); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/helper/hive/hive_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | 3 | class HiveService{ 4 | addBoxes(List items,String boxName)async{ 5 | final box = await Hive.openBox(boxName); 6 | for (var item in items){ 7 | box.add(item); 8 | } 9 | } 10 | getBoxes(String boxName)async{ 11 | List boxList = List(); 12 | final box = await Hive.openBox(boxName); 13 | int length = box.length; 14 | for(int i = 0;i get props => []; 7 | } 8 | class FetchManhua extends ManhuamanhwaEvent{ 9 | final String endpoint; 10 | 11 | FetchManhua({this.endpoint}); 12 | @override 13 | List get props => [endpoint]; 14 | } 15 | class FetchManhwa extends ManhuamanhwaEvent{ 16 | final String endpoint; 17 | 18 | FetchManhwa({this.endpoint}); 19 | @override 20 | List get props => [endpoint]; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /lib/repositories/recommended_repo.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:mangamint/constants/base_url.dart'; 5 | import 'package:mangamint/models/recomended_model.dart'; 6 | import 'package:mangamint/service/api_service.dart'; 7 | import 'package:mangamint/service/base_service.dart'; 8 | 9 | class RecommendedRepo extends BaseService{ 10 | Future>getRecomended()async{ 11 | Response response = await request(url: 'recommended'); 12 | List res = RecommendedModel.fromJson(response.data).recommendedList; 13 | return res; 14 | } 15 | } -------------------------------------------------------------------------------- /lib/repositories/manga_list_repo.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:mangamint/constants/base_url.dart'; 5 | import 'package:mangamint/models/manga_list_model.dart'; 6 | import 'package:mangamint/service/api_service.dart'; 7 | import 'package:mangamint/service/base_service.dart'; 8 | 9 | class MangaListRepo extends BaseService{ 10 | Future > getMangaList({int page})async{ 11 | Response response = await request(url:BaseUrl+'manga/page/$page'); 12 | final List list = MangaModel.fromJson(response.data).mangaList; 13 | return list; 14 | } 15 | } -------------------------------------------------------------------------------- /lib/bloc/mangabygenre_bloc/manga_by_genre_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class MangaByGenreEvent extends Equatable { 4 | const MangaByGenreEvent(); 5 | @override 6 | List get props => []; 7 | } 8 | class FetchMangByGenre extends MangaByGenreEvent{ 9 | final String endpoint; 10 | 11 | FetchMangByGenre({this.endpoint}); 12 | @override 13 | List get props => [endpoint]; 14 | 15 | } 16 | class InitialMangaByGenreEvent extends MangaByGenreEvent{ 17 | final String endpoint; 18 | 19 | InitialMangaByGenreEvent({this.endpoint}); 20 | @override 21 | List get props => [endpoint]; 22 | } -------------------------------------------------------------------------------- /lib/models/popular_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class PopularModel extends Equatable{ 4 | String title,chapter,type,thumb,endpoint,upload_on; 5 | 6 | PopularModel({this.title, this.chapter, this.type, this.thumb, this.endpoint, 7 | this.upload_on}); 8 | 9 | factory PopularModel.fromJson(Mapjson){ 10 | return PopularModel( 11 | title: json['title'], 12 | endpoint: json['endpoint'], 13 | thumb: json['thumb'], 14 | type: json['type'], 15 | chapter: json['chapter'], 16 | upload_on: json['upload_on'] 17 | ); 18 | } 19 | 20 | @override 21 | List get props => []; 22 | } 23 | -------------------------------------------------------------------------------- /lib/screens/lainnya_screen/detail_lainnya.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:mangamint/constants/base_color.dart'; 3 | 4 | class DetailLainnya extends StatelessWidget { 5 | final String title,body; 6 | 7 | DetailLainnya({this.title, this.body}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Scaffold( 12 | appBar: AppBar( 13 | title: Text(title,style: TextStyle(color: BaseColor.black),), 14 | ), 15 | body: Padding( 16 | padding: const EdgeInsets.all(8.0), 17 | child: SingleChildScrollView(child: Text(body,textAlign: TextAlign.justify,)), 18 | ), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mangamint", 3 | "short_name": "mangamint", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter application.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /lib/models/search_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class SearchModel extends Equatable{ 4 | String title,thumb,type,endpoint,updated_on; 5 | num score; 6 | 7 | SearchModel({this.title, this.thumb, this.type, this.endpoint, this.score,this.updated_on}); 8 | 9 | factory SearchModel.fromJson(Mapjson){ 10 | return SearchModel( 11 | title: json['title'], 12 | type: json['type'], 13 | thumb: json['thumb'], 14 | score: json['score'], 15 | endpoint: json['endpoint'], 16 | updated_on: json['updated_on'] 17 | ); 18 | } 19 | 20 | @override 21 | List get props => [title,thumb,endpoint,score,type]; 22 | } -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Flutter Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | include ':app' 6 | 7 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 8 | def properties = new Properties() 9 | 10 | assert localPropertiesFile.exists() 11 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 12 | 13 | def flutterSdkPath = properties.getProperty("flutter.sdk") 14 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 15 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 16 | -------------------------------------------------------------------------------- /lib/service/base_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:mangamint/constants/base_url.dart'; 3 | 4 | class BaseService { 5 | Dio _dio = new Dio(); 6 | 7 | Futurerequest({String url})async{ 8 | Response response; 9 | _dio.interceptors.add(HeaderInterceptor()); 10 | 11 | try{ 12 | response = await _dio.get(url); 13 | 14 | }on DioError catch(e){ 15 | response = e.response; 16 | 17 | } 18 | return response; 19 | } 20 | } 21 | 22 | class HeaderInterceptor extends InterceptorsWrapper{ 23 | @override 24 | Future onRequest(RequestOptions options) { 25 | options.baseUrl = BaseUrl; 26 | return super.onRequest(options); 27 | } 28 | } -------------------------------------------------------------------------------- /lib/models/manhua_manhwa_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class ManhuaManwaModel extends Equatable{ 4 | String title,type,thumb,endpoint,updated_on,chapter; 5 | num score; 6 | 7 | ManhuaManwaModel({this.title, this.type, this.thumb, this.endpoint, 8 | this.score,this.updated_on,this.chapter}); 9 | factory ManhuaManwaModel.fromJson(Mapjson){ 10 | return ManhuaManwaModel( 11 | title: json['title'], 12 | type: json['type'], 13 | thumb: json['thumb'], 14 | score: json['score'], 15 | endpoint: json['endpoint'], 16 | updated_on: json['updated_on'], 17 | chapter: json['chapter'] 18 | ); 19 | } 20 | 21 | @override 22 | List get props => [title,type,thumb,endpoint,updated_on]; 23 | } -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | classpath 'com.google.gms:google-services:4.3.3' 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | jcenter() 19 | } 20 | } 21 | 22 | rootProject.buildDir = '../build' 23 | subprojects { 24 | project.buildDir = "${rootProject.buildDir}/${project.name}" 25 | } 26 | subprojects { 27 | project.evaluationDependsOn(':app') 28 | } 29 | 30 | task clean(type: Delete) { 31 | delete rootProject.buildDir 32 | } 33 | -------------------------------------------------------------------------------- /lib/bloc/chapter_bloc/chapter_bloc_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:mangamint/models/chapter_model.dart'; 3 | 4 | abstract class ChapterBlocState extends Equatable { 5 | const ChapterBlocState(); 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class InitialChapterBlocState extends ChapterBlocState {} 11 | 12 | class ChapterLoadingState extends ChapterBlocState{} 13 | 14 | class ChapterLoadedState extends ChapterBlocState{ 15 | final ChapterModel data; 16 | 17 | ChapterLoadedState({this.data}); 18 | @override 19 | List get props => [data]; 20 | } 21 | 22 | class ChapterFailureState extends ChapterBlocState{ 23 | final String msg; 24 | 25 | ChapterFailureState({this.msg}); 26 | @override 27 | List get props => [msg]; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /lib/bloc/search_bloc/search_bloc_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:mangamint/models/search_model.dart'; 3 | import 'package:meta/meta.dart'; 4 | 5 | @immutable 6 | abstract class SearchBlocState extends Equatable{ 7 | @override 8 | List get props => []; 9 | } 10 | 11 | class InitialSearchBlocState extends SearchBlocState {} 12 | class SearchLoadingState extends SearchBlocState{} 13 | class SearchLoadedState extends SearchBlocState{ 14 | final ListsearchList; 15 | 16 | SearchLoadedState({this.searchList}); 17 | @override 18 | List get props => [searchList]; 19 | } 20 | class SearchFailureState extends SearchBlocState{ 21 | final String msg; 22 | 23 | SearchFailureState({this.msg}); 24 | @override 25 | List get props => [msg]; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /lib/bloc/genre_list_bloc/genre_list_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:mangamint/models/genre_list_model.dart'; 3 | 4 | abstract class GenreListState extends Equatable { 5 | const GenreListState(); 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class InitialGenreListState extends GenreListState {} 11 | 12 | class GenreListLoadingState extends GenreListState{} 13 | 14 | class GenreListLoadedState extends GenreListState{ 15 | final ListgenreList; 16 | 17 | GenreListLoadedState({this.genreList}); 18 | @override 19 | List get props => [genreList]; 20 | 21 | } 22 | 23 | class GenreListFailureState extends GenreListState{ 24 | final String msg; 25 | 26 | GenreListFailureState({this.msg}); 27 | @override 28 | List get props => [msg]; 29 | } 30 | -------------------------------------------------------------------------------- /lib/bloc/manga_detail_bloc/manga_detail_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:mangamint/models/manga_detail_model.dart'; 3 | 4 | abstract class MangaDetailState extends Equatable { 5 | const MangaDetailState(); 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class InitialMangaDetailState extends MangaDetailState {} 11 | 12 | class MangaDetailLoadingState extends MangaDetailState{} 13 | 14 | class MangaDetailLoadedState extends MangaDetailState{ 15 | final MangaDetailModel data; 16 | 17 | MangaDetailLoadedState({this.data}); 18 | 19 | @override 20 | List get props => [data]; 21 | } 22 | 23 | class MangaDetailFailureState extends MangaDetailState{ 24 | final String msg; 25 | 26 | MangaDetailFailureState({this.msg}); 27 | 28 | @override 29 | List get props => [msg]; 30 | } -------------------------------------------------------------------------------- /lib/bloc/recomended_bloc/recomended_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:mangamint/models/recomended_model.dart'; 3 | 4 | abstract class RecomendedState extends Equatable { 5 | const RecomendedState(); 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class InitialRecomendedState extends RecomendedState {} 11 | 12 | class RecommendedLoadingState extends RecomendedState{} 13 | 14 | class RecommendedLoadedState extends RecomendedState{ 15 | final List recommendedList; 16 | 17 | RecommendedLoadedState({this.recommendedList}); 18 | @override 19 | List get props => [recommendedList]; 20 | } 21 | 22 | class RecomendedFailureState extends RecomendedState{ 23 | final String msg; 24 | 25 | RecomendedFailureState({this.msg}); 26 | @override 27 | List get props => [msg]; 28 | } 29 | -------------------------------------------------------------------------------- /lib/bloc/chapter_bloc/chapter_bloc_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:bloc/bloc.dart'; 3 | import 'package:mangamint/models/chapter_model.dart'; 4 | import 'package:mangamint/repositories/chapter_repo.dart'; 5 | import './bloc.dart'; 6 | 7 | class ChapterBloc extends Bloc { 8 | final ChapterRepo _chapterRepo; 9 | 10 | ChapterBloc(this._chapterRepo) : super(InitialChapterBlocState()); 11 | 12 | @override 13 | Stream mapEventToState( 14 | ChapterBlocEvent event, 15 | ) async* { 16 | if(event is FetchChapter){ 17 | yield ChapterLoadingState(); 18 | try{ 19 | final ChapterModel data = await _chapterRepo.getChapter(event.endpoint); 20 | yield ChapterLoadedState(data: data); 21 | }catch(e){ 22 | yield ChapterFailureState(msg: e.toString()); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/bloc/search_bloc/search_bloc_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:bloc/bloc.dart'; 3 | import 'package:mangamint/models/search_model.dart'; 4 | import 'package:mangamint/repositories/search_repo.dart'; 5 | import './bloc.dart'; 6 | 7 | class SearchBlocBloc extends Bloc { 8 | final SearchRepo _searchRepo; 9 | 10 | SearchBlocBloc(this._searchRepo) : super(InitialSearchBlocState()); 11 | 12 | @override 13 | Stream mapEventToState( 14 | SearchBlocEvent event, 15 | ) async* { 16 | if(event is FetchSearch){ 17 | yield SearchLoadingState(); 18 | try{ 19 | final Listlist = await _searchRepo.searchManga(query: event.query); 20 | yield SearchLoadedState(searchList: list); 21 | }catch(e){ 22 | yield SearchFailureState(msg: e.toString()); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/bloc/manga_detail_bloc/manga_detail_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:bloc/bloc.dart'; 3 | import 'package:mangamint/models/manga_detail_model.dart'; 4 | import 'package:mangamint/repositories/manga_detail_repo.dart'; 5 | import './bloc.dart'; 6 | 7 | class MangaDetailBloc extends Bloc { 8 | final MangaDetailRepo _detailRepo; 9 | 10 | MangaDetailBloc(this._detailRepo) : super(InitialMangaDetailState()); 11 | 12 | @override 13 | Stream mapEventToState( 14 | MangaDetailEvent event, 15 | ) async* { 16 | if(event is FetchMangaDetail){ 17 | yield MangaDetailLoadingState(); 18 | try{ 19 | final MangaDetailModel data = await _detailRepo.getMangaDetail(event.endpoint); 20 | yield MangaDetailLoadedState(data: data); 21 | }catch(e){ 22 | yield MangaDetailFailureState(msg: e.toString()); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/repositories/chapter_repo.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:mangamint/constants/base_url.dart'; 5 | import 'package:mangamint/models/chapter_model.dart'; 6 | import 'package:mangamint/service/api_service.dart'; 7 | import 'package:mangamint/service/base_service.dart'; 8 | 9 | class ChapterRepo extends BaseService{ 10 | FuturegetChapter(String endpoint)async{ 11 | final Response response = await request(url: 'chapter/$endpoint'); 12 | ChapterModel data = ChapterModel.fromJson(response.data); 13 | return data; 14 | // final response = await ApiService.api.get(BaseUrl+'chapter/$endpoint'); 15 | // 16 | // if(response.statusCode == 200){ 17 | // final res = json.decode(response.body); 18 | // ChapterModel data = ChapterModel.fromJson(res); 19 | // return data; 20 | // }else{ 21 | // throw Exception('failed Fetch'); 22 | // } 23 | } 24 | } -------------------------------------------------------------------------------- /lib/repositories/genre_list_repo.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:mangamint/models/genre_list_model.dart'; 3 | import 'package:mangamint/service/base_service.dart'; 4 | 5 | class GenreListRepo extends BaseService{ 6 | Future>getGenreList()async{ 7 | final Response response = await request(url:'genres'); 8 | Listlist = List.from(response.data['list_genre'].map((item) => GeneListModel.fromMap(item))); 9 | // print('genressssss ${response.data}'); 10 | return list; 11 | } 12 | } 13 | 14 | class MangaByGenreRepo extends BaseService{ 15 | Future>getManga({String genre,int page})async{ 16 | final Response response = await request(url: 'genres/$genre$page'); 17 | Listlist = List.from(response.data['manga_list'].map((item) => MangaByGenreModel.fromJson(item))); 18 | print(list.length); 19 | return list; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/repositories/manga_detail_repo.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:mangamint/constants/base_url.dart'; 5 | import 'package:mangamint/models/manga_detail_model.dart'; 6 | import 'package:mangamint/service/api_service.dart'; 7 | import 'package:mangamint/service/base_service.dart'; 8 | 9 | class MangaDetailRepo extends BaseService{ 10 | FuturegetMangaDetail(String endpoint)async{ 11 | final Response response = await request(url: 'manga/detail/$endpoint'); 12 | final MangaDetailModel data = MangaDetailModel.fromJson(response.data); 13 | print(response.data); 14 | return data; 15 | // final response = await ApiService.api.get(BaseUrl+'manga/detail/$endpoint'); 16 | // 17 | // if(response.statusCode == 200){ 18 | // final res = json.decode(response.body); 19 | // final MangaDetailModel data = MangaDetailModel.fromJson(res); 20 | // print(data.chapterList[0].chapter_title); 21 | // return data; 22 | // } 23 | } 24 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Exceptions to above rules. 44 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 45 | 46 | android/app/google-services.json 47 | android/app/src/main/google-services.json 48 | /android/key.properties -------------------------------------------------------------------------------- /lib/bloc/manhua_bloc/manhua_state.dart: -------------------------------------------------------------------------------- 1 | part of 'manhua_bloc.dart'; 2 | 3 | @immutable 4 | abstract class ManhuaState extends Equatable{ 5 | const ManhuaState(); 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class InitialManhuaState extends ManhuaState {} 11 | class ManhuaLoadingState extends ManhuaState {} 12 | class ManhuaLoadedState extends ManhuaState { 13 | final Listlist; 14 | final bool hasReachedMax; 15 | int page = 0; 16 | 17 | ManhuaLoadedState({this.list, this.hasReachedMax, this.page}); 18 | ManhuaLoadedState copyWith({ 19 | Listlist,bool hasReachedMax 20 | }){ 21 | return ManhuaLoadedState( 22 | list: list ?? this.list, 23 | hasReachedMax: hasReachedMax ?? this.hasReachedMax 24 | ); 25 | } 26 | @override 27 | List get props => [list,hasReachedMax,page]; 28 | } 29 | class ManhuaFailure extends ManhuaState { 30 | final String msg; 31 | 32 | ManhuaFailure({this.msg}); 33 | @override 34 | List get props => [msg]; 35 | } -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /lib/models/genre_list_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class GeneListModel extends Equatable{ 4 | String genre_name,endpoint; 5 | 6 | GeneListModel({this.genre_name, this.endpoint}); 7 | 8 | factory GeneListModel.fromMap(Mapjson){ 9 | return GeneListModel( 10 | endpoint: json['endpoint'], 11 | genre_name: json['genre_name'] 12 | ); 13 | } 14 | @override 15 | List get props => []; 16 | } 17 | class MangaByGenreModel extends Equatable{ 18 | String title,thumb,type,endpoint; 19 | num score; 20 | 21 | MangaByGenreModel({this.title, this.thumb, this.type, this.endpoint, this.score}); 22 | 23 | factory MangaByGenreModel.fromJson(Mapjson){ 24 | return MangaByGenreModel( 25 | title: json['title'], 26 | type: json['type'], 27 | thumb: json['thumb'], 28 | score: json['score'], 29 | endpoint: json['endpoint'] 30 | ); 31 | } 32 | 33 | @override 34 | List get props => [title,thumb,endpoint,score,type]; 35 | } -------------------------------------------------------------------------------- /lib/helper/hive/hive_chapter_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'hive_chapter_model.dart'; 4 | 5 | // ************************************************************************** 6 | // TypeAdapterGenerator 7 | // ************************************************************************** 8 | 9 | class HiveChapterModelAdapter extends TypeAdapter { 10 | @override 11 | HiveChapterModel read(BinaryReader reader) { 12 | var numOfFields = reader.readByte(); 13 | var fields = { 14 | for (var i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), 15 | }; 16 | return HiveChapterModel( 17 | endpoint: fields[0] as String, 18 | index: fields[1] as int, 19 | ); 20 | } 21 | 22 | @override 23 | void write(BinaryWriter writer, HiveChapterModel obj) { 24 | writer 25 | ..writeByte(2) 26 | ..writeByte(0) 27 | ..write(obj.endpoint) 28 | ..writeByte(1) 29 | ..write(obj.index); 30 | } 31 | @override 32 | int get typeId => 1; 33 | } 34 | -------------------------------------------------------------------------------- /lib/repositories/search_repo.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:mangamint/constants/base_url.dart'; 5 | import 'package:mangamint/models/search_model.dart'; 6 | import 'package:mangamint/service/api_service.dart'; 7 | import 'package:mangamint/service/base_service.dart'; 8 | 9 | class SearchRepo extends BaseService{ 10 | Future>searchManga({String query})async{ 11 | final Response response = await request(url:'search/$query'); 12 | List list = List.from(response.data['manga_list'].map((json) => SearchModel.fromJson(json))); 13 | print(response.data); 14 | return list; 15 | // final response = await ApiService.api.get(BaseUrl+'cari/$query'); 16 | // if(response.statusCode == 200){ 17 | // print(response.body); 18 | // var res = json.decode(response.body); 19 | // List list = List.from(res.map((json) => SearchModel.fromJson(json))); 20 | // print(list[0].type); 21 | // return list; 22 | // } 23 | } 24 | } -------------------------------------------------------------------------------- /lib/models/chapter_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class ChapterModel{ 4 | String title,chapter_endpoint; 5 | ListchapterImage; 6 | 7 | ChapterModel({this.title, this.chapterImage,this.chapter_endpoint}); 8 | 9 | factory ChapterModel.fromJson(Mapjson){ 10 | return ChapterModel( 11 | title: json['title'], 12 | chapter_endpoint: json['chapter_endpoint'], 13 | chapterImage: List.from(json['chapter_image'].map((json) => ChapterImage.fromMap(json))) 14 | ); 15 | } 16 | 17 | @override 18 | List get props => [title,chapterImage]; 19 | 20 | } 21 | class ChapterImage extends Equatable{ 22 | String chapter_image_link; 23 | int number; 24 | 25 | ChapterImage({this.number, this.chapter_image_link}); 26 | 27 | factory ChapterImage.fromMap(Mapjson){ 28 | return ChapterImage( 29 | number: json['image_number'], 30 | chapter_image_link: json['chapter_image_link'] 31 | ); 32 | } 33 | 34 | @override 35 | List get props => [number,chapter_image_link]; 36 | } -------------------------------------------------------------------------------- /lib/models/manga_list_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class MangaModel { 4 | List mangaList; 5 | 6 | MangaModel({this.mangaList}); 7 | 8 | factory MangaModel.fromJson(Mapjson){ 9 | return MangaModel( 10 | mangaList: List.from(json['manga_list'].map((item) => MangaListModel.fromMap(item))) 11 | ); 12 | } 13 | } 14 | 15 | class MangaListModel extends Equatable{ 16 | String title,thumb,type,endpoint,updated_on,chapter; 17 | num score; 18 | 19 | MangaListModel({this.title, this.thumb, this.type, this.score, this.endpoint, 20 | this.updated_on,this.chapter}); 21 | 22 | @override 23 | List get props => [title,thumb,type,score,endpoint]; 24 | 25 | factory MangaListModel.fromMap(Mapjson){ 26 | return MangaListModel( 27 | title: json['title'], 28 | thumb: json['thumb'], 29 | type: json['type'], 30 | chapter: json['chapter'], 31 | updated_on: json['updated_on'], 32 | score: json['score'], 33 | endpoint: json['endpoint'] 34 | ); 35 | } 36 | } -------------------------------------------------------------------------------- /lib/repositories/popular_repo.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:mangamint/constants/base_url.dart'; 5 | import 'package:mangamint/models/popular_model.dart'; 6 | import 'package:mangamint/service/api_service.dart'; 7 | import 'package:mangamint/service/base_service.dart'; 8 | 9 | class PopularRepo extends BaseService{ 10 | Future>getPopular({int page})async{ 11 | Response response = await request(url: 'manga/popular/$page'); 12 | final List res = List.from(response.data['manga_list'].map((json)=>PopularModel.fromJson(json))); 13 | return res; 14 | // final response = await ApiService.api.get(BaseUrl+'manga/popular/$page'); 15 | // if(response.statusCode == 200){ 16 | // var res = json.decode(response.body)['manga_list']; 17 | // List list = List.from(res.map((json) => PopularModel.fromJson(json))); 18 | // print(page); 19 | // print(list[0].title); 20 | // return list; 21 | // }else{ 22 | // throw Exception('Failed Fetch'); 23 | // } 24 | } 25 | } -------------------------------------------------------------------------------- /lib/models/recomended_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class RecommendedModel extends Equatable{ 4 | ListrecommendedList; 5 | 6 | RecommendedModel({this.recommendedList}); 7 | 8 | factory RecommendedModel.fromJson(Mapjson){ 9 | return RecommendedModel( 10 | recommendedList:List.from(json['manga_list'].map((json) => RecommendedList.fromMap(json))) 11 | ); 12 | } 13 | 14 | @override 15 | List get props => []; 16 | } 17 | 18 | class RecommendedList extends Equatable{ 19 | String title,endpoint,thumb,type; 20 | String update; 21 | 22 | RecommendedList({this.title, this.endpoint, this.thumb, this.type, 23 | this.update}); 24 | 25 | 26 | factory RecommendedList.fromMap(Mapjson){ 27 | return RecommendedList( 28 | title: json['title'], 29 | endpoint: json['endpoint'], 30 | thumb: json['thumb'], 31 | type: json['type'], 32 | update: json['update'] 33 | ); 34 | } 35 | 36 | @override 37 | List get props => [title,endpoint,thumb,type,update]; 38 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Febry Ardiansyah 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/bloc/popular_bloc/popular_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:mangamint/models/popular_model.dart'; 3 | 4 | abstract class PopularState extends Equatable { 5 | const PopularState(); 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class InitialPopularState extends PopularState {} 11 | class PopularLoadingState extends PopularState{} 12 | class PopularLoadedState extends PopularState{ 13 | final ListpopularList; 14 | final bool hasReachedMax; 15 | int page = 0; 16 | 17 | PopularLoadedState({this.popularList, this.hasReachedMax,this.page}); 18 | PopularLoadedState copyWith({ListpopularList,bool hasReachedMax}){ 19 | return PopularLoadedState( 20 | popularList: popularList ?? this.popularList, 21 | hasReachedMax: hasReachedMax ?? this.hasReachedMax 22 | ); 23 | } 24 | @override 25 | List get props => [popularList,page,hasReachedMax]; 26 | } 27 | class PopularFailureState extends PopularState{ 28 | final String msg; 29 | 30 | PopularFailureState({this.msg}); 31 | @override 32 | List get props => [msg]; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /lib/components/my_body.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:mangamint/constants/base_color.dart'; 3 | 4 | class MyBody extends StatelessWidget { 5 | final Widget title; 6 | final Widget body; 7 | final GestureTapCallback onRefresh,onSearch; 8 | final PreferredSizeWidget bottomAppBar; 9 | final bool showRefresh,showSearch; 10 | 11 | const MyBody({Key key, this.title, this.body, this.onRefresh,this.bottomAppBar, 12 | this.showRefresh = true,this.onSearch,this.showSearch = true,}) : super(key: key); 13 | @override 14 | Widget build(BuildContext context) { 15 | return Scaffold( 16 | appBar: AppBar( 17 | bottom: bottomAppBar, 18 | title: title, 19 | actions: [ 20 | showRefresh?IconButton( 21 | onPressed: onRefresh, 22 | icon: Icon(Icons.refresh), 23 | ):Center(), 24 | showSearch?IconButton( 25 | onPressed: (){ 26 | Navigator.pushNamed(context, '/search',); 27 | }, 28 | icon: Icon(Icons.search), 29 | ):Center(), 30 | ], 31 | ), 32 | body: body, 33 | ); 34 | } 35 | } 36 | 37 | 38 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:mangamint/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /lib/repositories/manhua_manhwa_repo.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:mangamint/constants/base_url.dart'; 5 | import 'package:mangamint/models/manhua_manhwa_model.dart'; 6 | import 'package:mangamint/service/api_service.dart'; 7 | import 'package:mangamint/service/base_service.dart'; 8 | 9 | class ManhuaManhwaRepo extends BaseService{ 10 | Future>getManhuaManhwa({String type,int page})async{ 11 | final Response response = await request(url: '$type/$page'); 12 | List list = List.from(response.data['manga_list'].map((json)=> 13 | ManhuaManwaModel.fromJson(json))); 14 | return list; 15 | // final response = await ApiService.api.get('${BaseUrl}$type/$page'); 16 | // if(response.statusCode == 200){ 17 | // var res = json.decode(response.body)['manga_list']; 18 | // List list = List.from(res.map((json)=> 19 | // ManhuaManwaModel.fromJson(json))); 20 | // print(page); 21 | // print(list[1].type); 22 | // return list; 23 | // }else{ 24 | // throw Exception('failed fetch'); 25 | // } 26 | } 27 | } -------------------------------------------------------------------------------- /lib/bloc/mangabygenre_bloc/manga_by_genre_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:mangamint/models/genre_list_model.dart'; 3 | 4 | abstract class MangaByGenreState extends Equatable { 5 | const MangaByGenreState(); 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class InitialMangaByGenreState extends MangaByGenreState {} 11 | class MangaByGenreLoadingState extends MangaByGenreState{} 12 | class MangaByGenreLoadedState extends MangaByGenreState{ 13 | final List list; 14 | final bool hasReachedMax; 15 | int page = 0; 16 | 17 | MangaByGenreLoadedState({this.list,this.page,this.hasReachedMax}); 18 | MangaByGenreLoadedState copyWith({List list,bool hasReachedMax}){ 19 | return MangaByGenreLoadedState( 20 | hasReachedMax: hasReachedMax ?? this.hasReachedMax, 21 | list: list ?? this.list 22 | ); 23 | } 24 | @override 25 | List get props => [list,hasReachedMax,page]; 26 | 27 | } 28 | class MangaByGenreFailureState extends MangaByGenreState{ 29 | final String msg; 30 | 31 | MangaByGenreFailureState({this.msg}); 32 | @override 33 | List get props => [msg]; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /lib/helper/hive/hive_chapter_opened_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'hive_chapter_opened_model.dart'; 4 | 5 | // ************************************************************************** 6 | // TypeAdapterGenerator 7 | // ************************************************************************** 8 | 9 | class HiveChapterOpenedModelAdapter 10 | extends TypeAdapter { 11 | @override 12 | HiveChapterOpenedModel read(BinaryReader reader) { 13 | var numOfFields = reader.readByte(); 14 | var fields = { 15 | for (var i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), 16 | }; 17 | return HiveChapterOpenedModel( 18 | lastChapter: fields[0] as int, 19 | manga_endpoint: fields[1] as String, 20 | )..chapter_endpoint = fields[2] as String; 21 | } 22 | 23 | @override 24 | void write(BinaryWriter writer, HiveChapterOpenedModel obj) { 25 | writer 26 | ..writeByte(3) 27 | ..writeByte(0) 28 | ..write(obj.lastChapter) 29 | ..writeByte(1) 30 | ..write(obj.manga_endpoint) 31 | ..writeByte(2) 32 | ..write(obj.chapter_endpoint); 33 | } 34 | @override 35 | int get typeId => 2; 36 | } 37 | -------------------------------------------------------------------------------- /lib/bloc/manga_list_bloc/manga_list_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:mangamint/models/manga_list_model.dart'; 3 | import 'package:meta/meta.dart'; 4 | 5 | @immutable 6 | abstract class MangaListState extends Equatable { 7 | const MangaListState(); 8 | 9 | List get props => []; 10 | } 11 | 12 | class InitialMangaListState extends MangaListState {} 13 | 14 | class MangaListLoadingState extends MangaListState {} 15 | 16 | class MangaListStateLoaded extends MangaListState { 17 | final List mangaList; 18 | final bool hasReachedMax; 19 | int page = 0; 20 | 21 | MangaListStateLoaded({this.mangaList,this.hasReachedMax,this.page}); 22 | 23 | MangaListStateLoaded copyWith({ListmangaList,bool hasReachedMax}){ 24 | return MangaListStateLoaded( 25 | mangaList: mangaList ?? this.mangaList, 26 | hasReachedMax: hasReachedMax ?? this.hasReachedMax, 27 | ); 28 | } 29 | 30 | @override 31 | List get props => [mangaList,hasReachedMax]; 32 | } 33 | 34 | class MangaListStateFailure extends MangaListState { 35 | final String msg; 36 | 37 | MangaListStateFailure({this.msg}); 38 | 39 | @override 40 | List get props => [msg]; 41 | } 42 | -------------------------------------------------------------------------------- /lib/helper/hive/hive_manga_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'hive_manga_model.dart'; 4 | 5 | // ************************************************************************** 6 | // TypeAdapterGenerator 7 | // ************************************************************************** 8 | 9 | class HiveMangaModelAdapter extends TypeAdapter { 10 | @override 11 | HiveMangaModel read(BinaryReader reader) { 12 | var numOfFields = reader.readByte(); 13 | var fields = { 14 | for (var i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), 15 | }; 16 | return HiveMangaModel( 17 | manga_endpoint: fields[0] as String, 18 | thumb: fields[1] as String, 19 | type: fields[2] as String, 20 | title: fields[3] as String, 21 | ); 22 | } 23 | 24 | @override 25 | void write(BinaryWriter writer, HiveMangaModel obj) { 26 | writer 27 | ..writeByte(4) 28 | ..writeByte(0) 29 | ..write(obj.manga_endpoint) 30 | ..writeByte(1) 31 | ..write(obj.thumb) 32 | ..writeByte(2) 33 | ..write(obj.type) 34 | ..writeByte(3) 35 | ..write(obj.title); 36 | } 37 | 38 | @override 39 | int get typeId => 0; 40 | } 41 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | mangamint 18 | 19 | 20 | 21 | 24 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /lib/components/image_cache_loading.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 5 | import 'package:flutter_spinkit/flutter_spinkit.dart'; 6 | import 'package:mangamint/constants/base_color.dart'; 7 | 8 | class ImageCacheLoading extends StatelessWidget { 9 | final String imgUrl; 10 | final ImageWidgetBuilder imageBuilder; 11 | final double width, height; 12 | 13 | const ImageCacheLoading({Key key, this.imgUrl, this.imageBuilder, 14 | this.height,this.width}) 15 | : assert(imgUrl != null), 16 | assert(imageBuilder != null), 17 | super(key: key); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return CachedNetworkImage( 22 | imageUrl: imgUrl, 23 | height: height ?? 300.h, 24 | width: width ?? 300.w, 25 | errorWidget: (context, i, _) => Center( 26 | child: Icon(Icons.error), 27 | ), 28 | placeholder: (context, _) => Center( 29 | child: SpinKitRipple( 30 | color: BaseColor.red, 31 | ), 32 | ), 33 | imageBuilder: imageBuilder); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/bloc/genre_list_bloc/genre_list_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:bloc/bloc.dart'; 3 | import 'package:mangamint/models/genre_list_model.dart'; 4 | import 'package:mangamint/repositories/genre_list_repo.dart'; 5 | import './bloc.dart'; 6 | 7 | class GenreListBloc extends Bloc { 8 | GenreListRepo _genreListRepo; 9 | 10 | GenreListBloc(this._genreListRepo) : super(InitialGenreListState()); 11 | 12 | @override 13 | Stream mapEventToState( 14 | GenreListEvent event, 15 | ) async* { 16 | final currentState = state; 17 | if(event is FetchGenreList){ 18 | try{ 19 | if(currentState is InitialGenreListState){ 20 | yield GenreListLoadingState(); 21 | Listlist = await _genreListRepo.getGenreList(); 22 | yield GenreListLoadedState(genreList: list); 23 | } 24 | if(currentState is GenreListLoadedState){ 25 | yield GenreListLoadedState(genreList: currentState.genreList); 26 | } 27 | }catch(e){ 28 | yield GenreListFailureState(msg: e.toString()); 29 | } 30 | } 31 | if(event is RefreshGenreList){ 32 | yield GenreListLoadingState(); 33 | try{ 34 | Listlist = await _genreListRepo.getGenreList(); 35 | yield GenreListLoadedState(genreList: list); 36 | }catch(e){ 37 | yield GenreListFailureState(msg: e.toString()); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/bloc/recomended_bloc/recomended_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:bloc/bloc.dart'; 3 | import 'package:mangamint/models/recomended_model.dart'; 4 | import 'package:mangamint/repositories/recommended_repo.dart'; 5 | import './bloc.dart'; 6 | 7 | class RecomendedBloc extends Bloc { 8 | RecommendedRepo _recommendedRepo; 9 | 10 | RecomendedBloc(this._recommendedRepo) : super(InitialRecomendedState()); 11 | 12 | @override 13 | Stream mapEventToState( 14 | RecomendedEvent event, 15 | ) async* { 16 | final currentState = state; 17 | if(event is FetchRecommended){ 18 | try{ 19 | if(currentState is InitialRecomendedState){ 20 | yield RecommendedLoadingState(); 21 | List list = await _recommendedRepo.getRecomended(); 22 | yield RecommendedLoadedState(recommendedList: list); 23 | } 24 | if(currentState is RecommendedLoadedState){ 25 | yield RecommendedLoadedState(recommendedList: currentState.recommendedList); 26 | } 27 | 28 | }catch(e){ 29 | yield RecomendedFailureState(msg: e.toString()); 30 | } 31 | } 32 | if(event is RefreshRecommended){ 33 | try{ 34 | yield RecommendedLoadingState(); 35 | List list = await _recommendedRepo.getRecomended(); 36 | yield RecommendedLoadedState(recommendedList: list); 37 | }catch(e){ 38 | yield RecomendedFailureState(msg: e.toString()); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/components/build_error.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:mangamint/constants/base_color.dart'; 4 | 5 | class BuildError extends StatelessWidget { 6 | final String msg; 7 | final Function onRefresh; 8 | 9 | const BuildError({Key key, this.msg, this.onRefresh}) : super(key: key); 10 | @override 11 | Widget build(BuildContext context) { 12 | return Scaffold( 13 | body: Center( 14 | child: Column( 15 | crossAxisAlignment: CrossAxisAlignment.center, 16 | mainAxisAlignment: MainAxisAlignment.center, 17 | children: [ 18 | Text(msg ?? 'Njir, mungkin server bermasalah\nklo gk lu gk ada paketan 😞',textAlign: TextAlign.center,), 19 | Padding( 20 | padding: EdgeInsets.only(top: 8), 21 | child: Row( 22 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 23 | children: [ 24 | FlatButton( 25 | child: Text('Kembali',style: TextStyle(color: Colors.white),), 26 | color: BaseColor.red, 27 | onPressed: (){ 28 | Navigator.pop(context); 29 | }, 30 | ), 31 | FlatButton( 32 | color: BaseColor.green, 33 | child: Text('Muat ulang cuy !!',style: TextStyle(color: Colors.white),), 34 | onPressed: onRefresh, 35 | ) 36 | ], 37 | ) 38 | ) 39 | ], 40 | ) 41 | ), 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/screens/result_screen/search_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:mangamint/constants/base_color.dart'; 3 | 4 | class SearchScreen extends StatefulWidget { 5 | @override 6 | _SearchScreenState createState() => _SearchScreenState(); 7 | } 8 | 9 | class _SearchScreenState extends State { 10 | TextEditingController _txtEditingCtrl = TextEditingController(); 11 | @override 12 | void dispose() { 13 | super.dispose(); 14 | _txtEditingCtrl.dispose(); 15 | } 16 | @override 17 | Widget build(BuildContext context) { 18 | return Scaffold( 19 | backgroundColor: BaseColor.grey1, 20 | appBar: AppBar( 21 | elevation: 0, 22 | title: TextFormField( 23 | autofocus: true, 24 | controller: _txtEditingCtrl, 25 | keyboardType: TextInputType.emailAddress, 26 | textInputAction: TextInputAction.go, 27 | onFieldSubmitted: (value){ 28 | print(value); 29 | Navigator.pushNamed(context, '/result',arguments: value); 30 | }, 31 | decoration: InputDecoration( 32 | hintText: 'Cari manga kesuakaan mu ..', 33 | border: InputBorder.none, 34 | suffix:FlatButton( 35 | shape: RoundedRectangleBorder( 36 | borderRadius: BorderRadius.circular(8) 37 | ), 38 | child: Text('Clear'), 39 | onPressed: (){ 40 | _txtEditingCtrl.clear(); 41 | }, 42 | ) 43 | ), 44 | ), 45 | ), 46 | body: Center(), 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | mangamint 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /lib/screens/detail_screen/index_detail.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:mangamint/bloc/manga_detail_bloc/bloc.dart'; 4 | import 'package:mangamint/components/build_error.dart'; 5 | import 'package:mangamint/components/loading_dialog.dart'; 6 | import 'package:mangamint/screens/detail_screen/manga_detail_screen.dart'; 7 | 8 | class IndexDetail extends StatefulWidget { 9 | final String endpoint; 10 | 11 | const IndexDetail({Key key, this.endpoint}) : super(key: key); 12 | 13 | @override 14 | _IndexDetailState createState() => _IndexDetailState(); 15 | } 16 | 17 | class _IndexDetailState extends State { 18 | MangaDetailBloc _mangaDetailBloc; 19 | String get endpoint => widget.endpoint; 20 | @override 21 | void initState() { 22 | super.initState(); 23 | _mangaDetailBloc = BlocProvider.of(context); 24 | _mangaDetailBloc.add(FetchMangaDetail(endpoint)); 25 | } 26 | @override 27 | Widget build(BuildContext context) { 28 | return Scaffold( 29 | body: BlocBuilder( 30 | builder: (context, state){ 31 | if(state is MangaDetailLoadingState){ 32 | return LoadingDialog(); 33 | }else if(state is MangaDetailLoadedState){ 34 | return MangaDetailScreen(data:state.data); 35 | }else if(state is MangaDetailFailureState){ 36 | return BuildError(msg: state.msg,onRefresh: (){ 37 | context.bloc().add(FetchMangaDetail(widget.endpoint)); 38 | },); 39 | } 40 | return Container(); 41 | }, 42 | ), 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/bloc/manhuamanhwa/manhuamanhwa_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:mangamint/models/manhua_manhwa_model.dart'; 3 | 4 | abstract class ManhuamanhwaState extends Equatable { 5 | const ManhuamanhwaState(); 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class InitialManhuamanhwaState extends ManhuamanhwaState {} 11 | class ManhuaManhwaLoadingState extends ManhuamanhwaState{} 12 | class ManhuaLoadedState extends ManhuamanhwaState{ 13 | final Listlist; 14 | final bool hasReachedMax; 15 | int page = 0; 16 | 17 | ManhuaLoadedState({this.list, this.hasReachedMax, this.page}); 18 | ManhuaLoadedState copyWith({ 19 | Listlist,bool hasReachedMax 20 | }){ 21 | return ManhuaLoadedState( 22 | list: list ?? this.list, 23 | hasReachedMax: hasReachedMax ?? this.hasReachedMax 24 | ); 25 | } 26 | @override 27 | List get props => [list,hasReachedMax,page]; 28 | } 29 | class ManhwaLoadedState extends ManhuamanhwaState{ 30 | final Listlist; 31 | final bool hasReachedMax; 32 | int page = 0; 33 | 34 | ManhwaLoadedState({this.list, this.hasReachedMax, this.page}); 35 | ManhwaLoadedState copyWith({ 36 | Listlist,bool hasReachedMax 37 | }){ 38 | return ManhwaLoadedState( 39 | list: list ?? this.list, 40 | hasReachedMax: hasReachedMax ?? this.hasReachedMax 41 | ); 42 | } 43 | @override 44 | List get props => [list,hasReachedMax,page]; 45 | } 46 | class ManhuaManhwaFailureState extends ManhuamanhwaState{ 47 | final String msg; 48 | 49 | ManhuaManhwaFailureState({this.msg}); 50 | @override 51 | List get props => [msg]; 52 | } 53 | -------------------------------------------------------------------------------- /lib/helper/routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:mangamint/components/bottom_nav_bar.dart'; 4 | import 'package:mangamint/components/splash_screen.dart'; 5 | import 'package:mangamint/screens/chapter_screen/index_chapter.dart'; 6 | import 'package:mangamint/screens/detail_screen/index_detail.dart'; 7 | import 'package:mangamint/screens/result_screen/manga_by_genre_screen.dart'; 8 | import 'package:mangamint/screens/result_screen/result_screen.dart'; 9 | import 'package:mangamint/screens/result_screen/search_screen.dart'; 10 | import 'package:mangamint/screens/result_screen/terpopuler_screen.dart'; 11 | 12 | MaterialPageRoute pageRoute({RouteSettings routeSettings,Widget builder,bool isFullscreen = false}){ 13 | return MaterialPageRoute( 14 | builder: (BuildContext context) => builder, 15 | settings: routeSettings, 16 | fullscreenDialog: isFullscreen, 17 | ); 18 | } 19 | Route generateRoute(RouteSettings routeSettings){ 20 | final args = routeSettings.arguments; 21 | switch(routeSettings.name){ 22 | case '/': 23 | return pageRoute(routeSettings: routeSettings,builder: SplashScreen()); 24 | case '/navbar': 25 | return pageRoute(routeSettings: routeSettings,builder: BottomNavBar()); 26 | case '/detailmanga': 27 | return pageRoute(routeSettings: routeSettings,builder: IndexDetail(endpoint: args,)); 28 | case '/chapter': 29 | return pageRoute(routeSettings: routeSettings,builder: IndexChapter(endpoint: args,),isFullscreen: true); 30 | case '/popular': 31 | return pageRoute(routeSettings: routeSettings,builder: TerpopulerScreen()); 32 | case '/search': 33 | return pageRoute(routeSettings: routeSettings,builder: SearchScreen(),isFullscreen: true); 34 | case '/result': 35 | return pageRoute(routeSettings: routeSettings,builder: ResultScreen(query: args,)); 36 | case '/mangabygenre': 37 | return pageRoute(routeSettings: routeSettings,builder: MangaByGenreScreen(endpoint: args,)); 38 | } 39 | } -------------------------------------------------------------------------------- /lib/bloc/manhua_bloc/manhua_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:bloc/bloc.dart'; 3 | import 'package:equatable/equatable.dart'; 4 | import 'package:mangamint/models/manhua_manhwa_model.dart'; 5 | import 'package:mangamint/repositories/manhua_manhwa_repo.dart'; 6 | import 'package:meta/meta.dart'; 7 | 8 | part 'manhua_event.dart'; 9 | 10 | part 'manhua_state.dart'; 11 | 12 | class ManhuaBloc extends Bloc { 13 | ManhuaManhwaRepo _manhuaManhwaRepo; 14 | 15 | ManhuaBloc(this._manhuaManhwaRepo) : super(InitialManhuaState()); 16 | 17 | bool _hasReachedMax(ManhuaLoadedState state) => 18 | state is ManhuaLoadedState && state.hasReachedMax; 19 | 20 | @override 21 | Stream mapEventToState(ManhuaEvent event) async* { 22 | final currentState = state; 23 | int page = 1; 24 | if(event is InitialFetchManhua){} 25 | if (event is FetchManhua && !_hasReachedMax(currentState)) { 26 | if (currentState is InitialManhuaState) { 27 | yield ManhuaLoadingState(); 28 | try { 29 | List manhuaList = await _manhuaManhwaRepo 30 | .getManhuaManhwa(page: page, type: 'manhua'); 31 | yield ManhuaLoadedState( 32 | list: manhuaList, page: page += 1, hasReachedMax: false); 33 | } catch (e) { 34 | yield ManhuaFailure(msg: e.toString()); 35 | } 36 | } 37 | 38 | if (currentState is ManhuaLoadedState) { 39 | try{ 40 | List list = await _manhuaManhwaRepo.getManhuaManhwa( 41 | type: 'manhua', 42 | page: currentState.page, 43 | ); 44 | yield list.isEmpty 45 | ? currentState.copyWith(hasReachedMax: true) 46 | : ManhuaLoadedState( 47 | list: currentState.list + list, 48 | page: currentState.page += 1, 49 | hasReachedMax: false); 50 | }catch(e){ 51 | yield ManhuaFailure(msg: e.toString()); 52 | } 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/components/item_small.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:mangamint/components/image_cache_loading.dart'; 5 | import 'package:mangamint/constants/base_color.dart'; 6 | 7 | class ItemSmall extends StatelessWidget { 8 | final String title, thumb, subtitle, bottom; 9 | final GestureTapCallback onTap; 10 | 11 | ItemSmall({this.title, this.thumb, this.subtitle, this.bottom,this.onTap}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | final size = MediaQuery.of(context).size; 16 | ScreenUtil.init(); 17 | return InkWell( 18 | onTap: onTap, 19 | child: Padding( 20 | padding: const EdgeInsets.only(left: 10), 21 | child: Column( 22 | crossAxisAlignment: CrossAxisAlignment.start, 23 | children: [ 24 | ClipRRect( 25 | borderRadius: BorderRadius.all(Radius.circular(8)), 26 | child: ImageCacheLoading( 27 | imgUrl: thumb, 28 | height: size.height, 29 | width: size.width, 30 | imageBuilder: (context,imgProvider){ 31 | return Container( 32 | height: 300.h, 33 | width: 300.w, 34 | decoration: BoxDecoration( 35 | image: DecorationImage( 36 | image: imgProvider, 37 | fit: BoxFit.fill 38 | ) 39 | ), 40 | ); 41 | }, 42 | )), 43 | SizedBox(height: 10,), 44 | Text( 45 | title.length > 15 ? '${title.substring(0, 15)}..' : title, 46 | style: TextStyle(fontWeight: FontWeight.bold), 47 | ), 48 | Text(subtitle,style: TextStyle(color: BaseColor.red),), 49 | Expanded(child: Text(bottom,style: TextStyle(color: BaseColor.grey1),)) 50 | ], 51 | ), 52 | ), 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/components/splash_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:flutter_spinkit/flutter_spinkit.dart'; 5 | import 'package:google_fonts/google_fonts.dart'; 6 | import 'package:mangamint/constants/base_color.dart'; 7 | 8 | class SplashScreen extends StatefulWidget { 9 | @override 10 | _SplashScreenState createState() => _SplashScreenState(); 11 | } 12 | 13 | class _SplashScreenState extends State { 14 | startTime()async{ 15 | Future.delayed(Duration(seconds: 3),(){ 16 | Navigator.pushReplacementNamed(context, '/navbar'); 17 | }); 18 | } 19 | @override 20 | void initState() { 21 | super.initState(); 22 | startTime(); 23 | } 24 | @override 25 | Widget build(BuildContext context) { 26 | ScreenUtil.init(); 27 | return Scaffold( 28 | body: Center( 29 | child: Column( 30 | mainAxisAlignment: MainAxisAlignment.center, 31 | children: [ 32 | Stack( 33 | alignment: Alignment.center, 34 | children: [ 35 | Container( 36 | width: MediaQuery.of(context).size.width, 37 | height: 120, 38 | ), 39 | Positioned( 40 | top: 20, 41 | left: 240.w, 42 | child: Text('MangaKah ?',style: TextStyle(color: BaseColor.grey1),)), 43 | Positioned( 44 | right: 20, 45 | top: 0, 46 | child: Image.asset('assets/images/aqua-chibi.png',fit: BoxFit.fill,height: 100,width: 100)), 47 | Text('MangaMint',style: GoogleFonts.modak( 48 | color: BaseColor.red,fontSize: 45 49 | ),), 50 | ], 51 | ), 52 | Text('Baca Manga & Komik\nBahasa Indonesia',textAlign: TextAlign.center,), 53 | SizedBox(height: 40,), 54 | SpinKitThreeBounce(color: BaseColor.red,size: 30,) 55 | ], 56 | ) 57 | ), 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/models/manga_detail_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class MangaDetailModel{ 4 | String title,thumb,status,type,author, 5 | updated_on,posted_on,synopsis,manga_endpoint; 6 | num score,released; 7 | ListgenreList; 8 | ListchapterList; 9 | 10 | 11 | MangaDetailModel({this.title, this.thumb, this.status, this.released, 12 | this.type, this.author, this.updated_on, this.posted_on, this.synopsis, 13 | this.score, this.genreList, this.chapterList,this.manga_endpoint}); 14 | 15 | factory MangaDetailModel.fromJson(Mapjson){ 16 | return MangaDetailModel( 17 | title: json['title'], 18 | thumb: json['thumb'], 19 | type: json['type'], 20 | author: json['author'], 21 | posted_on: json['posted_on'], 22 | released: json['released'], 23 | score: json['score'], 24 | status: json['status'], 25 | synopsis: json['synopsis'], 26 | updated_on: json['updated_on'], 27 | manga_endpoint: json['manga_endpoint'], 28 | genreList: List.from(json['genre_list'].map((item) => Genres.fromMap(item))) ?? [], 29 | chapterList: List.from(json['chapter'].map((item)=>ChapterList.fromMap(item))), 30 | ); 31 | } 32 | 33 | } 34 | class Genres extends Equatable { 35 | String genre_name; 36 | 37 | Genres({this.genre_name}); 38 | 39 | factory Genres.fromMap(Mapjson){ 40 | return Genres( 41 | genre_name: json['genre_name'] 42 | ); 43 | } 44 | 45 | @override 46 | List get props => [genre_name]; 47 | 48 | 49 | } 50 | class ChapterList extends Equatable{ 51 | String chapter_title,chapter_endpoint,chapter_uploaded; 52 | 53 | ChapterList({this.chapter_title, this.chapter_endpoint, this.chapter_uploaded}); 54 | 55 | @override 56 | List get props => [chapter_endpoint,chapter_title,chapter_uploaded]; 57 | 58 | factory ChapterList.fromMap(Mapjson){ 59 | return ChapterList( 60 | chapter_endpoint: json['chapter_endpoint'], 61 | chapter_title: json['chapter_title'], 62 | chapter_uploaded: json['chapter_uploaded'] 63 | ); 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /lib/screens/home_screens/terbaru.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:mangamint/bloc/manga_list_bloc/bloc.dart'; 4 | import 'package:mangamint/components/item_big.dart'; 5 | import 'package:mangamint/components/my_shimmer.dart'; 6 | import 'package:mangamint/constants/base_color.dart'; 7 | 8 | class TerbaruCategory extends StatelessWidget { 9 | @override 10 | Widget build(BuildContext context) { 11 | return Padding( 12 | padding: const EdgeInsets.all(8.0), 13 | child: BlocBuilder( 14 | builder: (context,state){ 15 | if (state is MangaListLoadingState || state is MangaListStateFailure) { 16 | return MyShimmer( 17 | child: GridView.builder( 18 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( 19 | crossAxisCount: 3,mainAxisSpacing: 1,crossAxisSpacing: 2, 20 | ), 21 | shrinkWrap: true, 22 | physics: ClampingScrollPhysics(), 23 | itemCount: 10, 24 | itemBuilder:(context,i)=> Container( 25 | color: BaseColor.red, 26 | width: MediaQuery.of(context).size.width, 27 | height: 100, 28 | ), 29 | ), 30 | ); 31 | }else if (state is MangaListStateLoaded) { 32 | var mystate = state.mangaList; 33 | return ItemBig( 34 | itemCount: mystate.getRange(0, 16).length, 35 | itemBuilder: (context,i){ 36 | var index = mystate[i]; 37 | return ItemBigChild( 38 | onTap: (){ 39 | Navigator.pushNamed(context, '/detailmanga',arguments: index.endpoint); 40 | }, 41 | title: index.title, 42 | thumb: index.thumb, 43 | type: index.type, 44 | chapter: index.chapter, 45 | update: index.updated_on, 46 | ); 47 | }, 48 | ); 49 | } 50 | return Container(); 51 | }, 52 | ), 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/components/bottom_nav_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:ff_navigation_bar/ff_navigation_bar.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:mangamint/constants/base_color.dart'; 4 | import 'package:mangamint/screens/home_screens/home_screen.dart'; 5 | import 'package:mangamint/screens/lainnya_screen/lainnya_screen.dart'; 6 | import 'package:mangamint/screens/list_manga_screen/index_manga_list.dart'; 7 | import 'package:mangamint/screens/tersimpan_screen/tersimpan_screen.dart'; 8 | 9 | class BottomNavBar extends StatefulWidget { 10 | @override 11 | _BottomNavBarState createState() => _BottomNavBarState(); 12 | } 13 | 14 | class _BottomNavBarState extends State { 15 | List _children = [ 16 | HomeScreen(), 17 | indexMangaLIst(), 18 | TersimpanScreen(), 19 | LainnyaScreen() 20 | ]; 21 | int index = 0; 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return Scaffold( 26 | body: IndexedStack( 27 | children: _children, 28 | index: index, 29 | ), 30 | bottomNavigationBar: FFNavigationBar( 31 | selectedIndex: index, 32 | onSelectTab: _onTapTapped, 33 | theme: FFNavigationBarTheme( 34 | barBackgroundColor: BaseColor.red, 35 | selectedItemBackgroundColor: BaseColor.green, 36 | selectedItemIconColor: Colors.white, 37 | selectedItemLabelColor: BaseColor.black, 38 | unselectedItemIconColor: BaseColor.grey2, 39 | unselectedItemLabelColor: BaseColor.grey2), 40 | items: [ 41 | FFNavigationBarItem( 42 | iconData: Icons.home, 43 | label: 'Home', 44 | ), 45 | FFNavigationBarItem( 46 | iconData: Icons.list, 47 | label: 'Daftar', 48 | ), 49 | FFNavigationBarItem( 50 | iconData: Icons.bookmark, 51 | label: 'Tersimpan', 52 | ), 53 | FFNavigationBarItem( 54 | iconData: Icons.more_horiz, 55 | label: 'Lainnya', 56 | ), 57 | ], 58 | ), 59 | ); 60 | } 61 | 62 | void _onTapTapped(value) { 63 | setState(() { 64 | index = value; 65 | }); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/screens/chapter_screen/index_chapter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:hive/hive.dart'; 4 | import 'package:mangamint/bloc/chapter_bloc/bloc.dart'; 5 | import 'package:mangamint/components/build_error.dart'; 6 | import 'package:mangamint/components/loading_dialog.dart'; 7 | import 'package:mangamint/helper/hive/hive_chapter_model.dart'; 8 | import 'package:mangamint/helper/hive/hive_chapter_opened_model.dart'; 9 | import 'package:mangamint/screens/chapter_screen/chapter_screen.dart'; 10 | 11 | class IndexChapter extends StatefulWidget { 12 | final String endpoint; 13 | 14 | const IndexChapter({Key key, this.endpoint}) : super(key: key); 15 | @override 16 | _IndexChapterState createState() => _IndexChapterState(); 17 | } 18 | 19 | class _IndexChapterState extends State { 20 | ChapterBloc _chapterBlocBloc; 21 | String get endpoint => widget.endpoint; 22 | int currentIndex = 0; 23 | bool isExist = false; 24 | var chapterBox = Hive.box('chapter'); 25 | var lastBox = Hive.box('lastOpenedChapter'); 26 | HiveChapterOpenedModel lastModel; 27 | void _checkLastChapter(){ 28 | int count = chapterBox.length; 29 | for(int i = 0;i(context)..add(FetchChapter(endpoint: widget.endpoint)); 48 | _checkLastChapter(); 49 | 50 | } 51 | @override 52 | Widget build(BuildContext context) { 53 | return Scaffold( 54 | body: BlocBuilder( 55 | builder: (context,state){ 56 | if(state is ChapterLoadingState){ 57 | return LoadingDialog(); 58 | }else if(state is ChapterLoadedState){ 59 | return ChapterScreen(data: state.data,currentIndex: currentIndex,); 60 | }else if(state is ChapterFailureState){ 61 | return BuildError(onRefresh: (){},msg: state.msg,); 62 | } 63 | return Container(); 64 | }, 65 | ), 66 | ); 67 | } 68 | } -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lib/screens/list_manga_screen/index_manga_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:mangamint/bloc/manga_list_bloc/bloc.dart'; 5 | import 'package:mangamint/components/my_body.dart'; 6 | import 'package:mangamint/constants/base_color.dart'; 7 | import 'package:mangamint/screens/list_manga_screen/manhua_category.dart'; 8 | import 'package:mangamint/screens/list_manga_screen/manhwa_category.dart'; 9 | import 'package:mangamint/screens/list_manga_screen/semuanya_category.dart'; 10 | 11 | class indexMangaLIst extends StatefulWidget { 12 | @override 13 | _indexMangaLIstState createState() => _indexMangaLIstState(); 14 | } 15 | 16 | class _indexMangaLIstState extends State { 17 | TabController _tabController; 18 | MangaListBloc _mangaListBloc; 19 | void _init_(){ 20 | _mangaListBloc = BlocProvider.of(context); 21 | _mangaListBloc.add(InitialFetchMangaEvent()); 22 | } 23 | @override 24 | void initState() { 25 | super.initState(); 26 | _init_(); 27 | } 28 | @override 29 | Widget build(BuildContext context) { 30 | return DefaultTabController( 31 | length: 3, 32 | child: MyBody( 33 | showRefresh: false, 34 | title: Text('Daftar Manga',style: TextStyle(color: BaseColor.black,fontWeight: FontWeight.bold),), 35 | bottomAppBar: PreferredSize( 36 | preferredSize: Size.fromHeight(50), 37 | child: Padding( 38 | padding: const EdgeInsets.symmetric(vertical: 10,horizontal: 20), 39 | child: TabBar( 40 | controller: _tabController, 41 | unselectedLabelColor: BaseColor.red, 42 | indicatorSize: TabBarIndicatorSize.tab, 43 | indicatorColor: Colors.white, 44 | indicator: BoxDecoration( 45 | color: BaseColor.red, 46 | borderRadius: BorderRadius.circular(8), 47 | ), 48 | tabs: [ 49 | Tab(text: 'Semuanya',), 50 | Tab( 51 | text: 'Manhua', 52 | ), 53 | Tab( 54 | text: 'Manhwa', 55 | ) 56 | ], 57 | ), 58 | ), 59 | ), 60 | body: TabBarView( 61 | children: [ 62 | SemuanyaCategory(), 63 | ManhuaCategory(), 64 | ManhwaCategory(), 65 | ], 66 | ), 67 | ), 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/screens/home_screens/genre_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:mangamint/bloc/genre_list_bloc/bloc.dart'; 5 | import 'package:mangamint/components/my_shimmer.dart'; 6 | import 'package:mangamint/constants/base_color.dart'; 7 | 8 | class GenreListHome extends StatelessWidget { 9 | @override 10 | Widget build(BuildContext context) { 11 | return Padding( 12 | padding: EdgeInsets.only(top: 10), 13 | child: BlocBuilder( 14 | builder: (context,state){ 15 | if (state is GenreListLoadingState || state is GenreListFailureState) { 16 | return MyShimmer( 17 | child: _myGridview( 18 | itemCount: 16, 19 | itemBuilder: (context,i){ 20 | return Container( 21 | color: BaseColor.red, 22 | height: 10, 23 | ); 24 | } 25 | ), 26 | ); 27 | } else if(state is GenreListLoadedState){ 28 | return _myGridview( 29 | itemCount: state.genreList.length, 30 | itemBuilder: (context,i){ 31 | return ClipRRect( 32 | borderRadius: BorderRadius.all(Radius.circular(5)), 33 | child: InkWell( 34 | onTap: (){ 35 | Navigator.pushNamed(context, '/mangabygenre',arguments: state.genreList[i] 36 | .endpoint); 37 | }, 38 | child: Container( 39 | color: BaseColor.red, 40 | height: 20, 41 | child: Center(child: Text(state.genreList[i].genre_name,style: TextStyle(color: Colors.white),textAlign: TextAlign.center,)), 42 | ), 43 | ), 44 | ); 45 | } 46 | ); 47 | } 48 | return Container(); 49 | }, 50 | ), 51 | ); 52 | } 53 | Widget _myGridview({int itemCount,itemBuilder,}){ 54 | return GridView.builder( 55 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( 56 | crossAxisCount: 4, 57 | mainAxisSpacing: 5, 58 | crossAxisSpacing: 4, 59 | childAspectRatio: 3, 60 | ), 61 | itemCount: itemCount, 62 | physics: ClampingScrollPhysics(), 63 | shrinkWrap: true, 64 | itemBuilder: itemBuilder, 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/bloc/mangabygenre_bloc/manga_by_genre_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:bloc/bloc.dart'; 3 | import 'package:mangamint/models/genre_list_model.dart'; 4 | import 'package:mangamint/repositories/genre_list_repo.dart'; 5 | import 'package:rxdart/rxdart.dart'; 6 | import './bloc.dart'; 7 | 8 | class MangaByGenreBloc extends Bloc { 9 | MangaByGenreRepo _mangaByGenreRepo; 10 | MangaByGenreBloc(this._mangaByGenreRepo) : super(InitialMangaByGenreState()); 11 | 12 | bool _hasReachedMax(MangaByGenreState state) => state is MangaByGenreLoadedState && state.hasReachedMax; 13 | 14 | @override 15 | Stream> transformEvents(Stream events, transitionFn) { 16 | return super.transformEvents(events.debounceTime( 17 | Duration(milliseconds: 500) 18 | ), transitionFn); 19 | } 20 | 21 | @override 22 | Stream mapEventToState( 23 | MangaByGenreEvent event, 24 | ) async* { 25 | final currentState = state; 26 | int page = 1; 27 | if(event is FetchMangByGenre && !_hasReachedMax(currentState)){ 28 | try{ 29 | if(currentState is InitialMangaByGenreState){ 30 | yield MangaByGenreLoadingState(); 31 | List list = await _mangaByGenreRepo.getManga(genre: event.endpoint,page: page+=1); 32 | 33 | yield MangaByGenreLoadedState(list: list,page: page+=1,hasReachedMax: false); 34 | } 35 | if(currentState is MangaByGenreLoadedState){ 36 | try{ 37 | List list = await _mangaByGenreRepo.getManga(genre: event.endpoint,page: currentState.page); 38 | yield list.isEmpty ? currentState.copyWith(hasReachedMax: true,list: currentState.list): 39 | MangaByGenreLoadedState(list: currentState.list+list,hasReachedMax: false,page: 40 | currentState.page+=1); 41 | }catch(e){ 42 | yield MangaByGenreLoadedState(list: currentState.list,hasReachedMax: true,page: currentState.page); 43 | } 44 | } 45 | }catch(e){ 46 | yield MangaByGenreFailureState(msg: e.toString()); 47 | } 48 | } 49 | if(event is InitialMangaByGenreEvent){ 50 | yield MangaByGenreLoadingState(); 51 | try{ 52 | List list = await _mangaByGenreRepo.getManga(genre: event.endpoint,page: page); 53 | yield list.length < 20 ?MangaByGenreLoadedState(list: list,page: page,hasReachedMax: true) 54 | :MangaByGenreLoadedState(list: list,page: page+=1,hasReachedMax: false); 55 | }catch(e){ 56 | yield MangaByGenreFailureState(msg: e.toString()); 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | def keystoreProperties = new Properties() 29 | def keystorePropertiesFile = rootProject.file('key.properties') 30 | if (keystorePropertiesFile.exists()) { 31 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 32 | } 33 | 34 | android { 35 | compileSdkVersion 28 36 | 37 | sourceSets { 38 | main.java.srcDirs += 'src/main/kotlin' 39 | } 40 | 41 | lintOptions { 42 | disable 'InvalidPackage' 43 | } 44 | 45 | defaultConfig { 46 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 47 | applicationId "com.febryardiansyah.mangamint" 48 | minSdkVersion 16 49 | targetSdkVersion 28 50 | versionCode flutterVersionCode.toInteger() 51 | versionName flutterVersionName 52 | } 53 | 54 | signingConfigs { 55 | release { 56 | keyAlias keystoreProperties['keyAlias'] 57 | keyPassword keystoreProperties['keyPassword'] 58 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 59 | storePassword keystoreProperties['storePassword'] 60 | } 61 | } 62 | 63 | buildTypes { 64 | release { 65 | // TODO: Add your own signing config for the release build. 66 | // Signing with the debug keys for now, so `flutter run --release` works. 67 | signingConfig signingConfigs.debug 68 | } 69 | } 70 | } 71 | 72 | flutter { 73 | source '../..' 74 | } 75 | 76 | apply plugin: 'com.google.gms.google-services' 77 | 78 | dependencies { 79 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 80 | implementation 'com.google.firebase:firebase-analytics:17.2.2' 81 | } 82 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 13 | 20 | 24 | 28 | 33 | 37 | 38 | 39 | 40 | 41 | 42 | 44 | 47 | 48 | -------------------------------------------------------------------------------- /lib/screens/home_screens/terpopular.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:mangamint/bloc/popular_bloc/bloc.dart'; 5 | import 'package:mangamint/components/item_small.dart'; 6 | import 'package:mangamint/components/my_shimmer.dart'; 7 | import 'package:mangamint/constants/base_color.dart'; 8 | 9 | class TerpopularCategory extends StatelessWidget { 10 | @override 11 | Widget build(BuildContext context) { 12 | return Padding( 13 | padding: EdgeInsets.all(8), 14 | child: BlocBuilder( 15 | builder: (context,state){ 16 | if(state is PopularLoadingState || state is PopularFailureState){ 17 | return MyShimmer( 18 | child: GridView.builder( 19 | shrinkWrap: true, 20 | itemCount: 3, 21 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( 22 | crossAxisCount: 3,mainAxisSpacing: 2,crossAxisSpacing: 2 23 | ), 24 | itemBuilder: (context,i){ 25 | return Column( 26 | children: [ 27 | ClipRRect( 28 | borderRadius: BorderRadius.circular(8), 29 | child: Container( 30 | height: 80, 31 | width: MediaQuery.of(context).size.width, 32 | color: BaseColor.red, 33 | ), 34 | ), 35 | SizedBox(height: 10,), 36 | ClipRRect( 37 | borderRadius: BorderRadius.circular(8), 38 | child: Container( 39 | height: 10, 40 | width: MediaQuery.of(context).size.width, 41 | color: BaseColor.red, 42 | ), 43 | ) 44 | ], 45 | ); 46 | }, 47 | ) 48 | ); 49 | }else if (state is PopularLoadedState) { 50 | return SizedBox( 51 | height: 180, 52 | child: ListView.builder( 53 | itemCount: state.popularList.getRange(0, 10).length, 54 | shrinkWrap: true, 55 | physics: ClampingScrollPhysics(), 56 | scrollDirection: Axis.horizontal, 57 | itemBuilder: (context,i){ 58 | var data = state.popularList[i]; 59 | return ItemSmall( 60 | title: data.title, 61 | subtitle: data.type, 62 | bottom: data.upload_on, 63 | thumb: data.thumb, 64 | onTap: (){ 65 | Navigator.pushNamed(context, '/detailmanga',arguments: data.endpoint); 66 | }, 67 | ); 68 | }, 69 | ), 70 | ); 71 | } 72 | return Container(); 73 | }, 74 | ), 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/bloc/manga_list_bloc/manga_list_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:bloc/bloc.dart'; 3 | import 'package:mangamint/models/manga_list_model.dart'; 4 | import 'package:mangamint/repositories/manga_list_repo.dart'; 5 | import 'package:rxdart/rxdart.dart'; 6 | 7 | import 'manga_list_event.dart'; 8 | import 'manga_list_state.dart'; 9 | 10 | class MangaListBloc extends Bloc { 11 | final MangaListRepo _mangaListRepo; 12 | 13 | MangaListBloc(this._mangaListRepo) : super(InitialMangaListState()); 14 | 15 | 16 | @override 17 | Stream> transformEvents( 18 | Stream events, 19 | TransitionFunction transitionFn) { 20 | return super.transformEvents( 21 | events.debounceTime(Duration(milliseconds: 500)), 22 | transitionFn 23 | ); 24 | } 25 | 26 | bool _hasReachedMax(MangaListState state) => state is MangaListStateLoaded && state.hasReachedMax; 27 | 28 | @override 29 | Stream mapEventToState( 30 | MangaListEvent event, 31 | ) async* { 32 | final currentState = state; 33 | int page = 1; 34 | if(event is FetchManga && !_hasReachedMax(currentState)){ 35 | yield* _fetchMangaToState(currentState, page); 36 | } 37 | if(event is InitialFetchMangaEvent){ 38 | yield* _initialEventToState(currentState, page); 39 | } 40 | if(event is RefreshMangaEvent){ 41 | yield MangaListLoadingState(); 42 | try{ 43 | final list = await _mangaListRepo.getMangaList(page: page); 44 | yield MangaListStateLoaded(hasReachedMax: false,mangaList: list,page: page); 45 | }catch(e){ 46 | yield MangaListStateFailure(msg: e.toString()); 47 | } 48 | } 49 | } 50 | Stream _fetchMangaToState(MangaListState currentState,int page)async*{ 51 | try{ 52 | if(currentState is InitialMangaListState){ 53 | yield MangaListLoadingState(); 54 | final Listlist = await _mangaListRepo.getMangaList(page: 2); 55 | yield MangaListStateLoaded(mangaList: list,hasReachedMax: false,page: page+=1); 56 | } 57 | if(currentState is MangaListStateLoaded){ 58 | final Listlist = await _mangaListRepo.getMangaList( 59 | page: currentState.page 60 | ); 61 | yield list.isEmpty ? currentState.copyWith(hasReachedMax: true): 62 | MangaListStateLoaded(mangaList: currentState.mangaList + list,hasReachedMax: false,page: currentState.page+=1); 63 | } 64 | }catch(e){ 65 | yield MangaListStateFailure(msg: e.toString()); 66 | } 67 | } 68 | Stream _initialEventToState(MangaListState currentState,int page)async*{ 69 | try{ 70 | if(currentState is InitialMangaListState){ 71 | yield MangaListLoadingState(); 72 | final Listlist = await _mangaListRepo.getMangaList(page: page); 73 | yield MangaListStateLoaded(mangaList: list,hasReachedMax: false,page: page+=1); 74 | } 75 | if(currentState is MangaListStateLoaded){ 76 | yield MangaListStateLoaded(mangaList: currentState.mangaList,page: page,hasReachedMax: false); 77 | } 78 | }catch(e){ 79 | yield MangaListStateFailure(msg: e.toString()); 80 | 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/bloc/manhuamanhwa/manhuamanhwa_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:bloc/bloc.dart'; 3 | import 'package:mangamint/bloc/manga_list_bloc/bloc.dart'; 4 | import 'package:mangamint/models/manhua_manhwa_model.dart'; 5 | import 'package:mangamint/repositories/manhua_manhwa_repo.dart'; 6 | import 'package:rxdart/rxdart.dart'; 7 | import './bloc.dart'; 8 | 9 | class ManhuamanhwaBloc extends Bloc { 10 | ManhuaManhwaRepo _manhuaManhwaRepo; 11 | 12 | ManhuamanhwaBloc(this._manhuaManhwaRepo) : super(InitialManhuamanhwaState()); 13 | 14 | bool _hasReachedMax(ManhuamanhwaState state) => state is ManhuaLoadedState && state.hasReachedMax; 15 | 16 | @override 17 | Stream> transformEvents(Stream events, transitionFn) { 18 | return super.transformEvents(events.debounceTime( 19 | Duration(milliseconds: 500) 20 | ), transitionFn); 21 | } 22 | 23 | @override 24 | Stream mapEventToState( 25 | ManhuamanhwaEvent event, 26 | ) async* { 27 | final currentState = state; 28 | int page = 1; 29 | if(event is FetchManhua && !_hasReachedMax(currentState)){ 30 | yield* _fetchManhuaToState(page, currentState,); 31 | } 32 | if(event is FetchManhwa && !_hasReachedMax(currentState)){ 33 | yield* _fetchManhwaToState(page, currentState); 34 | } 35 | } 36 | 37 | Stream _fetchManhuaToState(int page,ManhuamanhwaState currentState)async*{ 38 | try{ 39 | if(currentState is InitialManhuamanhwaState || currentState is ManhwaLoadedState){ 40 | yield ManhuaManhwaLoadingState(); 41 | List manhuaList = await _manhuaManhwaRepo.getManhuaManhwa( 42 | type:'manhua',page: page 43 | ); 44 | yield ManhuaLoadedState(list: manhuaList,page: page+=1,hasReachedMax: false); 45 | } 46 | if(currentState is ManhuaLoadedState){ 47 | List list = await _manhuaManhwaRepo.getManhuaManhwa( 48 | type: 'manhua',page: currentState.page, 49 | ); 50 | yield list.isEmpty ? currentState.copyWith(hasReachedMax: true): 51 | ManhuaLoadedState(list: currentState.list+list, 52 | page: currentState.page+=1,hasReachedMax: false); 53 | } 54 | }catch(e){ 55 | yield ManhuaManhwaFailureState(msg: e.toString()); 56 | } 57 | } 58 | 59 | Stream _fetchManhwaToState(int page, ManhuamanhwaState currentState)async*{ 60 | try{ 61 | if(currentState is InitialManhuamanhwaState || currentState is ManhuaLoadedState){ 62 | yield ManhuaManhwaLoadingState(); 63 | List manhwaList = await _manhuaManhwaRepo.getManhuaManhwa( 64 | type:'manhwa',page: page 65 | ); 66 | yield ManhwaLoadedState(list: manhwaList,page: page+=1,hasReachedMax: false); 67 | } 68 | if(currentState is ManhwaLoadedState){ 69 | List list = await _manhuaManhwaRepo.getManhuaManhwa( 70 | type: 'manhwa',page: currentState.page, 71 | ); 72 | yield list.isEmpty ? currentState.copyWith(hasReachedMax: true): 73 | ManhwaLoadedState(list: currentState.list+list, 74 | page: currentState.page+=1,hasReachedMax: false); 75 | } 76 | }catch(e){ 77 | yield ManhuaManhwaFailureState(msg: e.toString()); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MangaMint 2 | 3 |

4 | GitHub issues 5 | GitHub pull requests 6 | GitHub 7 | GitHub stars 8 | GitHub forks 9 | GitHub watchers 10 | GitHub contributors 11 | GitHub last commit 12 |

13 | 14 | MangaMint is manga reader application that provides manga and comic bahasa indonesia and currently 15 | available on android only, idk in the future will be available on ios or not 16 | 17 | ## Usage 18 | 1. Download or clone this repository [manga-api](https://github.com/febryardiansyah/manga-api) 19 | > git clone https://github.com/febryardiansyah/manga-api 20 | 2. you have to deploy this api by yourself `(You can use heroku,vercel, or glitch)` 21 | 2. change `const String BaseUrl = 'https://10.0.2.2:8000/api/';` (/lib/constants/base_url.dart) to your API. ex: `example.herokuapp.com.com/api/` 22 | 23 | ## Prototype of This App 24 | [MangaMint Protyotype](https://www.figma.com/proto/tEwOEwAIycAuWfMOCffG3w/customDesign?node-id=591%3A3&scaling=scale-down) 25 | 26 | ## Download Apk 27 | [mangamint.apk](https://github.com/febryardiansyah/manga_mint/releases/tag/v.1.0) 28 | 29 | ## Features 30 | - [x] Manga List (Japanase Comic) 31 | - [x] Manhua List (Chinese Comic) 32 | - [x] Manhwa List (Korean Comic) 33 | - [x] Last chapter update 34 | - [x] Bookmarks Manga 35 | - [x] Read last chapter opened 36 | - [x] Search Manga by name and Genres 37 | - [x] Read chapter vertically or horizontally 38 | 39 | ## Screenshot 40 | 41 | 42 | 43 | 44 | 45 | ## Build Setup 46 | ``` bash 47 | 48 | # install dependencies 49 | $ flutter pub get 50 | 51 | # run debug mode 52 | $ flutter run 53 | 54 | # run release mode 55 | $ flutter run --release 56 | 57 | # build app bundle 58 | $ flutter build appbundle 59 | 60 | # build apk 61 | $ flutter build apk 62 | 63 | ``` 64 | ## Dependencies that i use 65 | - http: ^0.12.1 66 | - bloc: ^5.0.1 67 | - flutter_bloc: ^5.0.1 68 | - google_fonts: ^1.1.0 69 | - equatable: ^1.2.0 70 | - rxdart: ^0.24.1 71 | - ff_navigation_bar: ^0.1.5 72 | - flutter_spinkit: ^4.1.2+1 73 | - shimmer: ^1.1.1 74 | - carousel_slider: ^2.2.1 75 | - flutter_screenutil: ^2.2.0 76 | - cached_network_image: ^2.2.0+1 77 | - photo_view: ^0.9.2 78 | - shared_preferences: ^0.5.8 79 | - path_provider: ^1.6.11 80 | - path: ^1.7.0 81 | - hive: ^1.4.1+1 82 | - hive_flutter: ^0.2.1 83 | - toast: ^0.1.5 84 | - font_awesome_flutter: ^8.8.1 85 | - url_launcher: ^5.5.0 86 | 87 | For detailed explanation on how things work, check out [Flutter docs](https://flutter.dev/docs). 88 | 89 | -------------------------------------------------------------------------------- /lib/components/item_big.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:mangamint/components/image_cache_loading.dart'; 5 | import 'package:mangamint/constants/base_color.dart'; 6 | import 'package:mangamint/helper/color_manga_type.dart'; 7 | 8 | class ItemBig extends StatelessWidget { 9 | final itemCount, itemBuilder; 10 | final ScrollController controller; 11 | const ItemBig({Key key, this.itemCount, this.itemBuilder,this.controller}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return GridView.builder( 16 | controller: controller, 17 | shrinkWrap: true, 18 | physics: ClampingScrollPhysics(), 19 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( 20 | crossAxisCount: 3, 21 | mainAxisSpacing: 10, 22 | crossAxisSpacing: 10, 23 | childAspectRatio: 0.45), 24 | itemCount: itemCount, 25 | itemBuilder: itemBuilder, 26 | ); 27 | } 28 | } 29 | 30 | class ItemBigChild extends StatelessWidget { 31 | final String type, thumb, title, chapter, update; 32 | final GestureTapCallback onTap; 33 | 34 | const ItemBigChild( 35 | {Key key, this.type, this.thumb, this.title, this.chapter, this.update,this.onTap}) 36 | : super(key: key); 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | final size = MediaQuery.of(context).size; 41 | ScreenUtil.init(); 42 | return InkWell( 43 | onTap: onTap, 44 | child: Column( 45 | crossAxisAlignment: CrossAxisAlignment.start, 46 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 47 | children: [ 48 | Stack( 49 | children: [ 50 | ClipRRect( 51 | borderRadius: BorderRadius.all(Radius.circular(8)), 52 | child: ImageCacheLoading( 53 | imgUrl: thumb, 54 | imageBuilder: (context,imgProvider){ 55 | return Container( 56 | height: 450.h, 57 | width: size.width, 58 | decoration: BoxDecoration( 59 | image: DecorationImage(image: imgProvider,fit: BoxFit.cover), 60 | ), 61 | ); 62 | }, 63 | ), 64 | ), 65 | Positioned( 66 | bottom: 10, 67 | left: 0, 68 | child: Container( 69 | height: 80.h, 70 | width: 200.w, 71 | color: mangaTypeColor(type), 72 | child: Center( 73 | child: Text( 74 | type, 75 | style: TextStyle(color: Colors.white), 76 | )), 77 | ), 78 | ) 79 | ], 80 | ), 81 | Text( 82 | title.length > 20 ? '${title.substring(0, 20)}..' : title, 83 | style: TextStyle(fontWeight: FontWeight.bold), 84 | ), 85 | Text( 86 | chapter, 87 | style: TextStyle(color: BaseColor.green), 88 | ), 89 | Expanded( 90 | child: Text( 91 | update, 92 | style: TextStyle(color: BaseColor.grey1), 93 | ), 94 | ) 95 | ], 96 | ), 97 | ); 98 | } 99 | } -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | generated_key_values = {} 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) do |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | generated_key_values[podname] = podpath 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | end 32 | generated_key_values 33 | end 34 | 35 | target 'Runner' do 36 | use_frameworks! 37 | use_modular_headers! 38 | 39 | # Flutter Pod 40 | 41 | copied_flutter_dir = File.join(__dir__, 'Flutter') 42 | copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') 43 | copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') 44 | unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) 45 | # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. 46 | # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. 47 | # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. 48 | 49 | generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') 50 | unless File.exist?(generated_xcode_build_settings_path) 51 | raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" 52 | end 53 | generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) 54 | cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; 55 | 56 | unless File.exist?(copied_framework_path) 57 | FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) 58 | end 59 | unless File.exist?(copied_podspec_path) 60 | FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) 61 | end 62 | end 63 | 64 | # Keep pod path relative so it can be checked into Podfile.lock. 65 | pod 'Flutter', :path => 'Flutter' 66 | 67 | # Plugin Pods 68 | 69 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 70 | # referring to absolute paths on developers' machines. 71 | system('rm -rf .symlinks') 72 | system('mkdir -p .symlinks/plugins') 73 | plugin_pods = parse_KV_file('../.flutter-plugins') 74 | plugin_pods.each do |name, path| 75 | symlink = File.join('.symlinks', 'plugins', name) 76 | File.symlink(path, symlink) 77 | pod name, :path => File.join(symlink, 'ios') 78 | end 79 | end 80 | 81 | post_install do |installer| 82 | installer.pods_project.targets.each do |target| 83 | target.build_configurations.each do |config| 84 | config.build_settings['ENABLE_BITCODE'] = 'NO' 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /lib/screens/result_screen/result_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:flutter_spinkit/flutter_spinkit.dart'; 5 | import 'package:mangamint/bloc/search_bloc/bloc.dart'; 6 | import 'package:mangamint/components/my_body.dart'; 7 | import 'package:mangamint/constants/base_color.dart'; 8 | import 'package:mangamint/helper/color_manga_type.dart'; 9 | 10 | class ResultScreen extends StatefulWidget { 11 | final String query; 12 | 13 | ResultScreen({this.query}); 14 | 15 | @override 16 | _ResultScreenState createState() => _ResultScreenState(); 17 | } 18 | 19 | class _ResultScreenState extends State { 20 | SearchBlocBloc _searchBlocBloc; 21 | @override 22 | void initState() { 23 | super.initState(); 24 | _searchBlocBloc = BlocProvider.of(context)..add(FetchSearch(query: widget.query)); 25 | } 26 | @override 27 | Widget build(BuildContext context) { 28 | ScreenUtil.init(); 29 | return MyBody( 30 | showSearch: false, 31 | showRefresh: false, 32 | title: Text('Hasil Pencarian ${widget.query}',style: TextStyle(color: BaseColor.black),), 33 | body: BlocBuilder( 34 | builder: (context,state){ 35 | if(state is SearchLoadingState){ 36 | return Center( 37 | child: SpinKitCubeGrid(color: BaseColor.red,), 38 | ); 39 | }else if (state is SearchLoadedState) { 40 | if(state.searchList.isEmpty){ 41 | return Center(child: Text('Kosong Gan !!'),); 42 | } 43 | return Padding( 44 | padding: const EdgeInsets.all(8.0), 45 | child: ListView.separated( 46 | separatorBuilder: (context,index)=>Divider(color: BaseColor.grey2,), 47 | itemCount: state.searchList.length, 48 | itemBuilder: (context,i){ 49 | return ListTile( 50 | onTap: (){ 51 | Navigator.pushNamed(context, '/detailmanga',arguments: state.searchList[i].endpoint); 52 | }, 53 | subtitle: Column( 54 | crossAxisAlignment: CrossAxisAlignment.start, 55 | children: [ 56 | Text(state.searchList[i].type,style: TextStyle( 57 | color: mangaTypeColor(state.searchList[i].type) 58 | ),), 59 | Text(state.searchList[i].updated_on,style: TextStyle( 60 | color: BaseColor.grey1 61 | ),), 62 | ], 63 | ), 64 | leading: Image.network( 65 | state.searchList[i].thumb, 66 | height: MediaQuery.of(context).size.height, 67 | width: 200.w, 68 | fit: BoxFit.cover, 69 | ), 70 | title: Text(state.searchList[i].title.length >= 30 ?'${ 71 | state.searchList[i].title.substring(0,30) 72 | }..':state.searchList[i].title), 73 | 74 | ); 75 | }, 76 | ), 77 | ); 78 | }else if(state is SearchFailureState){ 79 | return Padding( 80 | padding: const EdgeInsets.all(20), 81 | child: Center(child: Text(state.msg)), 82 | ); 83 | } 84 | return Container(); 85 | }, 86 | ), 87 | ); 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /lib/bloc/popular_bloc/popular_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:bloc/bloc.dart'; 3 | import 'package:mangamint/bloc/genre_list_bloc/bloc.dart'; 4 | import 'package:mangamint/models/popular_model.dart'; 5 | import 'package:mangamint/repositories/popular_repo.dart'; 6 | import 'package:rxdart/rxdart.dart'; 7 | import './bloc.dart'; 8 | 9 | class PopularBloc extends Bloc { 10 | PopularRepo _popularRepo; 11 | 12 | PopularBloc(this._popularRepo) : super(InitialPopularState()); 13 | 14 | @override 15 | Stream> transformEvents( 16 | Stream events, 17 | TransitionFunction transitionFn) { 18 | return super.transformEvents( 19 | events.debounceTime(Duration(milliseconds: 500)), 20 | transitionFn 21 | ); 22 | } 23 | 24 | bool _hasReachedMax(PopularState state) => state is PopularLoadedState && state.hasReachedMax; 25 | @override 26 | Stream mapEventToState( 27 | PopularEvent event, 28 | ) async* { 29 | final currentState = state; 30 | int page = 1; 31 | if(event is FetchPopular && !_hasReachedMax(currentState)){ 32 | yield* _fetchPopularToState(currentState, page); 33 | } 34 | if(event is InitialFetchPopular){ 35 | yield* _initialFetchToState(currentState, page); 36 | } 37 | if(event is RefreshPopular){ 38 | yield* _refreshPopularToState(currentState, page); 39 | } 40 | } 41 | 42 | Stream _fetchPopularToState(PopularState currentState,int page)async*{ 43 | try{ 44 | // if(currentState is PopularFailureState){ 45 | // yield InitialPopularState(); 46 | // } 47 | if(currentState is InitialPopularState){ 48 | yield PopularLoadingState(); 49 | Listlist = await _popularRepo.getPopular(page: 2); 50 | yield PopularLoadedState(popularList: list,hasReachedMax: false,page: page+=1); 51 | } 52 | if(currentState is PopularLoadedState){ 53 | Listlist = await _popularRepo.getPopular(page: currentState.page); 54 | yield list.isEmpty ? currentState.copyWith(hasReachedMax: true): 55 | PopularLoadedState(hasReachedMax: false,popularList: currentState.popularList+list,page: currentState.page+=1); 56 | } 57 | }catch(e){ 58 | yield PopularFailureState(msg: e.toString()); 59 | } 60 | } 61 | 62 | Stream _initialFetchToState(PopularState currentState,int page)async*{ 63 | try{ 64 | // if(currentState is PopularFailureState){ 65 | // yield InitialPopularState(); 66 | // } 67 | if(currentState is InitialPopularState){ 68 | yield PopularLoadingState(); 69 | Listlist = await _popularRepo.getPopular(page: page); 70 | yield PopularLoadedState(popularList: list,hasReachedMax: false,page: page+=1); 71 | } 72 | if(currentState is PopularLoadedState){ 73 | yield PopularLoadedState(popularList: currentState.popularList,hasReachedMax: false,page: 74 | currentState.page == page ?page+1:currentState.page); 75 | } 76 | }catch(e){ 77 | yield PopularFailureState(msg: e.toString()); 78 | } 79 | } 80 | 81 | Stream _refreshPopularToState(PopularState currentState,int page)async*{ 82 | yield PopularLoadingState(); 83 | try{ 84 | // if(currentState is PopularFailureState){ 85 | // yield InitialPopularState(); 86 | // } 87 | Listlist = await _popularRepo.getPopular(page: page); 88 | yield PopularLoadedState(popularList: list,hasReachedMax: false,page: page+=1); 89 | 90 | }catch(e){ 91 | yield PopularFailureState(msg: e.toString()); 92 | } 93 | } 94 | } 95 | 96 | -------------------------------------------------------------------------------- /lib/screens/home_screens/home_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 5 | import 'package:google_fonts/google_fonts.dart'; 6 | import 'package:mangamint/bloc/manga_list_bloc/bloc.dart'; 7 | import 'package:mangamint/bloc/genre_list_bloc/bloc.dart'; 8 | import 'package:mangamint/bloc/popular_bloc/bloc.dart'; 9 | import 'package:mangamint/bloc/recomended_bloc/bloc.dart'; 10 | import 'package:mangamint/components/my_body.dart'; 11 | import 'package:mangamint/constants/base_color.dart'; 12 | import 'package:mangamint/screens/home_screens/genre_list.dart'; 13 | import 'package:mangamint/screens/home_screens/my_carousel.dart'; 14 | import 'package:mangamint/screens/home_screens/terbaru.dart'; 15 | import 'package:mangamint/screens/home_screens/terpopular.dart'; 16 | 17 | class HomeScreen extends StatefulWidget { 18 | @override 19 | _HomeScreenState createState() => _HomeScreenState(); 20 | } 21 | 22 | class _HomeScreenState extends State { 23 | RecomendedBloc _recomendedBloc; 24 | GenreListBloc _genreListBloc; 25 | PopularBloc _popularBloc; 26 | MangaListBloc _mangaListBloc; 27 | @override 28 | void initState() { 29 | super.initState(); 30 | init(); 31 | 32 | } 33 | @override 34 | Widget build(BuildContext context) { 35 | ScreenUtil.init(); 36 | return MyBody( 37 | onRefresh: (){ 38 | _onRefresh(); 39 | }, 40 | title: Text('MangaMint',style: GoogleFonts.modak(color: BaseColor.red),), 41 | body: Padding( 42 | padding: const EdgeInsets.symmetric(horizontal: 8,vertical: 15), 43 | child: ListView( 44 | children: [ 45 | MyCarousel(), 46 | // _rowTitle( 47 | // title: 'Popular', 48 | // seemore: (){ 49 | // Navigator.pushNamed(context, '/popular'); 50 | // }, 51 | // child: TerpopularCategory() 52 | // ), 53 | _rowTitle( 54 | showMore: false, 55 | title: 'Terbaru', 56 | child: TerbaruCategory() 57 | ), 58 | _categoryTitle('Genre'), 59 | GenreListHome(), 60 | ], 61 | ), 62 | ), 63 | ); 64 | } 65 | Widget _categoryTitle(String title){ 66 | return Padding( 67 | padding: EdgeInsets.only(top: 10), 68 | child: Text(title, 69 | style: TextStyle(fontWeight: FontWeight.bold,fontSize: 15),), 70 | ); 71 | } 72 | Widget _rowTitle({String title,Function seemore,Widget child,bool showMore = true}){ 73 | return Column( 74 | children: [ 75 | Row( 76 | children: [ 77 | _categoryTitle(title), 78 | Spacer(), 79 | showMore?InkWell( 80 | onTap: seemore, 81 | child: Text('lihat selengkapnya')):Center(), 82 | ], 83 | ), 84 | child 85 | ], 86 | ); 87 | } 88 | void init(){ 89 | _recomendedBloc = BlocProvider.of(context); 90 | _recomendedBloc.add(FetchRecommended()); 91 | // _popularBloc = BlocProvider.of(context); 92 | // _popularBloc.add(InitialFetchPopular()); 93 | _genreListBloc = BlocProvider.of(context); 94 | _genreListBloc.add(FetchGenreList()); 95 | _mangaListBloc = BlocProvider.of(context)..add(InitialFetchMangaEvent()); 96 | 97 | } 98 | void _onRefresh(){ 99 | _recomendedBloc.add(RefreshRecommended()); 100 | // _popularBloc.add(RefreshPopular()); 101 | _genreListBloc.add(RefreshGenreList()); 102 | _mangaListBloc.add(RefreshMangaEvent()); 103 | } 104 | } -------------------------------------------------------------------------------- /lib/screens/tersimpan_screen/tersimpan_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:hive/hive.dart'; 4 | import 'package:hive_flutter/hive_flutter.dart'; 5 | import 'package:mangamint/components/my_body.dart'; 6 | import 'package:mangamint/constants/base_color.dart'; 7 | import 'package:mangamint/helper/color_manga_type.dart'; 8 | import 'package:mangamint/helper/hive/hive_manga_model.dart'; 9 | import 'package:toast/toast.dart'; 10 | 11 | class TersimpanScreen extends StatefulWidget { 12 | @override 13 | _TersimpanScreenState createState() => _TersimpanScreenState(); 14 | } 15 | 16 | class _TersimpanScreenState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return MyBody( 20 | showRefresh: false, 21 | title: Text('Tersimpan',style: TextStyle(color: BaseColor.black,fontWeight: FontWeight.bold),), 22 | body: FutureBuilder( 23 | future: Hive.openBox('manga'), 24 | builder: (context,snapshot){ 25 | if(snapshot.connectionState == ConnectionState.done){ 26 | if (snapshot.hasError) { 27 | return Center(child: Text('error'),); 28 | } else{ 29 | return Hive.box('manga').isEmpty ?Center( 30 | child: Text('Kosong Mint xixixi'), 31 | ) 32 | :_buildListview(); 33 | } 34 | } 35 | return Container(); 36 | }, 37 | ), 38 | ); 39 | } 40 | _buildListview(){ 41 | var mangaBox = Hive.box('manga'); 42 | return WatchBoxBuilder( 43 | box: mangaBox, 44 | builder: (context,manga) => 45 | manga.isEmpty?Center(child: Text('kosong mint xixixi'),):ListView.builder( 46 | itemCount: mangaBox.length, 47 | itemBuilder: (context,i){ 48 | HiveMangaModel mangaModel = mangaBox.getAt(i); 49 | print(mangaBox.length); 50 | return ListTile( 51 | onTap: (){ 52 | Navigator.pushNamed(context, '/detailmanga',arguments: 53 | mangaModel.manga_endpoint); 54 | }, 55 | title: Text(mangaModel.title.length > 20 ? '${mangaModel.title}..':mangaModel.title), 56 | subtitle: Text(mangaModel.type,style: TextStyle(color: mangaTypeColor(mangaModel.type)),), 57 | trailing: FlatButton( 58 | child: Text('Hapus',style: TextStyle(color: BaseColor.red),), 59 | shape: RoundedRectangleBorder( 60 | side: BorderSide(width: 1,color: BaseColor.red) 61 | ), 62 | onPressed: (){ 63 | showDialog(context: context, 64 | builder: (nani){ 65 | return AlertDialog( 66 | title: Text('Apa kamu yakin ingin menghapusnya ?'), 67 | actions: [ 68 | FlatButton( 69 | child: Text('Batal'), 70 | onPressed: (){ 71 | Navigator.pop(context); 72 | }, 73 | ), 74 | FlatButton( 75 | child: Text('Yakin dong !!',style: TextStyle(color: BaseColor.red),), 76 | onPressed: (){ 77 | mangaBox.deleteAt(i); 78 | Navigator.pop(context); 79 | Toast.show('Berhasil Dihapus', context,duration: Toast.LENGTH_LONG 80 | ,gravity: Toast.CENTER); 81 | }, 82 | ) 83 | ], 84 | ); 85 | }); 86 | }, 87 | ), 88 | leading: Image.network( 89 | mangaModel.thumb, 90 | ), 91 | ); 92 | }, 93 | ), 94 | ); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: mangamint 2 | description: A new Flutter application. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ">=2.7.0 <3.0.0" 22 | 23 | dependencies: 24 | flutter: 25 | sdk: flutter 26 | 27 | 28 | # The following adds the Cupertino Icons font to your application. 29 | # Use with the CupertinoIcons class for iOS style icons. 30 | cupertino_icons: ^0.1.3 31 | http: ^0.12.1 32 | bloc: ^5.0.1 33 | flutter_bloc: ^5.0.1 34 | google_fonts: ^1.1.0 35 | equatable: ^1.2.0 36 | provider: ^4.3.0 37 | rxdart: ^0.24.1 38 | ff_navigation_bar: ^0.1.5 39 | flutter_spinkit: ^4.1.2+1 40 | shimmer: ^1.1.1 41 | carousel_slider: ^2.2.1 42 | flutter_screenutil: ^2.2.0 43 | cached_network_image: ^2.2.0+1 44 | photo_view: ^0.10.3 45 | shared_preferences: ^0.5.8 46 | path_provider: ^1.6.11 47 | path: ^1.7.0 48 | hive: ^1.4.1+1 49 | hive_flutter: ^0.2.1 50 | toast: ^0.1.5 51 | font_awesome_flutter: ^8.8.1 52 | url_launcher: ^5.5.0 53 | extended_image: ^0.9.0 54 | dio: ^3.0.10 55 | 56 | dev_dependencies: 57 | flutter_launcher_icons: ^0.7.5 58 | hive_generator: ^0.5.1 59 | build_runner: 60 | flutter_test: 61 | sdk: flutter 62 | flutter_icons: 63 | android: "launcher_icon" 64 | ios: true 65 | image_path: "assets/icons/icon.png" 66 | 67 | # For information on the generic Dart part of this file, see the 68 | # following page: https://dart.dev/tools/pub/pubspec 69 | 70 | # The following section is specific to Flutter. 71 | flutter: 72 | 73 | # The following line ensures that the Material Icons font is 74 | # included with your application, so that you can use the icons in 75 | # the material Icons class. 76 | uses-material-design: true 77 | 78 | # To add assets to your application, add an assets section, like this: 79 | assets: 80 | - assets/images/ 81 | - assets/images/aqua.JPG 82 | # - images/a_dot_ham.jpeg 83 | 84 | # An image asset can refer to one or more resolution-specific "variants", see 85 | # https://flutter.dev/assets-and-images/#resolution-aware. 86 | 87 | # For details regarding adding assets from package dependencies, see 88 | # https://flutter.dev/assets-and-images/#from-packages 89 | 90 | # To add custom fonts to your application, add a fonts section here, 91 | # in this "flutter" section. Each entry in this list should have a 92 | # "family" key with the font family name, and a "fonts" key with a 93 | # list giving the asset and other descriptors for the font. For 94 | # example: 95 | # fonts: 96 | # - family: Schyler 97 | # fonts: 98 | # - asset: fonts/Schyler-Regular.ttf 99 | # - asset: fonts/Schyler-Italic.ttf 100 | # style: italic 101 | # - family: Trajan Pro 102 | # fonts: 103 | # - asset: fonts/TrajanPro.ttf 104 | # - asset: fonts/TrajanPro_Bold.ttf 105 | # weight: 700 106 | # 107 | # For details regarding fonts from package dependencies, 108 | # see https://flutter.dev/custom-fonts/#from-packages 109 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | import 'package:hive/hive.dart'; 6 | import 'package:mangamint/bloc/manga_list_bloc/bloc.dart'; 7 | import 'package:mangamint/bloc/chapter_bloc/bloc.dart'; 8 | import 'package:mangamint/bloc/genre_list_bloc/bloc.dart'; 9 | import 'package:mangamint/bloc/manga_detail_bloc/bloc.dart'; 10 | import 'package:mangamint/bloc/mangabygenre_bloc/bloc.dart'; 11 | import 'package:mangamint/bloc/manhuamanhwa/bloc.dart'; 12 | import 'package:mangamint/bloc/popular_bloc/bloc.dart'; 13 | import 'package:mangamint/bloc/recomended_bloc/bloc.dart'; 14 | import 'package:mangamint/bloc/search_bloc/bloc.dart'; 15 | import 'package:mangamint/constants/base_color.dart'; 16 | import 'package:mangamint/helper/hive/hive_chapter_model.dart'; 17 | import 'package:mangamint/helper/hive/hive_chapter_opened_model.dart'; 18 | import 'package:mangamint/helper/hive/hive_manga_model.dart'; 19 | import 'package:mangamint/helper/routes.dart'; 20 | import 'package:mangamint/repositories/chapter_repo.dart'; 21 | import 'package:mangamint/repositories/genre_list_repo.dart'; 22 | import 'package:mangamint/repositories/manga_detail_repo.dart'; 23 | import 'package:mangamint/repositories/manga_list_repo.dart'; 24 | import 'package:mangamint/repositories/manhua_manhwa_repo.dart'; 25 | import 'package:mangamint/repositories/popular_repo.dart'; 26 | import 'package:mangamint/repositories/recommended_repo.dart'; 27 | import 'package:mangamint/repositories/search_repo.dart'; 28 | import 'package:provider/provider.dart'; 29 | import 'package:path_provider/path_provider.dart' as path_provider; 30 | 31 | void main() async{ 32 | WidgetsFlutterBinding.ensureInitialized(); 33 | final directory = await path_provider.getApplicationDocumentsDirectory(); 34 | Hive.init(directory.path); 35 | Hive.openBox('manga'); 36 | Hive.openBox('chapter'); 37 | Hive.openBox('lastOpenedChapter'); 38 | Hive.registerAdapter(HiveChapterModelAdapter()); 39 | Hive.registerAdapter(HiveMangaModelAdapter()); 40 | Hive.registerAdapter(HiveChapterOpenedModelAdapter()); 41 | SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]).then((value) => runApp(MyApp())); 42 | } 43 | 44 | class MyApp extends StatelessWidget { 45 | @override 46 | Widget build(BuildContext context) { 47 | return MultiProvider( 48 | providers: [ 49 | BlocProvider( 50 | create: (context) => MangaListBloc(MangaListRepo())), 51 | BlocProvider( 52 | create: (context) => MangaDetailBloc(MangaDetailRepo()), 53 | ), 54 | BlocProvider( 55 | create: (context) => ChapterBloc(ChapterRepo()), 56 | ), 57 | BlocProvider( 58 | create: (_) => RecomendedBloc(RecommendedRepo()), 59 | ), 60 | BlocProvider( 61 | create: (_) => GenreListBloc(GenreListRepo()), 62 | ), 63 | BlocProvider( 64 | create: (_) => PopularBloc(PopularRepo()), 65 | ), 66 | BlocProvider( 67 | create: (_) => SearchBlocBloc(SearchRepo()), 68 | ), 69 | BlocProvider( 70 | create: (_) => MangaByGenreBloc(MangaByGenreRepo()), 71 | ), 72 | BlocProvider( 73 | create: (_) => ManhuamanhwaBloc(ManhuaManhwaRepo()), 74 | ), 75 | ], 76 | child: MaterialApp( 77 | theme: ThemeData( 78 | textTheme: GoogleFonts.robotoTextTheme( 79 | Theme.of(context).textTheme 80 | ), 81 | appBarTheme: AppBarTheme( 82 | actionsIconTheme: IconThemeData(color: BaseColor.black), 83 | color: Colors.white, 84 | iconTheme: IconThemeData(color: BaseColor.red) 85 | ) 86 | ), 87 | debugShowCheckedModeBanner: false, 88 | initialRoute: '/', 89 | onGenerateRoute: generateRoute, 90 | ) 91 | ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/screens/result_screen/terpopuler_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:mangamint/bloc/popular_bloc/bloc.dart'; 5 | import 'package:mangamint/components/bottom_loader.dart'; 6 | import 'package:mangamint/components/build_error.dart'; 7 | import 'package:mangamint/components/item_small.dart'; 8 | import 'package:mangamint/components/my_body.dart'; 9 | import 'package:mangamint/components/my_shimmer.dart'; 10 | import 'package:mangamint/constants/base_color.dart'; 11 | 12 | class TerpopulerScreen extends StatefulWidget { 13 | TerpopulerScreen({Key key}) : super(key: key); 14 | 15 | @override 16 | _TerpopulerScreenState createState() => _TerpopulerScreenState(); 17 | } 18 | 19 | class _TerpopulerScreenState extends State { 20 | PopularBloc _popularBloc; 21 | final _scrollController = ScrollController(); 22 | final _scrollThreshold = 200.0; 23 | @override 24 | void initState() { 25 | super.initState(); 26 | _popularBloc = BlocProvider.of(context); 27 | _scrollController.addListener(_onScroll); 28 | } 29 | @override 30 | Widget build(BuildContext context) { 31 | ScreenUtil.init(); 32 | return BlocConsumer( 33 | listener: (context,state){ 34 | if(state is PopularFailureState){ 35 | Scaffold.of(context)..hideCurrentSnackBar() 36 | ..showSnackBar(SnackBar( 37 | content: Text(state.msg), 38 | )); 39 | } 40 | }, 41 | builder: (context,state){ 42 | if(state is PopularLoadingState){ 43 | return MyShimmer( 44 | child: Container( 45 | height: 100, 46 | width: MediaQuery.of(context).size.width, 47 | color: BaseColor.red, 48 | ), 49 | ); 50 | }else if (state is PopularLoadedState) { 51 | return MyBody( 52 | showRefresh: false, 53 | title: Text('Terpopuler',style: TextStyle(color: BaseColor.black,fontWeight: FontWeight.bold),), 54 | body: Padding( 55 | padding: const EdgeInsets.only(top: 10), 56 | child: Scrollbar( 57 | controller: _scrollController, 58 | child: GridView.builder( 59 | itemCount: state.popularList.length, 60 | shrinkWrap: true, 61 | controller: _scrollController, 62 | physics: ClampingScrollPhysics(), 63 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( 64 | crossAxisCount: 3, 65 | mainAxisSpacing: 1, 66 | crossAxisSpacing: 1, 67 | childAspectRatio: 0.7 68 | ), 69 | itemBuilder: (context,i){ 70 | var data = state.popularList[i]; 71 | return i >= state.popularList.length?BottomLoader() 72 | :InkWell( 73 | onTap: (){ 74 | Navigator.pushNamed(context, '/detailmanga',arguments: 75 | state.popularList[i].endpoint); 76 | }, 77 | child: ItemSmall( 78 | title: data.title, 79 | subtitle: data.type ?? '', 80 | bottom: data.upload_on ?? '', 81 | thumb: data.thumb ?? '', 82 | onTap: (){ 83 | Navigator.pushNamed(context, '/detailmanga',arguments: data.endpoint); 84 | }, 85 | ) 86 | ); 87 | }, 88 | ), 89 | ), 90 | ), 91 | ); 92 | } else if (state is PopularFailureState) { 93 | return BuildError(); 94 | } 95 | return Container(); 96 | }, 97 | ); 98 | } 99 | void _onScroll() { 100 | 101 | if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) { 102 | _popularBloc.add(FetchPopular()); 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /lib/screens/lainnya_screen/privacy.dart: -------------------------------------------------------------------------------- 1 | const String PRIVACY = '''Privacy Policy 2 | Febry Ardiansyah built the mangamint app as a Free app. This SERVICE is provided by Febry Ardiansyah at no cost and is intended for use as is. 3 | 4 | This page is used to inform visitors regarding my policies with the collection, use, and disclosure of Personal Information if anyone decided to use my Service. 5 | 6 | If you choose to use my Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that I collect is used for providing and improving the Service. I will not use or share your information with anyone except as described in this Privacy Policy. 7 | 8 | The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at mangamint unless otherwise defined in this Privacy Policy. 9 | 10 | Information Collection and Use 11 | 12 | For a better experience, while using our Service, I may require you to provide us with certain personally identifiable information. The information that I request will be retained on your device and is not collected by me in any way. 13 | 14 | The app does use third party services that may collect information used to identify you. 15 | 16 | Link to privacy policy of third party service providers used by the app 17 | 18 | Google Play Services 19 | AdMob 20 | Google Analytics for Firebase 21 | Log Data 22 | 23 | I want to inform you that whenever you use my Service, in a case of an error in the app I collect data and information (through third party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing my Service, the time and date of your use of the Service, and other statistics. 24 | 25 | Cookies 26 | 27 | Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory. 28 | 29 | This Service does not use these “cookies” explicitly. However, the app may use third party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service. 30 | 31 | Service Providers 32 | 33 | I may employ third-party companies and individuals due to the following reasons: 34 | 35 | To facilitate our Service; 36 | To provide the Service on our behalf; 37 | To perform Service-related services; or 38 | To assist us in analyzing how our Service is used. 39 | I want to inform users of this Service that these third parties have access to your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose. 40 | 41 | Security 42 | 43 | I value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and I cannot guarantee its absolute security. 44 | 45 | Links to Other Sites 46 | 47 | This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by me. Therefore, I strongly advise you to review the Privacy Policy of these websites. I have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services. 48 | 49 | Children’s Privacy 50 | 51 | These Services do not address anyone under the age of 13. I do not knowingly collect personally identifiable information from children under 13. In the case I discover that a child under 13 has provided me with personal information, I immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact me so that I will be able to do necessary actions. 52 | 53 | Changes to This Privacy Policy 54 | 55 | I may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. I will notify you of any changes by posting the new Privacy Policy on this page. 56 | 57 | This policy is effective as of 2020-07-24 58 | 59 | Contact Us 60 | 61 | If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me at febryardiansyah27@gmail.com. 62 | 63 | This privacy policy page was created at privacypolicytemplate.net and modified/generated by App Privacy Policy Generator'''; -------------------------------------------------------------------------------- /lib/screens/lainnya_screen/tos.dart: -------------------------------------------------------------------------------- 1 | const String TOS = '''Terms & Conditions 2 | By downloading or using the app, these terms will automatically apply to you – you should make sure therefore that you read them carefully before using the app. You’re not allowed to copy, or modify the app, any part of the app, or our trademarks in any way. You’re not allowed to attempt to extract the source code of the app, and you also shouldn’t try to translate the app into other languages, or make derivative versions. The app itself, and all the trade marks, copyright, database rights and other intellectual property rights related to it, still belong to Febry Ardiansyah. 3 | 4 | Febry Ardiansyah is committed to ensuring that the app is as useful and efficient as possible. For that reason, we reserve the right to make changes to the app or to charge for its services, at any time and for any reason. We will never charge you for the app or its services without making it very clear to you exactly what you’re paying for. 5 | 6 | The mangamint app stores and processes personal data that you have provided to us, in order to provide my Service. It’s your responsibility to keep your phone and access to the app secure. We therefore recommend that you do not jailbreak or root your phone, which is the process of removing software restrictions and limitations imposed by the official operating system of your device. It could make your phone vulnerable to malware/viruses/malicious programs, compromise your phone’s security features and it could mean that the mangamint app won’t work properly or at all. 7 | 8 | The app does use third party services that declare their own Terms and Conditions. 9 | 10 | Link to Terms and Conditions of third party service providers used by the app 11 | 12 | Google Play Services 13 | AdMob 14 | Google Analytics for Firebase 15 | You should be aware that there are certain things that Febry Ardiansyah will not take responsibility for. Certain functions of the app will require the app to have an active internet connection. The connection can be Wi-Fi, or provided by your mobile network provider, but Febry Ardiansyah cannot take responsibility for the app not working at full functionality if you don’t have access to Wi-Fi, and you don’t have any of your data allowance left. 16 | 17 | If you’re using the app outside of an area with Wi-Fi, you should remember that your terms of the agreement with your mobile network provider will still apply. As a result, you may be charged by your mobile provider for the cost of data for the duration of the connection while accessing the app, or other third party charges. In using the app, you’re accepting responsibility for any such charges, including roaming data charges if you use the app outside of your home territory (i.e. region or country) without turning off data roaming. If you are not the bill payer for the device on which you’re using the app, please be aware that we assume that you have received permission from the bill payer for using the app. 18 | 19 | Along the same lines, Febry Ardiansyah cannot always take responsibility for the way you use the app i.e. You need to make sure that your device stays charged – if it runs out of battery and you can’t turn it on to avail the Service, Febry Ardiansyah cannot accept responsibility. 20 | 21 | With respect to Febry Ardiansyah’s responsibility for your use of the app, when you’re using the app, it’s important to bear in mind that although we endeavour to ensure that it is updated and correct at all times, we do rely on third parties to provide information to us so that we can make it available to you. Febry Ardiansyah accepts no liability for any loss, direct or indirect, you experience as a result of relying wholly on this functionality of the app. 22 | 23 | At some point, we may wish to update the app. The app is currently available on Android & iOS – the requirements for both systems(and for any additional systems we decide to extend the availability of the app to) may change, and you’ll need to download the updates if you want to keep using the app. Febry Ardiansyah does not promise that it will always update the app so that it is relevant to you and/or works with the Android & iOS version that you have installed on your device. However, you promise to always accept updates to the application when offered to you, We may also wish to stop providing the app, and may terminate use of it at any time without giving notice of termination to you. Unless we tell you otherwise, upon any termination, (a) the rights and licenses granted to you in these terms will end; (b) you must stop using the app, and (if needed) delete it from your device. 24 | 25 | Changes to This Terms and Conditions 26 | 27 | I may update our Terms and Conditions from time to time. Thus, you are advised to review this page periodically for any changes. I will notify you of any changes by posting the new Terms and Conditions on this page. 28 | 29 | These terms and conditions are effective as of 2020-07-24 30 | 31 | Contact Us 32 | 33 | If you have any questions or suggestions about my Terms and Conditions, do not hesitate to contact me at febryardiansyah27@gmail.com. 34 | 35 | This Terms and Conditions page was generated by App Privacy Policy Generator'''; -------------------------------------------------------------------------------- /lib/screens/list_manga_screen/manhwa_category.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:mangamint/bloc/manhuamanhwa/bloc.dart'; 5 | import 'package:mangamint/components/bottom_loader.dart'; 6 | import 'package:mangamint/components/build_error.dart'; 7 | import 'package:mangamint/components/my_shimmer.dart'; 8 | import 'package:mangamint/constants/base_color.dart'; 9 | import 'package:mangamint/helper/color_manga_type.dart'; 10 | 11 | class ManhwaCategory extends StatefulWidget { 12 | ManhwaCategory({Key key}) : super(key: key); 13 | 14 | @override 15 | _ManhwaCategoryState createState() => _ManhwaCategoryState(); 16 | } 17 | 18 | class _ManhwaCategoryState extends State { 19 | ManhuamanhwaBloc _manhuamanhwaBloc; 20 | final _scrollCtrl = ScrollController(); 21 | final _scrollThreshold = 200.0; 22 | 23 | @override 24 | void initState() { 25 | super.initState(); 26 | _manhuamanhwaBloc = BlocProvider.of(context) 27 | ..add(FetchManhwa()); 28 | _scrollCtrl.addListener(() { 29 | final maxScroll = _scrollCtrl.position.maxScrollExtent; 30 | final currentScroll = _scrollCtrl.position.pixels; 31 | if (maxScroll - currentScroll <= _scrollThreshold) { 32 | _manhuamanhwaBloc = BlocProvider.of(context); 33 | _manhuamanhwaBloc.add(FetchManhwa()); 34 | } 35 | }); 36 | } 37 | @override 38 | Widget build(BuildContext context) { 39 | ScreenUtil.init(); 40 | return Padding( 41 | padding: EdgeInsets.all(8), 42 | child: BlocBuilder( 43 | builder: (context,state){ 44 | if(state is ManhuaManhwaLoadingState){ 45 | return MyShimmer( 46 | child: ListView.builder( 47 | itemCount: 10, 48 | physics: ClampingScrollPhysics(), 49 | shrinkWrap: true, 50 | itemBuilder: (context,i){ 51 | return ListTile( 52 | leading: Container( 53 | height: 100.h, 54 | width: 200.w, 55 | color: BaseColor.red, 56 | ), 57 | title: Container( 58 | height: 100.h, 59 | width:MediaQuery.of(context).size.width, 60 | color: BaseColor.red, 61 | ), 62 | ); 63 | }, 64 | ), 65 | ); 66 | }else if(state is ManhwaLoadedState){ 67 | return Scrollbar( 68 | child: ListView.separated( 69 | separatorBuilder: (context,index)=>Divider(color: BaseColor.grey2,), 70 | itemCount: state.hasReachedMax 71 | ? state.list.length 72 | : state.list.length + 1, 73 | controller: _scrollCtrl, 74 | itemBuilder: (context, i) { 75 | return i >= state.list.length 76 | ? BottomLoader() 77 | : ListTile( 78 | onTap: (){ 79 | Navigator.pushNamed(context, '/detailmanga',arguments: 80 | state.list[i].endpoint); 81 | }, 82 | title: Text(state.list[i].title.length > 20 83 | ? '${state.list[i].title.substring(0, 20)}..' 84 | : state.list[i].title), 85 | subtitle: Column( 86 | crossAxisAlignment: CrossAxisAlignment.start, 87 | children: [ 88 | Text(state.list[i].type,style: TextStyle( 89 | color: mangaTypeColor(state.list[i].type) 90 | ),), 91 | Text(state.list[i].updated_on,style: TextStyle( 92 | color:BaseColor.grey1 93 | ),), 94 | ], 95 | ), 96 | leading: Image.network( 97 | state.list[i].thumb, 98 | height: MediaQuery.of(context).size.height, 99 | width: 200.w, 100 | fit: BoxFit.cover, 101 | ), 102 | trailing: SizedBox( 103 | height: 100.h, 104 | width: 200.w, 105 | child: Text( 106 | state.list[i].chapter, 107 | style: TextStyle( 108 | fontWeight: FontWeight.bold, 109 | ), 110 | ), 111 | ), 112 | ); 113 | }, 114 | ), 115 | ); 116 | }else if(state is ManhuaManhwaFailureState){ 117 | return BuildError(); 118 | } 119 | return Container(); 120 | }, 121 | ), 122 | ); 123 | } 124 | } -------------------------------------------------------------------------------- /lib/screens/list_manga_screen/semuanya_category.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 5 | import 'package:mangamint/bloc/manga_list_bloc/bloc.dart'; 6 | import 'package:mangamint/components/bottom_loader.dart'; 7 | import 'package:mangamint/components/build_error.dart'; 8 | import 'package:mangamint/components/my_shimmer.dart'; 9 | import 'package:mangamint/constants/base_color.dart'; 10 | import 'package:mangamint/helper/color_manga_type.dart'; 11 | 12 | class SemuanyaCategory extends StatefulWidget { 13 | @override 14 | _SemuanyaCategoryState createState() => _SemuanyaCategoryState(); 15 | } 16 | 17 | class _SemuanyaCategoryState extends State { 18 | final _scrollCtrl = ScrollController(); 19 | final _scrollThreshold = 200.0; 20 | MangaListBloc _mangaListBloc; 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | _scrollCtrl.addListener(() { 26 | final maxScroll = _scrollCtrl.position.maxScrollExtent; 27 | final currentScroll = _scrollCtrl.position.pixels; 28 | if (maxScroll - currentScroll <= _scrollThreshold) { 29 | _mangaListBloc = BlocProvider.of(context); 30 | _mangaListBloc.add(FetchManga()); 31 | } 32 | }); 33 | } 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | ScreenUtil.init(); 38 | return Padding( 39 | padding: const EdgeInsets.all(8.0), 40 | child: BlocBuilder( 41 | builder: (context, state) { 42 | if (state is MangaListLoadingState) { 43 | return MyShimmer( 44 | child: ListView.builder( 45 | itemCount: 10, 46 | physics: ClampingScrollPhysics(), 47 | shrinkWrap: true, 48 | itemBuilder: (context, i) { 49 | return ListTile( 50 | leading: Container( 51 | height: 100.h, 52 | width: 200.w, 53 | color: BaseColor.red, 54 | ), 55 | title: Container( 56 | height: 100.h, 57 | width: MediaQuery.of(context).size.width, 58 | color: BaseColor.red, 59 | ), 60 | ); 61 | }, 62 | ), 63 | ); 64 | } else if (state is MangaListStateLoaded) { 65 | return Scrollbar( 66 | child: ListView.separated( 67 | separatorBuilder: (context,index)=>Divider(color: BaseColor.grey2,), 68 | itemCount: state.hasReachedMax 69 | ? state.mangaList.length 70 | : state.mangaList.length + 1, 71 | controller: _scrollCtrl, 72 | itemBuilder: (context, i) { 73 | return i >= state.mangaList.length 74 | ? BottomLoader() 75 | : ListTile( 76 | onTap: () { 77 | Navigator.pushNamed(context, '/detailmanga', 78 | arguments: state.mangaList[i].endpoint); 79 | }, 80 | title: Text(state.mangaList[i].title.length > 20 81 | ? '${state.mangaList[i].title.substring(0, 20)}..' 82 | : state.mangaList[i].title), 83 | subtitle: Column( 84 | crossAxisAlignment: CrossAxisAlignment.start, 85 | children: [ 86 | Text( 87 | state.mangaList[i].type, 88 | style: TextStyle( 89 | color: mangaTypeColor(state.mangaList[i].type)), 90 | ), 91 | Text( 92 | state.mangaList[i].updated_on, 93 | style: TextStyle( 94 | color: BaseColor.grey1), 95 | ), 96 | ], 97 | ), 98 | leading: Image.network( 99 | state.mangaList[i].thumb, 100 | height: MediaQuery.of(context).size.height, 101 | width: 200.w, 102 | fit: BoxFit.cover, 103 | ), 104 | trailing: SizedBox( 105 | height: 100.h, 106 | width: 200.w, 107 | child: Text( 108 | state.mangaList[i].chapter, 109 | style: TextStyle( 110 | fontWeight: FontWeight.bold, 111 | ), 112 | ), 113 | ), 114 | ); 115 | }, 116 | ), 117 | ); 118 | }else if(state is MangaListStateFailure){ 119 | return BuildError(); 120 | } 121 | return Container(); 122 | }, 123 | ), 124 | ); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /lib/screens/result_screen/manga_by_genre_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:mangamint/bloc/mangabygenre_bloc/bloc.dart'; 5 | import 'package:mangamint/components/bottom_loader.dart'; 6 | import 'package:mangamint/components/my_body.dart'; 7 | import 'package:mangamint/components/my_shimmer.dart'; 8 | import 'package:mangamint/constants/base_color.dart'; 9 | import 'package:mangamint/helper/color_manga_type.dart'; 10 | 11 | class MangaByGenreScreen extends StatefulWidget { 12 | final String endpoint; 13 | 14 | MangaByGenreScreen({Key key, this.endpoint}) : super(key: key); 15 | 16 | @override 17 | _MangaByGenreScreenState createState() => _MangaByGenreScreenState(); 18 | } 19 | 20 | class _MangaByGenreScreenState extends State { 21 | MangaByGenreBloc _mangaByGenreBloc; 22 | 23 | final _scrollController = ScrollController(); 24 | final _scrollThreshold = 200.0; 25 | 26 | void _onScroll() { 27 | 28 | if(_scrollController.position.pixels == _scrollController.position.maxScrollExtent){ 29 | _mangaByGenreBloc.add(FetchMangByGenre(endpoint: widget.endpoint)); 30 | } 31 | // final maxScroll = _scrollController.position.maxScrollExtent; 32 | // final currentScroll = _scrollController.position.pixels; 33 | // if (maxScroll - currentScroll <= _scrollThreshold) { 34 | // _mangaByGenreBloc.add(FetchMangByGenre(endpoint: widget.endpoint)); 35 | // } 36 | } 37 | @override 38 | void initState() { 39 | super.initState(); 40 | _mangaByGenreBloc = BlocProvider.of(context) 41 | ..add(InitialMangaByGenreEvent(endpoint: widget.endpoint)); 42 | _scrollController.addListener(_onScroll); 43 | } 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | ScreenUtil.init(); 48 | return MyBody( 49 | onSearch: (){ 50 | Navigator.pushNamed(context, '/search'); 51 | }, 52 | onRefresh: (){ 53 | _mangaByGenreBloc = BlocProvider.of(context) 54 | ..add(InitialMangaByGenreEvent(endpoint: widget.endpoint)); 55 | }, 56 | title: Text( 57 | '${widget.endpoint[0].toUpperCase()}${widget.endpoint.substring(1, widget.endpoint.length - 1)}', 58 | style: TextStyle(color: BaseColor.black), 59 | ), 60 | body: BlocConsumer( 61 | listener: (context,state){ 62 | if(state is MangaByGenreFailureState){ 63 | Scaffold.of(context)..hideCurrentSnackBar() 64 | ..showSnackBar(SnackBar( 65 | content: Text('Njir bruh, gk ada paketan kah ?'), 66 | )); 67 | } 68 | 69 | }, 70 | builder: (context,state){ 71 | print(state); 72 | if(state is MangaByGenreLoadingState || state is MangaByGenreFailureState){ 73 | return MyShimmer( 74 | child: ListView.builder( 75 | itemCount: 10, 76 | physics: ClampingScrollPhysics(), 77 | shrinkWrap: true, 78 | itemBuilder: (context,i){ 79 | return ListTile( 80 | leading: Container( 81 | height: 100.h, 82 | width: 200.w, 83 | color: BaseColor.red, 84 | ), 85 | title: Container( 86 | height: 100.h, 87 | width:MediaQuery.of(context).size.width, 88 | color: BaseColor.red, 89 | ), 90 | ); 91 | }, 92 | ), 93 | ); 94 | } 95 | 96 | else if(state is MangaByGenreLoadedState){ 97 | 98 | if(state.list.isEmpty){ 99 | return Text('no manga'); 100 | } 101 | return ListView.builder( 102 | padding: const EdgeInsets.all(8.0), 103 | controller: _scrollController, 104 | itemCount : state.hasReachedMax? state.list.length:state.list.length+1, 105 | itemBuilder: (context,i){ 106 | // print('list ${state.list.length}'); 107 | // print('index $i'); 108 | print('max ? ${state.hasReachedMax}'); 109 | return i >= state.list.length?BottomLoader(): ListTile( 110 | onTap: (){ 111 | Navigator.pushNamed(context, '/detailmanga',arguments: state.list[i].endpoint); 112 | }, 113 | subtitle: Text(state.list[i].type,style: TextStyle( 114 | color: mangaTypeColor(state.list[i].type) 115 | ),), 116 | leading: Image.network( 117 | state.list[i].thumb ?? 'https://webhostingmedia.net/wp-content/uploads/2018/01/http-error-404-not-found.png', 118 | height: MediaQuery.of(context).size.height, 119 | width: 200.w, 120 | fit: BoxFit.cover, 121 | ), 122 | title: Text(state.list[i].title.length >= 30 ?'${ 123 | state.list[i].title.substring(0,30) 124 | }..':state.list[i].title), 125 | ); 126 | }, 127 | ); 128 | } 129 | return Container(); 130 | }, 131 | ), 132 | ); 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /lib/screens/list_manga_screen/manhua_category.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:mangamint/bloc/manga_detail_bloc/bloc.dart'; 5 | import 'package:mangamint/bloc/manhuamanhwa/bloc.dart'; 6 | import 'package:mangamint/components/bottom_loader.dart'; 7 | import 'package:mangamint/components/build_error.dart'; 8 | import 'package:mangamint/components/loading_dialog.dart'; 9 | import 'package:mangamint/components/my_shimmer.dart'; 10 | import 'package:mangamint/constants/base_color.dart'; 11 | import 'package:flutter/material.dart'; 12 | import 'package:mangamint/helper/color_manga_type.dart'; 13 | 14 | class ManhuaCategory extends StatefulWidget { 15 | @override 16 | _ManhuaCategoryState createState() => _ManhuaCategoryState(); 17 | } 18 | 19 | class _ManhuaCategoryState extends State { 20 | ManhuamanhwaBloc _manhuamanhwaBloc; 21 | final _scrollCtrl = ScrollController(); 22 | final _scrollThreshold = 200.0; 23 | 24 | @override 25 | void initState() { 26 | super.initState(); 27 | _manhuamanhwaBloc = BlocProvider.of(context) 28 | ..add(FetchManhua(endpoint: 'manhua')); 29 | 30 | _scrollCtrl.addListener(() { 31 | final maxScroll = _scrollCtrl.position.maxScrollExtent; 32 | final currentScroll = _scrollCtrl.position.pixels; 33 | if (maxScroll - currentScroll <= _scrollThreshold) { 34 | _manhuamanhwaBloc = BlocProvider.of(context); 35 | _manhuamanhwaBloc.add(FetchManhua(endpoint: 'manhua')); 36 | } 37 | }); 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | ScreenUtil.init(); 43 | return Padding( 44 | padding: EdgeInsets.all(8), 45 | child: BlocBuilder( 46 | builder: (context, state) { 47 | if (state is ManhuaManhwaLoadingState) { 48 | return MyShimmer( 49 | child: ListView.builder( 50 | itemCount: 10, 51 | physics: ClampingScrollPhysics(), 52 | shrinkWrap: true, 53 | itemBuilder: (context, i) { 54 | return ListTile( 55 | leading: Container( 56 | height: 100.h, 57 | width: 200.w, 58 | color: BaseColor.red, 59 | ), 60 | title: Container( 61 | height: 100.h, 62 | width: MediaQuery.of(context).size.width, 63 | color: BaseColor.red, 64 | ), 65 | ); 66 | }, 67 | ), 68 | ); 69 | } else if (state is ManhuaLoadedState) { 70 | return Scrollbar( 71 | child: ListView.separated( 72 | separatorBuilder: (context, index) => Divider( 73 | color: BaseColor.grey2, 74 | ), 75 | itemCount: state.hasReachedMax 76 | ? state.list.length 77 | : state.list.length + 1, 78 | controller: _scrollCtrl, 79 | itemBuilder: (context, i) { 80 | return i >= state.list.length 81 | ? BottomLoader() 82 | : ListTile( 83 | onTap: () { 84 | Navigator.pushNamed(context, '/detailmanga', 85 | arguments: state.list[i].endpoint); 86 | }, 87 | title: Text(state.list[i].title.length > 20 88 | ? '${state.list[i].title.substring(0, 20)}..' 89 | : state.list[i].title), 90 | subtitle: Column( 91 | crossAxisAlignment: CrossAxisAlignment.start, 92 | children: [ 93 | Text( 94 | state.list[i].type, 95 | style: TextStyle( 96 | color: mangaTypeColor(state.list[i].type)), 97 | ), 98 | Text(state.list[i].updated_on, 99 | style: TextStyle(color: BaseColor.grey1)), 100 | ], 101 | ), 102 | trailing: SizedBox( 103 | height: 100.h, 104 | width: 200.w, 105 | child: Text( 106 | state.list[i].chapter, 107 | style: TextStyle( 108 | fontWeight: FontWeight.bold, 109 | ), 110 | ), 111 | ), 112 | leading: Image.network( 113 | state.list[i].thumb, 114 | height: MediaQuery.of(context).size.height, 115 | width: 200.w, 116 | fit: BoxFit.cover, 117 | ), 118 | ); 119 | }, 120 | ), 121 | ); 122 | } else if (state is ManhuaManhwaFailureState) { 123 | return BuildError(); 124 | } 125 | return Container(); 126 | }, 127 | ), 128 | ); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /lib/screens/home_screens/my_carousel.dart: -------------------------------------------------------------------------------- 1 | import 'package:carousel_slider/carousel_slider.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 6 | import 'package:mangamint/bloc/recomended_bloc/bloc.dart'; 7 | import 'package:mangamint/components/image_cache_loading.dart'; 8 | import 'package:mangamint/components/my_shimmer.dart'; 9 | import 'package:mangamint/constants/base_color.dart'; 10 | 11 | class MyCarousel extends StatefulWidget { 12 | @override 13 | _MyCarouselState createState() => _MyCarouselState(); 14 | } 15 | 16 | class _MyCarouselState extends State { 17 | int _current = 0; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | ScreenUtil.init(); 22 | return BlocConsumer( 23 | listener: (context,state){ 24 | if(state is RecomendedFailureState){ 25 | Scaffold.of(context)..hideCurrentSnackBar() 26 | ..showSnackBar(SnackBar( 27 | content: Row( 28 | children: [ 29 | Icon(Icons.info_outline), 30 | Text('Kenapa hayoo, cobak cek internet mu cuk !!',style: TextStyle(color: Colors.white),), 31 | ], 32 | ), 33 | backgroundColor: BaseColor.green, 34 | )); 35 | } 36 | }, 37 | builder: (context,state){ 38 | if (state is RecommendedLoadingState || state is RecomendedFailureState) { 39 | return MyShimmer( 40 | child: ClipRRect( 41 | borderRadius: BorderRadius.all(Radius.circular(8)), 42 | child: Container( 43 | height: 500.h, 44 | width: MediaQuery.of(context).size.width, 45 | color: BaseColor.red, 46 | ), 47 | ), 48 | ); 49 | }else if (state is RecommendedLoadedState) { 50 | return Column( 51 | children: [ 52 | CarouselSlider( 53 | items: state.recommendedList.map((e){ 54 | return InkWell( 55 | onTap: (){ 56 | Navigator.pushNamed(context, '/detailmanga',arguments: 57 | e.endpoint); 58 | }, 59 | child: ClipRRect( 60 | borderRadius: BorderRadius.all(Radius.circular(8)), 61 | child: ImageCacheLoading( 62 | imgUrl: e.thumb, 63 | imageBuilder: (context,imgProvider){ 64 | return Container( 65 | width: MediaQuery.of(context).size.width, 66 | height: 500.h, 67 | decoration: BoxDecoration( 68 | image: DecorationImage( 69 | image: imgProvider, 70 | fit: BoxFit.cover 71 | ) 72 | ), 73 | child: Container( 74 | padding: EdgeInsets.all(8), 75 | child: Center(child: Text(e.title,style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold,fontSize: 20),)), 76 | width: MediaQuery.of(context).size.width, 77 | decoration: BoxDecoration( 78 | gradient: LinearGradient( 79 | begin: Alignment.bottomCenter, 80 | end: Alignment.center, 81 | colors: [ 82 | BaseColor.black, 83 | Colors.black.withOpacity(0.4) 84 | ] 85 | ) 86 | ), 87 | ), 88 | ); 89 | }, 90 | ), 91 | ), 92 | ); 93 | }).toList(), 94 | options: CarouselOptions( 95 | autoPlay: true, 96 | enlargeCenterPage: true, 97 | aspectRatio: 2.0, 98 | onPageChanged: (index, reason) { 99 | setState(() { 100 | _current = index; 101 | }); 102 | } 103 | ), 104 | ), 105 | Row( 106 | mainAxisAlignment: MainAxisAlignment.start, 107 | children: state.recommendedList.map((url) { 108 | int index = state.recommendedList.indexOf(url); 109 | return Container( 110 | width: 8.0, 111 | height: 8.0, 112 | margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0), 113 | decoration: BoxDecoration( 114 | shape: BoxShape.circle, 115 | color: _current == index 116 | ? Color.fromRGBO(0, 0, 0, 0.9) 117 | : Color.fromRGBO(0, 0, 0, 0.4), 118 | ), 119 | ); 120 | }).toList(), 121 | ), 122 | ], 123 | ); 124 | } 125 | // if(state is RecomendedFailureState){ 126 | // print(state.msg); 127 | // return Text('Cek internet mu euy atau tunggu nanti'); 128 | // } 129 | return Container(); 130 | }, 131 | ); 132 | } 133 | } 134 | --------------------------------------------------------------------------------