Building an Interactive Login Screen with Flare & Flutter

Umberto Sonnino
Jan 10, 2019 · 5 min read
Image for post
Image for post

Overview

First, we need to import the flare_flutter library in pubspec.yaml (N.B. We use a relative path since we’re in the library’s repo, but the package is also available on DartPub). We also added the assets folder to the pubspec.yaml so that its contents are accessible in Flutter.

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

How This Works

Image for post
Image for post

Into The Code

In main.dart, MyHomePage builds the layout for the app.
We use the FlareActor widget from the flare_flutter library to place the animation in the view:

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

Custom Controls

Let’s take a look at theTeddyController class: you’ll notice that TeddyController extendsFlareControls and not FlareController!
FlareControls is a concrete implementation of FlareController that flare_flutter already provides, and it has some basic play/mix functionality.

// Matrix to transform Flutter global coordinates
//
into Flare world coordinates.
Mat2D _globalToFlareWorld = Mat2D();
// A reference to the `ctrl_look` node.
ActorNode _faceControl;
// Store the node's origin in world and local transform spaces.
Vec2D _faceOrigin = Vec2D();
Vec2D _faceOriginLocal = Vec2D();
// Caret in global Flutter coordinates, and in Flare world coordinates.
Vec2D _caretGlobal = Vec2D();
Vec2D _caretWorld = Vec2D()
_faceControl = artboard.getNode("ctrl_face");
if (_faceControl != null) {
_faceControl.getWorldTranslation(_faceOrigin);
Vec2D.copy(_faceOriginLocal, _faceControl.translation);
}
play("idle");
// Project gaze forward by this many pixels.  
static const double _projectGaze = 60.0;
[...]
// Get caret in Flare world space.
Vec2D.transformMat2D(
_caretWorld, _caretGlobal, _globalToFlareWorld);
[...]// Compute direction vector.
Vec2D toCaret = Vec2D.subtract(Vec2D(), _caretWorld, _faceOrigin);
Vec2D.normalize(toCaret, toCaret);
// Scale the direction with a constant value.
Vec2D.scale(toCaret, toCaret, _projectGaze);
// Compute the transform that gets us in face ctrl_face space.
Mat2D toFaceTransform = Mat2D();
if (Mat2D.invert(toFaceTransform,
_faceControl.parent.worldTransform)) {
// Put toCaret in local space.
// N.B. we're using a direction vector, not a translation,
// so use
transformMat2() to transform without translation
Vec2D.transformMat2(toCaret, toCaret, toFaceTransform);
// The final ctrl_face position is the original face translation
// plus this direction vector
targetTranslation = Vec2D.add(Vec2D(), toCaret, _faceOriginLocal);
}
Image for post
Image for post
Image for post
Image for post

Caret Position

The last piece of the puzzle is how to compute the screen position of the caret.

RenderObject fieldBox = _fieldKey.currentContext.findRenderObject();

Rive

News, tips, and insights on our real-time animation tools.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store