ajustes de captura de tela ios e android

This commit is contained in:
João Pedro Oliveira 2026-01-06 15:19:11 -03:00
parent bebed14863
commit d9893fa59e
30 changed files with 321 additions and 254 deletions

View File

0
books_viewer/android/Get Normal file
View File

View File

0
books_viewer/android/Run Normal file
View File

View File

@ -31,12 +31,17 @@ if (flutterVersionName == null) {
android {
namespace = "com.myrobot.MakerBook"
compileSdk = 34 // flutter.compileSdkVersion
compileSdk = 36 // flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
coreLibraryDesugaringEnabled true
}
kotlinOptions {
jvmTarget = "17"
}
defaultConfig {
@ -45,7 +50,7 @@ android {
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdk = flutter.minSdkVersion
targetSdk = 34 //flutter.targetSdkVersion
targetSdk = 36 //flutter.targetSdkVersion
versionCode = flutterVersionCode.toInteger()
versionName = flutterVersionName
}
@ -74,6 +79,10 @@ android {
}
dependencies {
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.4"
}
flutter {
source = "../.."
}

View File

@ -3,16 +3,43 @@ allprojects {
google()
mavenCentral()
}
tasks.withType(JavaCompile).configureEach {
sourceCompatibility = JavaVersion.VERSION_17.toString()
targetCompatibility = JavaVersion.VERSION_17.toString()
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
kotlinOptions {
jvmTarget = "17"
}
}
}
rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(":app")
}
subprojects { p ->
// Força especificamente o plugin qr_code_scanner
if (p.name == "qr_code_scanner") {
p.tasks.withType(JavaCompile).configureEach {
sourceCompatibility = JavaVersion.VERSION_17.toString()
targetCompatibility = JavaVersion.VERSION_17.toString()
}
p.tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
kotlinOptions { jvmTarget = "17" }
}
}
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
buildscript {
ext.kotlin_version = '2.0.20'
}

View File

@ -1,3 +1,4 @@
org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
org.gradle.jvmargs=-Xmx4096m
kotlin.jvm.target=17

View File

@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip

View File

@ -18,8 +18,9 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
id "com.android.application" version "8.6.0" apply false
id "com.android.library" version "8.6.0" apply false
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
}
include ":app"

View File

@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '13.0'
platform :ios, '15.5'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@ -198,6 +198,7 @@
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
AE54976BBDDFFE7ECF874480 /* [CP] Embed Pods Frameworks */,
851D672B5FE59B27299B483A /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -329,6 +330,23 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
851D672B5FE59B27299B483A /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;

View File

@ -3,11 +3,91 @@ import UIKit
@main
@objc class AppDelegate: FlutterAppDelegate {
private var secureField: UITextField?
private var secureContainer: UIView?
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller = window?.rootViewController as! FlutterViewController
let channel = FlutterMethodChannel(name: "ios_secure_screen", binaryMessenger: controller.binaryMessenger)
channel.setMethodCallHandler { [weak self] call, result in
guard let self = self else { return }
switch call.method {
case "enable":
self.enableSecureMode()
result(nil)
case "disable":
self.disableSecureMode()
result(nil)
default:
result(FlutterMethodNotImplemented)
}
}
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func enableSecureMode() {
guard let window = self.window else { return }
guard let flutterVC = window.rootViewController else { return }
let flutterView = flutterVC.view!
if secureContainer != nil { return }
let field = UITextField(frame: window.bounds)
field.isSecureTextEntry = true
// IMPORTANTE: precisa ficar interativo, senão mata os toques do FlutterView
field.isUserInteractionEnabled = true
field.isEnabled = true
field.textColor = .clear
field.tintColor = .clear
field.backgroundColor = .clear
field.borderStyle = .none
field.autocorrectionType = .no
field.spellCheckingType = .no
guard let container = field.subviews.first else { return }
container.frame = window.bounds
container.isUserInteractionEnabled = true
flutterView.removeFromSuperview()
container.addSubview(flutterView)
flutterView.frame = container.bounds
flutterView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
window.addSubview(field)
field.frame = window.bounds
field.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.secureField = field
self.secureContainer = container
}
private func disableSecureMode() {
guard let window = self.window else { return }
guard let flutterVC = window.rootViewController else { return }
let flutterView = flutterVC.view!
// Se não está habilitado, sai
guard let field = secureField else { return }
// Remove flutter do container e volta pra window
flutterView.removeFromSuperview()
window.insertSubview(flutterView, belowSubview: field)
flutterView.frame = window.bounds
flutterView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Remove o campo seguro
field.removeFromSuperview()
self.secureField = nil
self.secureContainer = nil
}
}

View File

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
@ -14,13 +16,14 @@
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-16" y="-40"/>
</scene>
</scenes>
</document>

View File

@ -47,5 +47,7 @@
<true/>
<key>NSCameraUsageDescription</key>
<string>Usamos a câmera para ler QR Code e permitir o login seguro no Maker Book.</string>
<key>NSMicrophoneUsageDescription</key>
<string>O Maker Book não grava áudio. Esta permissão pode ser exibida pelo iOS por causa de bibliotecas de reprodução de áudio usadas no app.</string>
</dict>
</plist>

View File

@ -1,22 +1,54 @@
import 'dart:io';
//import 'package:MakerBook/secure_screen/screenshot_guard.dart';
import 'package:flutter/material.dart';
import 'package:flutter_windowmanager/flutter_windowmanager.dart';
import 'package:screen_protector/screen_protector.dart';
import 'pages/home_page.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Android tem esse plugin
if (Platform.isAndroid) {
await FlutterWindowManager.addFlags(FlutterWindowManager.FLAG_SECURE);
}
// Android FLAG_SECURE
// iOS blur no app switcher + bloqueio de screen recording
await ScreenProtector.preventScreenshotOn();
await ScreenProtector.protectDataLeakageOn();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) async {
// iOS: protege quando vai para background / app switcher
if (state == AppLifecycleState.paused ||
state == AppLifecycleState.inactive) {
await ScreenProtector.protectDataLeakageOn();
}
// remove blur ao voltar
if (state == AppLifecycleState.resumed) {
await ScreenProtector.protectDataLeakageOff();
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(

View File

@ -2,10 +2,11 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:audioplayers/audioplayers.dart';
//import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:just_audio/just_audio.dart';
import 'package:path_provider/path_provider.dart';
import 'package:MakerBook/pages/epub_page.dart';
import 'package:MakerBook/pages/qr_code_scanner.dart';
@ -54,8 +55,11 @@ class _LoginPageState extends State<LoginPage> {
await baixarOuAtualizarArquivo(book_id, bookPath, ext, modifiedAt);
//final player = AudioPlayer();
//await player.play(AssetSource('sounds/open_book.mp3'));
final player = AudioPlayer();
await player.play(AssetSource('sounds/open_book.mp3'));
await player.setAsset('assets/sounds/open_book.mp3');
await player.play();
Navigator.push(
context,

View File

@ -1,4 +1,3 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
@ -10,17 +9,27 @@ class PrivacyPage extends StatefulWidget {
}
class _PrivacyPageState extends State<PrivacyPage> {
late final WebViewController _controller;
bool _loading = true;
//late WebViewController _controller;
@override
void initState() {
super.initState();
// Necessário para Android quando usando WebView em SDKs antigos
if (Platform.isAndroid) {
WebView.platform = SurfaceAndroidWebView();
}
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(
NavigationDelegate(
onPageFinished: (_) {
setState(() => _loading = false);
},
onWebResourceError: (error) {
// Opcional: se quiser mostrar algo quando falhar
// print('WebView error: ${error.description}');
},
),
)
..loadRequest(Uri.parse('https://makereducacional.com.br/privacy.html'));
}
@override
@ -34,22 +43,9 @@ class _PrivacyPageState extends State<PrivacyPage> {
),
body: Stack(
children: [
WebView(
initialUrl: 'https://makereducacional.com.br/privacy.html',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (controller) {
//_controller = controller;
},
onPageFinished: (_) {
setState(() {
_loading = false;
});
},
),
WebViewWidget(controller: _controller),
if (_loading)
const Center(
child: CircularProgressIndicator(),
),
const Center(child: CircularProgressIndicator()),
],
),
);

View File

@ -1,62 +1,53 @@
import 'package:flutter/material.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
class QRCodeScannerPage extends StatefulWidget {
const QRCodeScannerPage({Key? key}) : super(key: key);
@override
_QRCodeScannerPageState createState() => _QRCodeScannerPageState();
State<QRCodeScannerPage> createState() => _QRCodeScannerPageState();
}
class _QRCodeScannerPageState extends State<QRCodeScannerPage> {
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
QRViewController? controller;
bool lido = false;
@override
void dispose() {
controller?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Leia o QR Code')),
body: Stack(
children: [
// Câmera
QRView(
key: qrKey,
onQRViewCreated: _onQRViewCreated,
// 📷 Câmera
MobileScanner(
fit: BoxFit.cover,
onDetect: (capture) {
if (lido) return;
final barcode = capture.barcodes.firstOrNull;
final value = barcode?.rawValue;
if (value == null) return;
lido = true;
debugPrint('[LOG] Leitura QRCode: $value');
Navigator.pop(context, value);
},
),
// Escurecimento com buraco no centro
Positioned.fill(
// 🕶 Overlay com janela central
const Positioned.fill(
child: ScannerOverlay(),
),
],
),
);
}
void _onQRViewCreated(QRViewController controller) {
print("[LOG] QRCode criado");
this.controller = controller;
controller.scannedDataStream.listen((scanData) {
if (!lido) {
lido = true;
print("[LOG] Leitura QRCode: ${scanData.code}");
Navigator.pop(context, scanData.code); // Retorna o código escaneado
}
});
}
}
class ScannerOverlay extends StatelessWidget {
const ScannerOverlay({super.key});
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
@ -68,10 +59,10 @@ class ScannerOverlay extends StatelessWidget {
return Stack(
children: [
// Camada escura semi-transparente
// Camada escura
Container(color: Colors.black.withOpacity(0.5)),
// Janela recortada no centro
// Janela central
Positioned(
left: left,
top: top,

View File

@ -0,0 +1,16 @@
import 'dart:io';
import 'package:flutter/services.dart';
class IosSecureScreen {
static const MethodChannel _ch = MethodChannel('ios_secure_screen');
static Future<void> enable() async {
if (!Platform.isIOS) return;
await _ch.invokeMethod('enable');
}
static Future<void> disable() async {
if (!Platform.isIOS) return;
await _ch.invokeMethod('disable');
}
}

View File

@ -1,10 +1,10 @@
import 'dart:io';
import 'package:flutter_windowmanager/flutter_windowmanager.dart';
//import 'package:flutter_windowmanager/flutter_windowmanager.dart';
class SecureScreen {
static Future<void> enable() async {
if (Platform.isAndroid) {
await FlutterWindowManager.addFlags(FlutterWindowManager.FLAG_SECURE);
//await FlutterWindowManager.addFlags(FlutterWindowManager.FLAG_SECURE);
}
}
}

View File

@ -7,7 +7,7 @@ import 'package:MakerBook/utils/global.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:vocsy_epub_viewer/epub_viewer.dart';
//import 'package:vocsy_epub_viewer/epub_viewer.dart';
class EpubViewerPage extends StatefulWidget {
final int book_id;
@ -28,14 +28,14 @@ class _EpubViewerPageState extends State<EpubViewerPage> {
_prepareTempEpub();
// Registra um listener para capturar o lastLocation quando o leitor informar
VocsyEpub.locatorStream.listen((locator) async {
/*VocsyEpub.locatorStream.listen((locator) async {
print('Localização atual: $locator');
// Armazena o locator como um Map<String, dynamic> em vez de string diretamente
lastLocation = locator;
await saveLastLocation(widget.book_id, lastLocation);
Navigator.pop(context);
});
});*/
//openLocalEpub();
}
@ -49,7 +49,7 @@ class _EpubViewerPageState extends State<EpubViewerPage> {
await file.writeAsBytes(data.buffer.asUint8List());
// Config obrigatória
VocsyEpub.setConfig(
/*VocsyEpub.setConfig(
themeColor: Colors.blue,
identifier: 'test-book',
scrollDirection: EpubScrollDirection.VERTICAL,
@ -57,7 +57,7 @@ class _EpubViewerPageState extends State<EpubViewerPage> {
enableTts: false,
);
VocsyEpub.open(path);
VocsyEpub.open(path);*/
}
@override
@ -84,19 +84,19 @@ class _EpubViewerPageState extends State<EpubViewerPage> {
lastLocation = await getLastLocation(widget.book_id);
// Configuração do leitor
VocsyEpub.setConfig(
/*VocsyEpub.setConfig(
themeColor: Theme.of(context).primaryColor,
identifier: widget.book_id.toString(),
scrollDirection: EpubScrollDirection.ALLDIRECTIONS,
allowSharing: false,
enableTts: false,
);
);*/
// Abrindo o arquivo EPUB e usando o lastLocation se estiver disponível
VocsyEpub.open(
/*VocsyEpub.open(
tempFile.path,
lastLocation: lastLocation != null ? EpubLocator.fromJson(jsonDecode(lastLocation!)) : null,
);
);*/
}

View File

@ -1,120 +0,0 @@
import 'dart:convert';
import 'dart:io';
import 'package:MakerBook/utils/encrypt.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:vocsy_epub_viewer/epub_viewer.dart';
class EpubViewer2Page extends StatefulWidget {
final int book_id;
const EpubViewer2Page({super.key, required this.book_id});
@override
_EpubViewer2PageState createState() => _EpubViewer2PageState();
}
class _EpubViewer2PageState extends State<EpubViewer2Page> {
String? lastLocation;
String? epubPath;
@override
void initState() {
super.initState();
_prepareTempEpub();
//_initializeEpubController();
// Registra um listener para capturar o lastLocation quando o leitor informar
VocsyEpub.locatorStream.listen((locator) async {
print('Localização atual: $locator');
// Armazena o locator como um Map<String, dynamic> em vez de string diretamente
lastLocation = locator;
await _saveLastLocation();
});
}
@override
void dispose() {
super.dispose();
}
// Função para salvar a última posição de leitura
Future<void> _saveLastLocation() async {
if (lastLocation == null) return;
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/${widget.book_id}.epub');
// Recarrega o conteúdo protegido atual, atualiza o lastLocation, e salva novamente
String protectedContent = await file.readAsString();
Map<String, dynamic> fileData = unprotectFile(protectedContent);
String updatedProtectedContent = await protectFile(fileData['epubBytes'], lastLocation!);
await file.writeAsString(updatedProtectedContent);
Navigator.pop(context);
}
// Prepara o arquivo temporário para o leitor EPUB
Future<void> _prepareTempEpub_bkp() async {
final directoryReal = await getApplicationDocumentsDirectory();
final file = File('${directoryReal.path}/${widget.book_id}.epub');
final directory = await getTemporaryDirectory();
final tempFile = File('${directory.path}/${widget.book_id}_temp.epub');
// o conteúdo protegido e remove a proteção
String protectedContent = await file.readAsString();
Map<String, dynamic> fileData = unprotectFile(protectedContent);
List<int> unprotectedBytes = fileData['epubBytes'];
lastLocation = fileData['lastLocation']; // Recupera o lastLocation
// Salva o arquivo descriptografado no diretório temporário
await tempFile.writeAsBytes(unprotectedBytes);
// Configuração do leitor
VocsyEpub.setConfig(
themeColor: Theme.of(context).primaryColor,
identifier: widget.book_id.toString(),
scrollDirection: EpubScrollDirection.ALLDIRECTIONS,
allowSharing: false,
enableTts: false,
);
// Abrindo o arquivo EPUB e usando o lastLocation se estiver disponível
VocsyEpub.open(
tempFile.path,
lastLocation: lastLocation != null ? EpubLocator.fromJson(jsonDecode(lastLocation!)) : null,
);
}
// Prepara o arquivo temporário para o leitor EPUB
Future<void> _prepareTempEpub() async {
final directoryReal = await getApplicationDocumentsDirectory();
final file = File('${directoryReal.path}/${widget.book_id}.epub');
// Configuração do leitor
VocsyEpub.setConfig(
themeColor: Theme.of(context).primaryColor,
identifier: widget.book_id.toString(),
scrollDirection: EpubScrollDirection.ALLDIRECTIONS,
allowSharing: false,
enableTts: false,
);
// Abrindo o arquivo EPUB e usando o lastLocation se estiver disponível
VocsyEpub.open(
file.path,
lastLocation: lastLocation != null ? EpubLocator.fromJson(jsonDecode(lastLocation!)) : null,
);
}
@override
Widget build(BuildContext context) {
return const Center(child: CircularProgressIndicator());
}
}

View File

@ -1,6 +1,7 @@
// widgets/pdf_viewer.dart
import 'dart:convert';
import 'package:MakerBook/secure_screen/ios_secure_screen.dart';
import 'package:MakerBook/utils/global.dart';
import 'package:flutter/material.dart';
import 'package:flutter_pdfview/flutter_pdfview.dart';
@ -34,6 +35,7 @@ class _PdfViewerState extends State<PdfViewer> {
@override
void initState() {
super.initState();
IosSecureScreen.enable(); // <- liga a tela segura no iOS
_loadPdf();
_verificaSeMostraTutorial();
}
@ -41,6 +43,7 @@ class _PdfViewerState extends State<PdfViewer> {
@override
void dispose() {
_saveLastPage();
IosSecureScreen.enable(); // <- liga a tela segura no iOS
super.dispose();
}
@ -380,7 +383,8 @@ class _PdfViewerState extends State<PdfViewer> {
void _verificaSeMostraTutorial() async {
final prefs = await SharedPreferences.getInstance();
bool jaViu = true; // prefs.getBool('tutorialPdfVisto') ?? false;
bool jaViu = prefs.getBool('tutorialPdfVisto') ?? false;
//bool jaViu = false;
setState(() {
setaTutorial = !jaViu;

View File

@ -8,28 +8,40 @@ class WebViewer extends StatefulWidget {
const WebViewer({super.key, required this.book_id});
@override
_WebViewerState createState() => _WebViewerState();
State<WebViewer> createState() => _WebViewerState();
}
class _WebViewerState extends State<WebViewer> {
late WebViewController _controller;
late final WebViewController _controller;
bool _loading = true;
final String baseUrl = "http://192.168.99.115:8080/#fmt=EPUB&mode=read_book&book_id=";
final String baseUrl =
"http://192.168.99.115:8080/#fmt=EPUB&mode=read_book&book_id=";
@override
void initState() {
super.initState();
if (Platform.isAndroid) {
WebView.platform = SurfaceAndroidWebView();
}
final fullUrl = baseUrl + widget.book_id;
// Controller novo (API 4.x)
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setUserAgent("Mozilla/5.0 (Mobile; Flutter)")
..setNavigationDelegate(
NavigationDelegate(
onPageFinished: (_) {
setState(() {
_loading = false;
});
},
),
)
..loadRequest(Uri.parse(fullUrl));
}
@override
Widget build(BuildContext context) {
final fullUrl = baseUrl + widget.book_id;
return Scaffold(
appBar: AppBar(
title: const Text("Leitor Online"),
@ -38,21 +50,11 @@ class _WebViewerState extends State<WebViewer> {
),
body: Stack(
children: [
WebView(
initialUrl: fullUrl,
javascriptMode: JavascriptMode.unrestricted,
userAgent: "Mozilla/5.0 (Mobile; Flutter)",
onWebViewCreated: (controller) {
_controller = controller;
},
onPageFinished: (_) {
setState(() {
_loading = false;
});
},
),
WebViewWidget(controller: _controller),
if (_loading)
const Center(child: CircularProgressIndicator()),
const Center(
child: CircularProgressIndicator(),
),
],
),
);

View File

@ -6,10 +6,6 @@
#include "generated_plugin_registrant.h"
#include <audioplayers_linux/audioplayers_linux_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
}

View File

@ -3,7 +3,6 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
audioplayers_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -5,12 +5,18 @@
import FlutterMacOS
import Foundation
import audioplayers_darwin
import audio_session
import just_audio
import mobile_scanner
import path_provider_foundation
import shared_preferences_foundation
import webview_flutter_wkwebview
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin"))
JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin"))
}

View File

@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.3+14
version: 1.0.4+18
environment:
sdk: '>=3.4.3 <4.0.0'
@ -32,18 +32,22 @@ dependencies:
sdk: flutter
flutter_pdfview: ^1.0.4
http: ^1.4.0
flutter_windowmanager: ^0.2.0
qr_code_scanner: ^1.0.0
vocsy_epub_viewer: ^3.0.0
#flutter_windowmanager: ^0.2.0
#qr_code_scanner: ^1.0.1
mobile_scanner: ^6.0.2
#vocsy_epub_viewer: ^3.0.0
encrypt: ^5.0.0
epub_view: ^3.2.0
webview_flutter: ^3.0.4
flutter_svg: ^1.1.6
webview_flutter: ^4.13.0
flutter_svg: ^2.2.3
flutter_launcher_icons: ^0.11.0
google_fonts: ^4.0.4
audioplayers: ^5.2.1
google_fonts: ^6.3.3
#audioplayers: ^6.5.1
just_audio: ^0.9.38
tutorial_coach_mark: ^1.3.2
shared_preferences: ^2.2.0
screen_protector: ^1.4.2
#screenshot_callback: ^3.0.0
# The following adds the Cupertino Icons font to your application.
@ -66,7 +70,7 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^3.0.0
flutter_lints: ^6.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View File

@ -6,9 +6,6 @@
#include "generated_plugin_registrant.h"
#include <audioplayers_windows/audioplayers_windows_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
AudioplayersWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
}

View File

@ -3,7 +3,6 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
audioplayers_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST