Skip to main content
Blog 8 min read

Je publie ma première librairie Flutter ! 🚀

Comment j'ai résolu le cauchemar de l'installation PWA et des navigateurs In-App.

F
Fabien Chung
Je publie ma première librairie Flutter ! 🚀

Pendant longtemps, ma routine de développeur face à un nouveau besoin se résumait à ça : imaginer la solution, trouver le package open-source qui allait faire le gros du travail, taper machinalement flutter pub add ou npm install dans mon terminal... et passer à la suite. On est tellement habitués à consommer ces librairies gratuitement qu'on en oublie presque l'auteur inconnu qui a pris le temps de les développer.

Mais quand il n'y avait pas de package miracle, je retombais dans une autre routine bien connue des développeurs : le fameux fichier utils ou la classe spécifique que l'on traîne d'un projet à l'autre.

Pour moi, c'était la gestion de l'installation de mes PWA (Progressive Web Apps). Je l'ai d'abord codée pour un side-project puis inclus dans mon app LOVT, puis je l'ai copiée pour une mission client et ainsi de suite.

Développeur copiant-collant du code entre projets FlutterDéveloppeur copiant-collant du code entre projets Flutter

Jusqu'au jour où j'ai dû corriger un bug sur la détection iOS dans le projet A, et que j'ai réalisé avec lassitude que je devais aller appliquer ce même correctif manuellement dans les projets B et C.

C'était le signal. Au lieu de continuer à bricoler dans mon coin, pourquoi ne pas en faire une librairie ?

Après tout, j'ai passé des années à profiter du travail open source de la communauté (merci aux mainteneurs de url_launcher ou device_info_plus qui me sauvent la vie au quotidien). Il était temps pour moi de "renvoyer l'ascenseur" et de transformer mon "hack" personnel en une solution propre, centralisée et partageable.

Voici l'histoire de ma première librairie sur pub.dev.


Le problème : Le cauchemar des PWA et des "In-App Browsers"

Si vous avez lu mon précédent article sur les PWA, vous savez que j'adore cette technologie pour sa rapidité de déploiement. Mais elle vient avec deux douleurs majeures pour l'expérience utilisateur :

  1. L'installation est invisible : Sur iOS (et parfois Android), il n'y a pas de bouton magique "Installer". Il faut expliquer à l'utilisateur de cliquer sur "Partager" puis "Sur l'écran d'accueil". Sans un tutoriel visuel, personne ne le fait.
  2. Le piège des réseaux sociaux (In-App Browsers) : C'est le point critique. Si un utilisateur clique sur le lien de votre app depuis Instagram, TikTok ou Facebook, votre PWA s'ouvre dans leur navigateur interne.
    • Conséquence 1 : Les cookies et sessions sautent souvent.
    • Conséquence 2 : Les permissions (caméra, micro, géolocalisation) sont souvent bloquées ou buggées.

Pour une app comme la mienne qui nécessite des accès, c'est fatal. L'utilisateur pense que l'app ne marche pas.

PWA ne fonctionnant pas dans un navigateur in-appPWA ne fonctionnant pas dans un navigateur in-app

La solution : pwa_installer

J'ai donc décidé de créer un package qui gère tout ça automatiquement.

L'objectif était simple : proposer un Widget unique qui détecte l'environnement de l'utilisateur et agit en conséquence.

  • Sur un navigateur classique (Chrome/Safari) : Il affiche une belle page d'instruction adaptée à l'OS (iOS ou Android) pour guider l'installation.
  • Sur un Desktop : On peut choisir de bloquer l'accès ("Mobile only") ou de laisser passer utiliser l'application sans avoir à l'installer.
  • Sur un In-App Browser (TikTok/Insta) : Il détecte le User Agent et propose (ou force) une redirection vers le navigateur système (Chrome/Safari) pour garantir que l'app fonctionne.
Guide d'installation PWA pour appareils AndroidGuide d'installation PWA pour appareils Android QR code desktop pour rediriger vers la PWA mobileQR code desktop pour rediriger vers la PWA mobile Détection de navigateur in-app et redirection vers le navigateur systèmeDétection de navigateur in-app et redirection vers le navigateur système

Démystification : Un package, c'est juste du code

Après avoir recherché comment publier un package, je me suis rendu compte que ce n'était finalement pas si compliqué que ça. La structure est la même que pour une application Flutter classique :

  • Un fichier pubspec.yaml
  • Un dossier lib
  • Du code Dart.

C'est tout. N'importe quel développeur peut le faire, même un junior. La barrière n'est pas technique, elle est psychologique.


Le défi de l'Abstraction

C'est là que les choses se corsent. Quand ce code était dans mon projet LOVT, il était "hardcodé" : le logo de LOVT, les couleurs de LOVT, les textes de LOVT.

Pour en faire une librairie, j'ai dû faire un gros travail d'abstraction. Ça s'est traduit par quatre axes principaux.

Le logo : un Widget?, pas une String. La première tentation aurait été d'accepter un chemin vers un asset. Mais c'est trop restrictif — chaque app charge ses images différemment (assets, réseau, SVG…). En acceptant un Widget?, on laisse l'app passer ce qu'elle veut. Si null, la zone est simplement omise.

Les textes : PwaInstallerLabels et le pattern copyWith. L'i18n est souvent le parent pauvre des librairies UI. La classe PwaInstallerLabels centralise les 30+ chaînes nécessaires (iOS 26+, iOS legacy, Android, Desktop QR…) avec des valeurs par défaut en anglais. Le pattern copyWith permet de ne surcharger que ce dont on a besoin, sans réécrire l'objet entier.

Le thème : trois niveaux de personnalisation. PwaInstallerTheme propose deux presets prêts à l'emploi (defaultTheme dark, lightTheme), une factory fromContext qui lit automatiquement le ColorScheme Material de l'app, et un copyWith pour ajuster n'importe quel token (couleur, gradient, borderRadius, padding).

Les écrans custom : des builder functions. Pour les cas où le thème ne suffit pas, chaque écran peut être entièrement remplacé. Le onDismiss passé en paramètre est null quand forceInstall: true — c'est le signal pour cacher le bouton "Continuer sans installer".

dart35 lines
1PwaInstaller.init( 2 // forceInstall: true = l'utilisateur est bloqué jusqu'à l'installation. 3 // et ne peut pas accéder à l'application depuis le navigateur. 4 forceInstall: true, 5); 6 7runApp( 8 MaterialApp( 9 home: PwaInstaller( 10 // L'app passe ce qu'elle veut : Image.asset, SvgPicture, NetworkImage... 11 logo: Image.asset('assets/logo.png'), 12 appName: 'My App', 13 14 // Hérite du thème Material de l'app, (defaultTheme / lightTheme) 15 // mais on peut aussi surcharger les valeurs une par une avec copyWith(). 16 theme: PwaInstallerTheme.fromContext(context).copyWith( 17 accentColor: Colors.deepPurple, 18 ), 19 20 // Tout les labels de la vue peuvent être surchargés avec copyWith(). 21 // Idéal si on veut traduire en plusieurs langues. 22 labels: const PwaInstallerLabels().copyWith( 23 titleAdd: 'Ajouter ', 24 titleToHomeScreen: 'à l'écran d'accueil', 25 ), 26 27 // Si le thème ne suffit pas, on peut remplacer l'écran entièrement. 28 customMobileScreen: (context, onDismiss) => MyCustomInstallScreen( 29 onDismiss: onDismiss, 30 ), 31 32 child: MyHomePage(), 33 ), 34 ), 35);

C'est l'étape la plus intéressante techniquement : passer d'un code "produit" à un code "outil". Chaque décision de design — Widget? plutôt que String, copyWith plutôt qu'un constructeur à 30 paramètres, des builder functions pour les écrans custom — est un choix conscient pour garder l'API simple en surface tout en restant flexible en profondeur.

La "Pre-Flight Checklist" : Mes vérifications avant le décollage

Publier sur pub.dev implique une certaine rigueur que je me suis imposée. On ne publie pas du code "sale". Voici la checklist que j'ai suivie pour être le plus professionnel possible :

1. Le Linter impitoyable 🧹 J'ai configuré mon fichier analysis_options.yaml pour être strict. L'objectif : zéro warning. Pas de variables non utilisées, pas de print oubliés. Un code propre inspire confiance.

2. La Documentation (La vraie) 📚 Il y a deux types de docs et les deux sont indispensables :

  • Le README.md : C'est la vitrine marketing. J'ai inclus des Gifs, des captures d'écran et un exemple "Copier-Coller" pour que l'utilisateur comprenne en 10 secondes à quoi sert la lib.
  • La Dart Doc (///) : J'ai documenté chaque propriété publique. C'est ce qui permet à votre IDE de vous afficher l'aide quand vous survolez une variable.

3. Le dossier example n'est pas une option 📱 Pour une librairie visuelle comme la mienne, le dossier example est vital. J'ai dû créer une mini-app complète à l'intérieur du package. C'est du travail en plus, mais cela permet aux développeurs de cloner le repo et de tester la redirection ou l'affichage immédiatement.

4. La chasse aux "Pub Points" avec Pana 💯 Le site pub.dev attribue une note sur 140 à votre package. Cette note est calculée par un outil appelé pana. Je l'ai fait tourner en local plusieurs fois pour corriger les petits détails (formatage du code, longueur de la description) et viser le score de 130+/140 dès la sortie. C'est un gage de qualité pour ceux qui découvriront la librairie.

5. Le filet de sécurité : Dry Run 🚀 Avant d'appuyer sur le bouton rouge, la commande magique : flutter pub publish --dry-run Elle simule la publication et vérifie que tout est vert. C'est le dernier check avant le grand saut.


Conclusion

Ça y est, c'est en ligne.

Publication du package Flutter sur pub.devPublication du package Flutter sur pub.dev

Voir son package publié sur pub.dev procure une satisfaction particulière. C'est un mélange de fierté et d'humilité.

Je sais que ce n'est qu'une V1 et il y aura peut-être des cas particuliers que je n'ai pas couverts. Mais ça fais partie du jeu. Je suis prêt à recevoir les retours et les Issues sur GitHub.

"Si vous développez des PWA avec Flutter et que vous rencontrez des difficultés avec l'installation ou les navigateurs In-App, allez jeter un œil à pwa_installer. J'espère qu'elle vous fera gagner autant de temps qu'à moi."

Happy coding! 🚀