Изграждане на интерактивен екран за вход с Flare & Flutter

Екипът ни в 2Dimensions наскоро се натъкна на взаимодействие с формата за вход Remembear: решихме, че това е перфектен пример, който можем да изградим във Flare и да споделим с общността!

Изходният код е достъпен в GitHub, а файлът Flare може да бъде намерен в 2Dimensions.

Преглед

Първо, трябва да импортираме библиотеката flare_flutter в pubspec.yaml (N.B. Използваме относителен път, тъй като сме в репото на библиотеката, но пакетът е достъпен и в DartPub). Също така добавихме папката с активи към pubspec.yaml, така че съдържанието й да е достъпно във Flutter.

Всички файлове са в папката / lib, докато файлът Flare е в папката с активи:

/ ИЪ
  - input_helper.dart
  - main.dart
  - signin_button.dart
  - teddy_controller.dart
  - track_text_input.dart
/ активи
  - Teddy.flr

Как работи това

Нека първо да разгледаме Теди в Flare: този герой има възел, наречен ctrl_face, който е Целта за ограничаване на превода на елементите на лицето. Това означава, че преместването на възела ще доведе и до преместване на всички негови зависими.

Като вземем референцията към възела ctrl_face, можем да преместим лицето на Теди и да регулираме посоката на погледа му. Просто ще трябва да намерим позицията на текстовото поле под Теди и да коригираме съответно позицията на възела ctrl_face.

В кодекса

В main.dart, MyHomePage изгражда оформлението за приложението.
Използваме джаджата FlareActor от библиотеката flare_flutter, за да поставим анимацията в изгледа:

[...]
FlareActor (
  "Активи / Teddy.flr",
  // Завържете FlareController
  контролер: _teddyController
  [...]
)

Тъй като искаме да манипулираме позицията на възела ctrl_face, ние обвързваме _teddyController с нашия FlareActor. Контролерът е конкретна реализация на FlareController, интерфейс, предоставен от flare_flutter, и ни дава възможност да питаме и манипулираме йерархията на Flare.

Персонализирани контроли

Нека да разгледаме класа наTeddyController: ще забележите, че TeddyController разширява FlaveControls, а не FlareController!
FlareControls е конкретна реализация на FlareController, която flare_flutter вече осигурява и има някои основни функции за игра / микс.

TeddyController има няколко полета:

// Матрица за трансформиране на Flutter глобални координати
// в координатите на света Flare.
Mat2D _globalToFlareWorld = Mat2D ();
// Препратка към възела `ctrl_look`
ActorNode _faceControl;
// Съхраняване на произхода на възела в световни и местни трансформационни пространства.
Vec2D _faceOrigin = Vec2D ();
Vec2D _faceOriginLocal = Vec2D ();
// Внимавайте в глобални координати на Flutter и в световни координати Flare.
Vec2D _caretGlobal = Vec2D ();
Vec2D _caretWorld = Vec2D ()

След това този клас ще трябва да отмени три метода: инициализиране (), предварително () и setViewTransform ().
се инициализира () се нарича - познахте! - по време на инициализация, когато е изградена джаджата FlareActor. Тук първо се извлича нашата препратка към възел, отново с повикване от библиотеката:

_faceControl = artboard.getNode ("ctrl_face");
ако (_faceControl! = null) {
  _faceControl.getWorldTranslation (_faceOrigin);
  Vec2D.copy (_faceOriginLocal, _faceControl.translation);
}
играе ( "празен ход");

Artboards in Flare са контейнери от най-високо ниво за възли, форми и анимации. artboard.getNode (String name) връща референцията на ActorNode с даденото име.

След като запазим референтната точка на възела, ние също запазваме оригиналния му превод, така че можем да го възстановим, когато текстовото поле изгуби фокуса, и започваме да играем на празен анимация.

Другите две отменяния се наричат ​​всеки кадър: setViewTransform () се използва тук за изграждане на _globalToFlareWorld - това е матрицата за преобразуване на глобални координати на Flutter екрана в координати на Flare world.

Методът аванс () е, когато всичко по-горе се събере!
Когато потребителят започне да пише, TrackingTextInput ще пренесе позицията на екрана на каретата в _caretGlobal. С тази координата контролерът може да изчисли новото положение на ctrl_face, като по този начин измести погледа си.

// Проектирайте поглед напред от тези много пиксели.
статичен const двоен _projectGaze = 60.0;
[...]
// Вземете каре в световното пространство на Flare.
Vec2D.transformMat2D (
  _caretWorld, _caretGlobal, _globalToFlareWorld);
[...]
// Изчислете вектор на посоката.
Vec2D toCaret = Vec2D.извлечение (Vec2D (), _caretWorld, _faceOrigin);
Vec2D.normalize (toCaret, toCaret);
// Мащабиране на посоката с постоянна стойност.
Vec2D.scale (toCaret, toCaret, _projectGaze);
// Изчислете трансформацията, която ни получава в лицето ctrl_face пространство.
Mat2D toFaceTransform = Mat2D ();
ако (Mat2D.invert (toFaceTransform,
        _faceControl.parent.worldTransform)) {
  // Поставете toCaret в локално пространство.
  // Н.Б. използваме вектор за посока, а не превод,
  // затова използвайте transformMat2 (), за да трансформирате без превод
  Vec2D.transformMat2 (toCaret, toCaret, toFaceTransform);
  // Крайната позиция ctrl_face е оригиналният превод на лицето
  // плюс този вектор на посоката
  targetTranslation = Vec2D.add (Vec2D (), toCaret, _faceOriginLocal);
}

Тъй като една снимка струва хиляда думи - или в този случай редове от код - по-долу можем да видим как се изчислява посоката: разликата вектор се съхранява в toCaret.

Тъй като това е посока, тя се нормализира и след това се увеличава с броя пиксели, които погледът трябва да изпъква от първоначалната си позиция.

И накрая, ние трансформираме toCaret в собственото пространство на възела, така че да можем да го добавим към оригиналния превод на възела.

Положение за грижи

Последното парче от пъзела е как да се изчисли позицията на екрана на каретата.

Това се прави в приспособлението TrackingTextInput. Тази джаджа съхранява препратка към GlobalKey, за да изгради своите TextFormFields. Чрез този ключ Flutter ни позволява да получим RenderObject, който обхваща това TextFormField:

RenderObject fieldBox = _fieldKey.currentContext.findRenderObject ();

С трите помощни функции, налични в lib / input_helper.dart, можем да използваме RenderBox за изчисляване на действителната позиция на карета в координатите на екрана, като пресичаме йерархията на приспособленията от този RenderBox и търсим RenderEditable. Този Flutter клас предоставя метод getEndpointsForSelection (), който се използва за изчисляване на локални координати, които могат да бъдат трансформирани в глобални координати от originalRenderBox.

И това е!

Още веднъж, не забравяйте да разгледате източниците на GitHub и Flare и се присъединете към нас на 2Dimensions.com!