youscribe_sdk (1.1.0)
Installation
dart pub add youscribe_sdk:1.1.0 --hosted-url=About this package
YouScribe SDK - Version 1.1.0
Ce document présente le SDK YouScribe pour Flutter, conçu pour intégrer la lecture de produits YouScribe dans une application hôte. Il détaille les fonctionnalités, l'architecture, les étapes d'installation et d'intégration, ainsi que des exemples d'usage recommandés.
ATTENTION Cette documentation est en cours de rédaction et peut être sujette à des modifications.
Le SDK prend en charge :
- l'initialisation et la configuration des accès API ;
- l'ouverture d'un produit à partir de son
productId; - le téléchargement, le suivi et la gestion des fichiers locaux ;
- la lecture PDF, EPUB et audio via un widget prêt à l'emploi ;
- la personnalisation des thèmes de lecture ;
- l'émission d'événements techniques et de téléchargement.
Architecture
Le SDK s'articule autour de quatre points d'entrée publics :
YouScribeSDKpour l'initialisation et les opérations de service ;YouScribeReaderpour ouvrir un produit ;YouScribeConfigpour la configuration ;DownloadTaskpour suivre un téléchargement.
Le point d'entrée principal est le singleton YouScribeSDK . L'application hôte l'initialise au démarrage, lui fournit son token, puis utilise :
YouScribeReaderpour ouvrir un produit ;download/trackDownload/watchActiveDownloadspour piloter les téléchargements.
Fonctionnalités disponibles
Lecture produit
YouScribeReader(productId: ...) encapsule le flux standard :
- vérification de la disponibilité locale ou distante du produit ;
- déclenchement du téléchargement si nécessaire ;
- suivi de la tâche jusqu'à disponibilité du fichier ;
- ouverture du lecteur adapté au type de fichier disponible : PDF, EPUB ou audio.
Téléchargement et cache local
Le SDK expose un vrai cycle de téléchargement :
- démarrage avec
download(productId: ...); - rattachement à une tâche existante avec
trackDownload(productId: ...); - observation globale avec
watchActiveDownloads(); - annulation avec
DownloadTask.cancel()oustopDownload(productId); - récupération des fichiers locaux avec
getDownloadedFiles(); - suppression d'un fichier avec
deleteFile(productId: ...).
Une tâche de téléchargement expose :
snapshotpour l'état courant ;snapshotStreampour suivre l'évolution complète ;progressStreampour une progression simple ;completedpour attendre la disponibilité finale du fichier.
Événements SDK
Le singleton expose events , un stream d'événements émis par le SDK. En pratique, ce flux permet surtout de journaliser :
- les événements HTTP de diagnostic ;
- les erreurs SDK ;
- le cycle de vie des téléchargements.
Le flag enableEventDebugPrint permet de dupliquer ces événements dans debugPrint .
Installation
Le SDK est publié sur le registre Dart privé Wyatt Studio. La méthode recommandée pour l'ajouter à un projet Flutter est d'utiliser la commande suivante :
dart pub add youscribe_sdk:1.1.0 --hosted-url=https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
Cette commande ajoute automatiquement la dépendance avec le bon registre dans votre pubspec.yaml .
Puis :
flutter pub get
Vous pourriez être amené à forcer certaines versions de dépendances:
dependency_overrides:
youscribe_audio_player:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
version: 1.1.0
youscribe_epub_reader:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
version: 1.1.0
youscribe_pdf_reader:
hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/
version: 1.1.0
nutrient_flutter: 5.3.1
Configuration native Android pour la liseuse PDF
La lecture PDF repose sur le SDK Android de Nutrient. Dans une application Flutter existante, il faut donc compléter la configuration native Android de l'application hôte en plus de l'ajout de la dépendance Flutter.
Note
Les versions exactes de
compileSdkVersionetminSdkVersionpeuvent évoluer selon la version de Nutrient utilisée. Le guide officiel Nutrient reste la référence pour les valeurs minimales à jour : https://www.nutrient.io/sdk/flutter/getting-started/
1. Mettre à jour la configuration Gradle Android
Dans android/app/build.gradle ou android/app/build.gradle.kts , vérifiez au minimum les points suivants :
android {
compileSdkVersion 35
defaultConfig {
minSdkVersion 21
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:<version>'
}
Si votre projet utilise la syntaxe Kotlin DSL ( build.gradle.kts ), adaptez simplement la syntaxe. L'important est d'utiliser Java 17, Kotlin JVM 17 et d'ajouter androidx.appcompat .
2. Mettre à jour le thème Android
Dans android/app/src/main/res/values/styles.xml , remplacez le parent du thème principal :
<style name="NormalTheme" parent="Theme.AppCompat.Light.NoActionBar">
par :
<style name="NormalTheme" parent="PSPDFKit.Theme.Default">
Si votre application définit aussi un thème nuit dans android/app/src/main/res/values-night/styles.xml , appliquez le même parent ou une variante compatible avec votre thème sombre.
3. Utiliser FlutterAppCompatActivity
Mettez à jour votre activité principale Android pour hériter de FlutterAppCompatActivity :
import io.flutter.embedding.android.FlutterAppCompatActivity
class MainActivity : FlutterAppCompatActivity()
Cette étape est nécessaire pour permettre à la couche Android de la liseuse PDF de s'intégrer correctement à l'activité Flutter hôte.
4. Ajouter l'icône retour Android
Copiez le fichier back_icon.xml dans :
android/app/src/main/res/drawable/back_icon.xml
Vous pouvez reprendre l'icône utilisée dans l'exemple du SDK :
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000000"
android:pathData="M20,11H7.83L13.42,5.41L12,4L4,12L12,20L13.41,18.59L7.83,13H20V11Z"/>
</vector>
5. Vérifier la configuration avant intégration
Avant d'ouvrir YouScribeReader , vérifiez que :
- l'application Android compile bien avec Java 17 ;
androidx.appcompatest bien présent dans les dépendances de l'application hôte ;MainActivityhérite deFlutterAppCompatActivity;- le thème
NormalThemeutilise bienPSPDFKit.Theme.Default; back_icon.xmlest présent dans les resources Android.
Intégration dans une application Flutter
1. Initialiser le SDK au démarrage
L'initialisation doit être faite une fois avant tout accès à YouScribeSDK.instance .
import 'package:flutter/material.dart';
import 'package:youscribe_sdk/youscribe_sdk.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await YouScribeSDK.initialize(
config: YouScribeConfig(
apiBaseUrl: 'https://api-mobile.youscribe.com',
audioStreamingBaseUrl: 'https://api-streaming.youscribe.com',
authToken: '',
locale: const Locale('fr'),
enableEventDebugPrint: true,
onTokenExpired: () async {
final refreshedToken = await refreshTokenFromHostApp();
YouScribeSDK.instance.updateAuthToken(refreshedToken);
},
),
);
runApp(const MyApp());
}
Points importants :
initialize()est idempotent : après la première initialisation, les appels suivants renvoient la même instance et ne réappliquent pas une nouvelle configuration ;- si vous avez besoin de changer le token après login ou refresh, utilisez
updateAuthToken(); - si vous avez besoin de cloisonner les données locales par utilisateur, utilisez
updateLocalDataDiscriminator().
2. Brancher l'authentification de l'app hôte
Le SDK ne gère pas le login. L'application hôte reste responsable de :
- récupérer le token d'accès ;
- le transmettre au SDK ;
- gérer son refresh ;
- gérer les changements d'utilisateur.
Exemple après authentification :
Future<void> onUserSignedIn({
required String authToken,
required String userId,
}) async {
final sdk = YouScribeSDK.instance;
sdk.updateAuthToken(authToken);
sdk.updateLocalDataDiscriminator(userId);
}
3. Ouvrir un produit dans l'application
Le point d'intégration le plus simple est YouScribeReader .
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => const YouScribeReader(productId: '123456'),
),
);
Ce widget gère lui-même le chargement du produit. Dans le cas standard, il n'est pas nécessaire d'appeler readProduct() à la main.
4. Personnaliser les thèmes de lecture
YouScribeReader expose deux niveaux de personnalisation :
themepour définir une palette commune à toutes les liseuses ;pdfReaderTheme,epubReaderThemeetaudioReaderThemepour surcharger un lecteur précis.
L'ordre de priorité est le suivant :
- thème spécifique au lecteur ;
- thème partagé
YouScribeReaderTheme; - thème par défaut fourni par le SDK.
Exemple avec un thème partagé cohérent sur les trois liseuses :
final readerTheme = YouScribeReaderTheme.defaultTheme().copyWith(
accentColor: const Color(0xFF0F766E),
textColor: const Color(0xFF1F2937),
backButtonLabel: 'Retour',
);
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => YouScribeReader(
productId: '123456',
theme: readerTheme,
),
),
);
Exemple avec une surcharge ciblée pour l'audio uniquement :
final sharedTheme = YouScribeReaderTheme.defaultTheme();
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => YouScribeReader(
productId: '123456',
theme: sharedTheme,
audioReaderTheme: sharedTheme.toAudioReaderTheme().copyWith(
primaryColor: const Color(0xFF7C3AED),
previewColor: const Color(0xFFD97706),
),
),
),
);
Si vous préférez piloter chaque reader séparément, les classes PdfReaderTheme , EpubReaderTheme et AudioReaderTheme sont aussi exportées par package:youscribe_sdk/youscribe_sdk.dart .
5. Personnaliser les écrans de chargement
Le SDK distingue deux étapes de chargement :
productLoadingBuilderpour le chargement SDK avant qu'un reader concret soit prêt ;readerLoadingBuilderpour le chargement interne des readers PDF, EPUB et audio.
Ces builders permettent soit d'afficher votre propre écran, soit de masquer totalement le loading par défaut avec un widget vide.
Exemple minimal :
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => YouScribeReader(
productId: '123456',
productLoadingBuilder: (_) => const SizedBox.shrink(),
readerLoadingBuilder: (_) => const Center(
child: CircularProgressIndicator(),
),
),
),
);
Configuration
YouScribeConfig centralise la configuration du SDK.
| Paramètre | Rôle | Valeur par défaut |
|---|---|---|
apiBaseUrl |
URL de base des APIs YouScribe | https://api-mobile.youscribe.com |
audioStreamingBaseUrl |
URL de base des endpoints audio | https://api-streaming.youscribe.com |
authToken |
Token transmis au backend | '' |
localDataDiscriminator |
Préfixe logique de stockage local par utilisateur/environnement | 'default' |
enableEventDebugPrint |
Duplication des événements SDK vers debugPrint |
true |
locale |
Locale UI du SDK | Locale('fr') |
onTokenExpired |
Callback invoqué sur expiration de session pour refresh du token | null |
Le paramètre localDataDiscriminator permet de séparer les données locales entre plusieurs utilisateurs ou plusieurs environnements.
API d'usage recommandée
Ouvrir un produit
Usage recommandé pour une app classique :
await Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => const YouScribeReader(productId: '123456'),
),
);
Pré-télécharger un produit
Pour télécharger sans ouvrir immédiatement le reader :
final task = await YouScribeSDK.instance.download(productId: '123456');
task.snapshotStream.listen((snapshot) {
debugPrint(
'status=${snapshot.status} progress=${snapshot.progress}',
);
});
final file = await task.completed;
debugPrint('Local file ready: ${file.metadata.uri}');
Pour un téléchargement temporaire :
await YouScribeSDK.instance.download(
productId: '123456',
temporary: true,
);
Reprendre le suivi d'un téléchargement en cours
final task = await YouScribeSDK.instance.trackDownload(productId: '123456');
if (task != null) {
task.snapshotStream.listen((snapshot) {
debugPrint('status=${snapshot.status}');
});
}
Observer tous les téléchargements actifs
YouScribeSDK.instance.watchActiveDownloads().listen((downloads) {
for (final download in downloads) {
debugPrint(
'product=${download.productId} status=${download.status} progress=${download.progress}',
);
}
});
Vérifier la disponibilité locale d'un fichier
final isAvailable = await YouScribeSDK.instance.isFileAvailable(
productId: '123456',
);
Lister et supprimer les fichiers téléchargés
final files = await YouScribeSDK.instance.getDownloadedFiles();
for (final file in files) {
debugPrint('${file.product.id} -> ${file.metadata.uri}');
}
await YouScribeSDK.instance.deleteFile(productId: '123456');
Hooks de lecture PDF
YouScribeReader accepte pdfReadingHooks pour étendre le comportement standard.
Exemple :
YouScribeReader(
productId: '123456',
pdfReadingHooks: PdfReadingHooks(
onOpenBook: ({required pdf, required pageCount, required pageIndex}) {
debugPrint('Opened ${pdf.productId} at page $pageIndex / $pageCount');
},
onPageChanged: ({
required pdf,
required oldPageIndex,
required newPageIndex,
required pageCount,
}) {
debugPrint('Page changed: $oldPageIndex -> $newPageIndex');
},
onError: ({pdf, required errorMessage}) {
debugPrint('Reader error: $errorMessage');
},
),
)
Gestion des erreurs
Les opérations du SDK peuvent échouer pour des raisons réseau, d'authentification, de téléchargement ou de lecture.
Dans une app hôte, le plus simple est de :
- entourer les appels réseau/lecture par un
try/catch; - afficher un message utilisateur adapté ;
- journaliser l'erreur ;
- s'appuyer sur
onTokenExpiredpour le refresh de session.
Exemple :
try {
await YouScribeSDK.instance.readProduct('123456');
} catch (error) {
debugPrint('SDK error: $error');
}
Écouter les événements du SDK
final subscription = YouScribeSDK.instance.events.listen((event) {
debugPrint('SDK event: $event');
});
Ce flux est particulièrement utile pour :
- suivre les téléchargements ;
- observer les erreurs centralisées ;
- diagnostiquer les échanges HTTP en environnement de développement.
Cycle de vie conseillé dans l'app hôte
Flux recommandé :
- initialiser le SDK au lancement de l'app ;
- injecter ou mettre à jour le token après login ;
- mettre à jour le
localDataDiscriminatorquand l'utilisateur change ; - ouvrir les produits via
YouScribeReader; - utiliser
download()pour le préchargement hors ligne ; - appeler
dispose()si vous voulez libérer explicitement le singleton.