Opbygning af en interaktiv login-skærm med Flare & Flutter

Vores team hos 2Dimensions kom for nylig over Remembear-loginformularinteraktionen: Vi troede, at dette var et perfekt eksempel, vi kunne bygge i Flare og dele med samfundet!

Kildekoden er tilgængelig på GitHub, og Flare-filen kan findes på 2Dimensions.

Oversigt

Først skal vi importere flare_flutter-biblioteket i pubspec.yaml (N.B. Vi bruger en relativ sti, da vi er i bibliotekets repo, men pakken er også tilgængelig på DartPub). Vi føjede også aktivmappen til pubspec.yaml, så dens indhold er tilgængeligt i Flutter.

De relevante filer er alle i mappen / lib, mens Flare-filen er i aktivmappen:

/ lib
  - input_helper.dart
  - main.dart
  - signin_button.dart
  - teddy_controller.dart
  - tracking_text_input.dart
/ aktiver
  - Teddy.flr

Sådan fungerer det

Lad os først kigge på Teddy i Flare: denne karakter har en node, der hedder ctrl_face, som er målet for oversættelsesbegrænsningen af ​​ansigtselementerne. Dette betyder, at flytning af noden også får alle de afhængige til at bevæge sig.

Ved at gribe henvisningen til ctrl_face-knuden kan vi flytte Teddy's ansigt og justere retningen på hans blik. Vi bliver bare nødt til at finde placeringen af ​​tekstfeltet under Teddy og justere ctrl_face node's position i overensstemmelse hermed.

Ind i koden

I main.dart bygger MyHomePage layoutet til appen.
Vi bruger FlareActor-widget fra flare_flutter-biblioteket til at placere animationen i visningen:

[...]
FlareActor (
  "Aktiver / Teddy.flr",
  // Bind en FlareController
  controller: _teddyController
  [...]
)

Da vi ønsker at manipulere placeringen af ​​ctrl_face-knuden, binder vi _teddyController til vores FlareActor. En controller er en konkret implementering af FlareController, et interface leveret af flare_flutter, og det giver os muligheden for at forespørge og manipulere Flare-hierarkiet.

Brugerdefinerede kontroller

Lad os se på klassen TeddyController: du vil bemærke, at TeddyController udviderFlareControls og ikke FlareController!
FlareControls er en konkret implementering af FlareController, som flare_flutter allerede leverer, og det har en vis grundlæggende play / mix-funktionalitet.

TeddyController har et par felter:

// Matrix til omdannelse af Flutter globale koordinater
// i Flare verdens koordinater.
Mat2D _globalToFlareWorld = Mat2D ();
// En henvisning til noden 'ctrl_look'.
ActorNode _faceControl;
// Opbevar nodenes oprindelse i verdens- og lokale transformeringsrum.
Vec2D _faceOrigin = Vec2D ();
Vec2D _faceOriginLocal = Vec2D ();
// Caret i globale Flutter-koordinater og i Flare World-koordinater.
Vec2D _caretGlobal = Vec2D ();
Vec2D _caretWorld = Vec2D ()

Denne klasse skal derefter tilsidesætte tre metoder: initialisere (), forskud () og setViewTransform ().
initialisere () kaldes - du gættede det! - på initialiseringstidspunktet, når FlareActor-widget'en er bygget. Det er her vores nodehenvisning hentes først, igen med et biblioteksopkald:

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

Artboards i Flare er de øverste containere til knudepunkter, figurer og animationer. artboard.getNode (strengnavn) returnerer ActorNode-referencen med det givne navn.

Efter at have gemt nodens reference, gemmer vi også dens originale oversættelse, så vi kan gendanne den, når tekstfeltet mister fokus, og vi begynder at spille den inaktive animation.

De to andre overstyringer kaldes hver ramme: setViewTransform () bruges her til at opbygge _globalToFlareWorld - det er matrixen til at omdanne globale Flutter-skærmkoordinater til Flare-verdens koordinater.

Fremgangsmåden () -metoden er, hvor alt ovenstående samles!
Når brugeren begynder at skrive, vil TrackingTextInput videresende skærmens placering af caret til _caretGlobal. Med denne koordinat kan controlleren beregne den nye position på ctrl_face og således skifte blik.

// Projekt blik fremad af disse mange pixels.
statisk const dobbelt _projectGaze = 60,0;
[...]
// Få caret i Flare verdensrum.
Vec2D.transformMat2D (
  _caretWorld, _caretGlobal, _globalToFlareWorld);
[...]
// Beregn retning vektor.
Vec2D toCaret = Vec2D.subtract (Vec2D (), _caretWorld, _faceOrigin);
Vec2D.normalize (toCaret, toCaret);
// Skala retningen med en konstant værdi.
Vec2D.scale (toCaret, toCaret, _projectGaze);
// Beregn den transformation, der får os i ansigtets ctrl_face-rum.
Mat2D tilFaceTransform = Mat2D ();
if (Mat2D.invert (til FacebookTransform,
        _faceControl.parent.worldTransform)) {
  // Sæt toCaret i det lokale rum.
  // N.B. vi bruger en retningsvektor, ikke en oversættelse,
  // så brug transformMat2 () til at transformere uden oversættelse
  Vec2D.transformMat2 (toCaret, toCaret, toFaceTransform);
  // Den endelige position ctrl_face er den originale ansigtsoversættelse
  // plus denne retningsvektor
  targetTranslation = Vec2D.add (Vec2D (), toCaret, _faceOriginLocal);
}

Da et billede er værd tusind ord - eller i dette tilfælde kodelinjer - nedenfor kan vi se, hvordan retningen beregnes: forskelvektoren gemmes i toCaret.

Da dette er en retning, normaliseres den og skaleres derefter op med antallet af pixels, som blikket skal projicere fra sin oprindelige position.

Til sidst omdanner vi toCaret til nodens eget rum, så vi kan føje det til nodens originale oversættelse.

Caret Position

Det sidste stykke i puslespillet er, hvordan man beregner skærmens placering af caret.

Dette gøres i widgeten TrackingTextInput. Denne widget gemmer en henvisning til en GlobalKey for at opbygge dens TextFormFields. Gennem denne nøgle giver Flutter os mulighed for at få RenderObject, der omfatter denne TextFormField:

RenderObject fieldBox = _fieldKey.currentContext.findRenderObject ();

Med de tre hjælperfunktioner, der er tilgængelige i lib / input_helper.dart, kan vi bruge RenderBox til at beregne den faktiske caret-position i skærmkoordinater ved at krydse widgethierarkiet fra denne RenderBox og se efter en RenderEditable. Denne Flutter Class giver metoden getEndpointsForSelection (), der bruges til at beregne lokale koordinater, som kan omdannes til globale koordinater af originalRenderBox.

Og det er det!

Igen, sørg for at tjekke kilderne på GitHub og Flare, og kom sammen med os på 2Dimensions.com!