Flutter SK/CZ – #1.3 – WebView – Navigačné tlačítka, Javascript komunikácia

Úvod

Vitajte na stránke Himdeve development, kde pre Vás pripravujeme tie najlepšie tutoriály, ktoré Vám uľahčia a zefektívnia vývoj mobilných aplikácií.

Cieľ

  1. Pridáme navigačné tlačítka – ísť dopredu a dozadu v histórii prehliadania stránok a refresh tlačítko na znovu načítanie stránky.
  2. Okrem toho sa naučíme ako priamo komunikovať s javascriptom stránky načítanej vo WebView, ako písať a volať javascript pre WebView vo Flutteri.

Postup

Najskôr otvoríme našu existujúcu aplikáciu z predchádzajúceho tutoriálu a otvoríme súbor shop_page.dart.

Do horného baru našej existujúcej aplikácie chceme pridať 3 tlačítka:

  1. Ísť v histórii prehliadania dozadu
  2. Refresh tlačítko
  3. Ísť v histórii prehliadania dopredu

Prejdeme teda k časti kódu, kde vytvárame Scaffold widget, konkrétne jeho horný bar – AppBar.

Pridáme sem nový atribút actions, do ktorého vložíme navigačné tlačítka. Kedže všetko vo Flutteri je widget, tak navigačné tlačítka môžeme vytvoriť v novej triede NavigationControls, ktorá bude dediť zo Stateless Widgetu a inštanciu tejto triedy vložíme ako hodnotu atribútu actions. Parametrom tejto triedy bude Future objekt WebViewControllera.

Copy to Clipboard

Následne v našej štruktúre projektu, pod prezentačnou vrstvou vytvoríme priečinok components a v ňom nový súbor navigation_controls.dart.

flutter sk #1.3 project structure

Navigačné tlačítka

Otvoríme novo vytvorený súbor navigation_controls.dart.

A vytvoríme v ňom triedu NavigationControls. Konštruktor tejto triedy bude mať argument Future objekt WebViewControllera.

Copy to Clipboard

Následne v build metóde Stateless Widgetu vytvoríme FutureBuilder widget takisto ako v predchádzajúcom tutoriále, kde future bude náš vstupný parameter pre túto triedu. Future tvori info o tom, či je WebView Controller validný.

Copy to Clipboard

Následne ak WebView Controller je validný, čiže controller.hasData je pravda, tak v build metóde vrátime Row widget. Row widget je Flutter komponent, ktorý zobrazuje svojich potomkov, čiže ďalšie widgety v tejto hierarchii widgetov, v horizontálnom poli. 

Čo je tu dobré si uvedomiť je, že Row widget nie je skrolovateľný. Dokonca mať viac widget – potomkov v Row widgete ako je voľný priestor pre tento widget, je všeobecne považované za error. V takom prípade je dobré použiť Flutter ListView widget a nastaviť mu smer skrolovania horizontálne pomocou ListView atribútu scrollDirection (scrollDirection: Axis.horizontal).

Náš Row widget bude mať 3 ďalšie widgety vo svojej hierarchii widgetov, ktoré budú reprezentovať 3 navigačné tlačítka spomenuté v prvom cieli tohto tutoriálu.

Copy to Clipboard

Ísť v histórii prehliadania dozadu

Prvé tlačítko v našom navigačnom poli bude: ísť v histórii prehliadania dozadu. Na to vytvoríme metódu _buildHistoryBackBtn, ktorá vráti FlatButton widget. Táto metóda má parametre BuildContext a Snapshot WebViewControllera

V tomto prípade chceme aby toto tlačítko bolo tvorené Ikonou reprezentujúcou šípku späť a zároveň textom ‘back’. A preto do child atribútu tohto tlačítka pridáme znovu horizontálny widget Row, kde vložíme danú ikonu a text.

Na onPressed metódu tohto tlačítka, čiže na interakciu kliknutia na toto tlačítko, sa spýtame WebViewControllera či má dáta na to aby mohol ísť v histórii prehliadania dozadu.

Copy to Clipboard

A ak má, tak zavoláme metódu WebViewControllera:

Copy to Clipboard

Ak nemá, tak zobrazíme Snackbar s informáciou, že nie je žiadna história prehliadania smerom dozadu.

Znovu si treba všimnúť, že metódu WebViewControllera controller.data.canGoBack() je potrebné awaitnuť, čiže počkať, dokým sa vyrieši Future hodnota, ktorú toto volanie vracia.

Copy to Clipboard

Ísť v histórii prehliadania dopredu

Táto metóda vyzerá takmer rovnako ako metóda reprezentujúca ísť v histórii dozadu. Akurát sa zmení text a ikona daného tlačítka ako aj info, že nie je žiadna história prehliadania smerom dopredu. A teda použijú sa metódy WebViewControlleracanGoForward() a goForward(). Názov tejto metódy je _buildHistoryForwardBtn.

Copy to Clipboard

Refresh tlačítko

Pre refresh tlačítko vytvoríme metódu _buildReloadBtn, ktorá bude mať len jeden vstupný parameter a tým je SnapShot WebViewControllera

Táto metóda vráti IconButton widget, ktorý ako názov naznačuje, zobrazuje len ikonu daného tlačítka. A na jeho kliknutie sa zavolá callback metóda onPressed, kde znovu načítame stránku vo WebView pomocou metódy WebViewControllera:

Copy to Clipboard

Následne sa vrátime do shop_page.dart súboru a importneme triedu NavigationControls.

Navigation Delegate

V triede _ShopPageState, v metóde _buildWebView pre widget WebView nastavíme atribút navigationDelegate, ktorý vracia callback metódu reprezentujúcu navigačnú požiadavku zmeny webovej stránky vo WebView

Na vykonanie tejto požiadavky vytvoríme funkciu _buildNavigationDecision, ktorá reprezentuje ukážku toho, ako je možné selektovať požiadavky na zmenu webovej stránky priamo vo Flutteri.

Copy to Clipboard

V našom prípade uvedieme príklad, keď užívateľ v našom eshope klikne na podstránku /my-account, kde my túto požiadavku vo Flutteri zakážeme s tým, že zobrazíme Snackbar s informáciou o tom, že užívateľ nemá práva na zobrazenie tejto podstránky.

Copy to Clipboard

Global Key

Čo je tu zaujímavé si všimnúť je, že zobrazenie SnackBaru nie je možné spraviť klasicky cez Scaffold.of(context).showSnackBar, ktorý by našiel najbližšieho predka Scaffoldu v tejto hierarchii widgetov. Pretože dostaneme exception (chybu), že Scaffold.of bol zavolaný s contextom, ktorý neobsahuje Scaffold. A to je z dôvodu, že sa snažíme použiť context widgetu, ktorý vytvoril inštanciu Scaffoldu, a nie jeho potomka. Scaffold.of(context) funguje len vtedy, ak máme context widget-potomka v tejto hierarchii widgetov pod Scaffoldom.

A preto použijeme iný prístup a to zadefinovaním globálneho kľúča (GlobalKey) pre Scaffold. A následne môžeme použiť príkaz globalKey.currentState.showSnackBar, ktorým zobrazíme želateľný SnackBar.

Copy to Clipboard

Javascript komunikácia

Na to aby sme ilustrovali ako funguje komunikácia medzi javascriptom stránky načítanej vo WebView a Flutterom, tak do WebView widgetu zadefinujeme ďalší atribút onPageFinished. Je to znovu WebView callback metóda, ktorá sa zavolá vtedy, keď je stránka vo WebView kompletne načítaná.

Copy to Clipboard

A našim cieľom bude následne prepísať nadpis v hornom bare našej aplikácie podľa toho aký je názov stránky, ktorú sme práve načítali. 

Túto informáciu zistíme pomocou javascript príkazu:

Copy to Clipboard

Čiže našou úlohou je zavolať tento javascript príkaz pomocou kódu vo Flutteri a následne zareagovať na výsledok získaného názvu aktuálnej zobrazenej stránky vo Flutteri a použiť ho pre horný bar našej obrazovky.

Vytvoríme si metódu _showPageTitle, ktorú zavoláme, keď je stránka vo WebView kompletne načítaná. A tu zavoláme metódu WebViewControllera webViewController.evaluateJavascript, ktorá vyhodnotí výraz jazyka Javascript. V našom prípade document.title.

A aby sme mohli tento výsledok, čiže ten názov stránky spracovať vo Flutteri, tak musíme použiť triedu z WebView packagu s názvom JavascriptChannel.

Predtým ako sa vrhneme na JavascriptChannel, poďme sa najskôr pozrieť ako sme vôbec dostali WebViewController v metóde _showPageTitle.

Future – Then metóda

Náš zakapsulovaný WebViewController do Completera musíme najskôr vyhodnotiť aby sme získali priamo hodnotu WebViewControllera. Doteraz sme to robili pomocou FutureBuilderu. Avšak v tomto prípade je to zbytočné, keďže nejdeme budovať ďalšie UI widgety v tejto metóde. Takže môžeme použiť ďalší spôsob ako získať hodnotu WebViewControllera a to použitím metódy Then, čo je callback Future objektu a zavolá sa vtedy, keď je vyhodnotená Future daného objektu.

Copy to Clipboard

Následne celá metóda _showPageTitle vyzerá takto:

Copy to Clipboard

JavascriptChannel

Takže aby sme poslali info o tom aký má stránka názov, získaný pomocou javascriptu vyhodnoteného priamo na aktuálnej stránke zobrazenej vo Webview, použijeme JavascriptChannel.

Na komunikáciu z javascriptu sa použije JavascriptChannel ako komunikačný kanál, ktorý posiela správy pomocou metódy JavascriptChannel.postMessage. A tieto správy sú následne prijímané callback metódou JavascriptChannelu onMessageReceived.

Vytvoríme si teda metódu na vytvorenie JavascriptChannelu a nazveme ju _createTopBarJsChannel.

Trieda JavascriptChannel má 2 atribúty: name, onMessageReceived.

Atribút name nastavíme na hodnotu ‘TopBarJsChannel’ a tento atribút reprezentuje názov JavascriptChannelu, ktorý následne používame na posielanie správ v metóde evaluateJavascript WebViewControllera.

Copy to Clipboard

Atribút onMessageReceived reprezentuje teda callback metódu JavascriptChannelu, ktorá vracia správu poslanú z javascriptu.

Copy to Clipboard

Textová hodnota tejto JavascriptMessage sa zavolá pomocou príkazu: message.message.

V našom prípade chceme vložiť tento názov do AppBaru nášho Scaffold widgetu. A na to zavoláme setState metódu, ktorá slúži na znovu zavolanie build metódy a nastavíme premennú _title na nový názov získaný z javascript príkazu document.title. Tento názov ešte upravíme podľa nášho konkrétneho príkladu, kde document.title vracia moc dlhý názov a nám stačí len jeho substring do znaku pomlčky ‘’.

Copy to Clipboard

Záver

A tým je naša tretia časť tejto prvej série Flutter SK/CZ Tutoriálov ukončená a kompletný zdrojový kód môžete samozrejme nájsť na githube.