Construirea unui ecran de conectare interactiv cu Flare & Flutter

Echipa noastră de la 2Dimensions a întâlnit recent interacțiunea formularului de conectare Remembear: ne-am gândit că acesta este un exemplu perfect pe care îl putem construi în Flare și să împărtășim comunitatea!

Codul sursă este disponibil pe GitHub, iar fișierul Flare poate fi găsit pe 2Dimensiuni.

Prezentare generală

În primul rând, trebuie să importăm biblioteca flare_flutter în pubspec.yaml (N.B. Folosim o cale relativă din moment ce ne aflăm în repo-ul bibliotecii, dar pachetul este disponibil și pe DartPub). De asemenea, am adăugat folderul de active la pubspec.yaml, astfel încât conținutul acestuia să fie accesibil în Flutter.

Fișierele relevante sunt toate în folderul / lib, în ​​timp ce fișierul Flare se află în folderul active:

/ lib
  - input_helper.dart
  - principal.dart
  - signin_button.dart
  - teddy_controller.dart
  - tracking_text_input.dart
/ active
  - Teddy.flr

Cum funcționează acest lucru

Aruncăm mai întâi o privire la Teddy în Flare: acest personaj are un nod numit ctrl_face, care este ținta pentru constrângerea traducerii elementelor feței. Aceasta înseamnă că mișcarea nodului îi va determina și pe toți dependenții să se miște.

Prinzând referința la nodul ctrl_face, putem muta fața lui Teddy și regla direcția privirii sale. Va trebui doar să găsim poziția câmpului de text sub Teddy și să reglăm poziția nodului ctrl_face în consecință.

În Cod

În main.dart, MyHomePage creează aspectul aplicației.
Folosim widget-ul FlareActor din biblioteca flare_flutter pentru a plasa animația în vizualizare:

[...]
FlareActor (
  "Active / Teddy.flr",
  // Legați un FlareController
  controler: _teddyController
  [...]
)

Deoarece dorim să manipulăm poziția nodului ctrl_face, legăm _teddyController la FlareActor-ul nostru. Un controler este o implementare concretă a FlareController, o interfață oferită de flare_flutter și ne oferă posibilitatea de a interoga și de a manipula ierarhia Flare.

Controale personalizate

Să aruncăm o privire la clasa TeddyController: veți observa că TeddyController extinde FlareControls și nu FlareController!
FlareControls este o implementare concretă a FlareController pe care flare_flutter o furnizează deja și are o funcționalitate de joc / mix de bază.

TeddyController are câteva câmpuri:

// Matrice pentru transformarea coordonatelor globale Flutter
// în coordonatele lumii Flare.
Mat2D _globalToFlareWorld = Mat2D ();
// O referință la nodul „ctrl_look`.
ActorNode _faceControl;
// Stocați originea nodului în spațiile de transformare locale și mondiale.
Vec2D _faceOrigin = Vec2D ();
Vec2D _faceOriginLocal = Vec2D ();
// Întrețineți în coordonatele globale Flutter și în coordonatele lumii Flare.
Vec2D _caretGlobal = Vec2D ();
Vec2D _caretWorld = Vec2D ()

Această clasă va trebui apoi să înlocuiască trei metode: inițializare (), avansare () și setViewTransform ().
inițializează () se numește - ai ghicit! - la momentul inițializării, când este construit widgetul FlareActor. Aici este trimisă prima dată referința la nod, din nou cu un apel de bibliotecă:

_faceControl = artboard.getNode ("ctrl_face");
if (_faceControl! = null) {
  _faceControl.getWorldTranslation (_faceOrigin);
  Vec2D.copy (_faceOriginLocal, _faceControl.translation);
}
juca ( "inactiv");

Plăcile de artă din Flare sunt containere de nivel superior pentru noduri, forme și animații. artboard.getNode (String name) returnează referința ActorNode cu numele dat.

După ce am stocat referința nodului, salvăm și traducerea inițială, astfel încât o putem restaura atunci când câmpul text pierde focalizarea și vom începe să redăm animația inactivă.

Celelalte două modificări sunt denumite fiecare cadru: setViewTransform () este folosit aici pentru a construi _globalToFlareWorld - aceasta este matricea pentru a transforma coordonatele globale ale ecranului Flutter în coordonatele lumii Flare.

Metoda avans () este în cazul în care toate cele de mai sus se reunesc!
Când utilizatorul începe să tasteze, TrackingTextInput va transmite poziția ecranului îngrijitului în _caretGlobal. Cu această coordonată, controlerul poate calcula noua poziție a ctrl_face, schimbându-și astfel privirea.

// Proiectul privește înainte cu mulți pixeli.
static const double _projectGaze = 60.0;
[...]
// Obțineți grijă în spațiul lumii Flare.
Vec2D.transformMat2D (
  _caretWorld, _caretGlobal, _globalToFlareWorld);
[...]
// Calculați vectorul de direcție.
Vec2D toCaret = Vec2D.subtract (Vec2D (), _caretWorld, _faceOrigin);
Vec2D.normalize (toCaret, toCaret);
// Scalați direcția cu o valoare constantă.
Vec2D.scale (toCaret, toCaret, _projectGaze);
// Calculați transformarea care ne primește în spațiul față ctrl_face.
Mat2D toFaceTransform = Mat2D ();
if (Mat2D.invert (toFaceTransform,
        _faceControl.parent.worldTransform)) {
  // Puneți toCaret în spațiul local.
  // N.B. folosim un vector de direcție, nu o traducere,
  // folosiți transformMat2 () pentru a transforma fără traducere
  Vec2D.transformMat2 (toCaret, toCaret, toFaceTransform);
  // Poziția finală ctrl_face este traducerea originală a feței
  // plus acest vector de direcție
  targetTranslation = Vec2D.add (Vec2D (), toCaret, _faceOriginLocal);
}

Întrucât o imagine valorează o mie de cuvinte - sau în acest caz, linii de cod - mai jos putem vedea cum se calculează direcția: vectorul diferenței este stocat în toCaret.

Întrucât aceasta este o direcție, aceasta este normalizată și apoi mărită de numărul de pixeli pe care ar trebui să-l proiecteze privirea din poziția inițială.

În cele din urmă, transformăm înCaret în spațiul propriu al nodului, astfel încât să-l putem adăuga la traducerea originală a nodului.

Poziția Caret

Ultima piesă a puzzle-ului este modul de calculare a poziției ecranului îngrijitorului.

Acest lucru se face în widgetul TrackingTextInput. Acest widget stochează o referință la un GlobalKey pentru a-și construi TextFormFields. Prin această cheie, Flutter ne permite să obținem RenderObject care cuprinde acest TextFormField:

RenderObject fieldBox = _fieldKey.currentContext.findRenderObject ();

Cu cele trei funcții helper disponibile în lib / input_helper.dart, putem folosi RenderBox pentru a calcula poziția reală de îngrijire în coordonatele ecranului, parcurgând ierarhia widget-ului din acel RenderBox și căutând un RenderEditable. Această clasă Flutter oferă metoda getEndpointsForSelection () folosită pentru calcularea coordonatelor locale, care poate fi transformată în coordonate globale prin originalRenderBox.

Si asta e!

Încă o dată, asigurați-vă că verificați sursele de pe GitHub și Flare și veniți alături de noi la 2Dimensions.com!