Skip to content
This repository has been archived by the owner on Feb 16, 2024. It is now read-only.

Commit

Permalink
Drop Flare Dependency (#375)
Browse files Browse the repository at this point in the history
* Reconstructs the settings icon on the home page.
* Drops the dependency on Flare from the Gallery.
* Updates goldens.
  • Loading branch information
pennzht committed Nov 16, 2020
1 parent e764888 commit 92b31c4
Show file tree
Hide file tree
Showing 10 changed files with 326 additions and 83 deletions.
2 changes: 0 additions & 2 deletions golden_test/flutter_test_config.dart
Expand Up @@ -4,7 +4,6 @@

import 'dart:async';

import 'package:flare_flutter/flare_testing.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';

Expand All @@ -21,7 +20,6 @@ Future<void> testExecutable(FutureOr<void> Function() testMain) async {
};

TestWidgetsFlutterBinding.ensureInitialized();
FlareTesting.setup();
// Disabling the warning because @visibleForTesting doesn't take the testing
// framework into account.
// ignore: invalid_use_of_visible_for_testing_member
Expand Down
Binary file modified golden_test/goldens/home_page_desktop_dark.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified golden_test/goldens/home_page_desktop_light.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified golden_test/goldens/home_page_mobile_dark.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified golden_test/goldens/home_page_mobile_light.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
78 changes: 17 additions & 61 deletions lib/pages/backdrop.dart
Expand Up @@ -2,10 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flare_dart/math/mat2d.dart';
import 'package:flare_flutter/flare.dart';
import 'package:flare_flutter/flare_actor.dart';
import 'package:flare_flutter/flare_controller.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
Expand All @@ -15,6 +11,7 @@ import 'package:flutter_gen/gen_l10n/gallery_localizations.dart';
import 'package:gallery/layout/adaptive.dart';
import 'package:gallery/pages/home.dart';
import 'package:gallery/pages/settings.dart';
import 'package:gallery/pages/settings_icon/icon.dart' as settings_icon;

const double _settingsButtonWidth = 64;
const double _settingsButtonHeightDesktop = 56;
Expand All @@ -33,24 +30,25 @@ class Backdrop extends StatefulWidget {
_BackdropState createState() => _BackdropState();
}

class _BackdropState extends State<Backdrop>
with SingleTickerProviderStateMixin, FlareController {
class _BackdropState extends State<Backdrop> with TickerProviderStateMixin {
AnimationController _settingsPanelController;
AnimationController _iconController;
FocusNode _settingsPageFocusNode;
ValueNotifier<bool> _isSettingsOpenNotifier;
Widget _settingsPage;
Widget _homePage;

FlutterActorArtboard _artboard;
FlareAnimationLayer _animationLayer;

@override
void initState() {
super.initState();
_settingsPanelController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 200),
);
_iconController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
);
_settingsPageFocusNode = FocusNode();
_isSettingsOpenNotifier = ValueNotifier(false);
_settingsPage = widget.settingsPage ??
Expand All @@ -63,52 +61,22 @@ class _BackdropState extends State<Backdrop>
@override
void dispose() {
_settingsPanelController.dispose();
_iconController.dispose();
_settingsPageFocusNode.dispose();
_isSettingsOpenNotifier.dispose();
super.dispose();
}

@override
void initialize(FlutterActorArtboard artboard) {
_artboard = artboard;
initAnimationLayer();
}

@override
void setViewTransform(Mat2D viewTransform) {
// This is a necessary override for the [FlareController] mixin.
}

@override
bool advance(FlutterActorArtboard artboard, double elapsed) {
if (_animationLayer != null) {
final layer = _animationLayer;
layer.time = _settingsPanelController.value * layer.duration;
layer.animation.apply(layer.time, _artboard, 1);
if (layer.isDone || layer.time == 0) {
_animationLayer = null;
}
}
return _animationLayer != null;
}

void initAnimationLayer() {
if (_artboard != null) {
final animationName = 'Animations';
final animation = _artboard.getAnimation(animationName);
_animationLayer = FlareAnimationLayer()
..name = animationName
..animation = animation;
}
}

void _toggleSettings() {
initAnimationLayer();
// Animate the settings panel to open or close.
_settingsPanelController.fling(
velocity: _isSettingsOpenNotifier.value ? -1 : 1);
if (_isSettingsOpenNotifier.value) {
_settingsPanelController.reverse();
_iconController.reverse();
} else {
_settingsPanelController.forward();
_iconController.forward();
}
_isSettingsOpenNotifier.value = !_isSettingsOpenNotifier.value;
isActive.value = true;
}

Animation<RelativeRect> _slideDownSettingsPageAnimation(
Expand Down Expand Up @@ -251,9 +219,8 @@ class _BackdropState extends State<Backdrop>
),
],
_SettingsIcon(
animationController: _settingsPanelController,
animationController: _iconController,
toggleSettings: _toggleSettings,
flareController: this,
isSettingsOpenNotifier: _isSettingsOpenNotifier,
),
],
Expand All @@ -272,13 +239,11 @@ class _BackdropState extends State<Backdrop>
class _SettingsIcon extends AnimatedWidget {
_SettingsIcon(
{this.animationController,
this.flareController,
this.toggleSettings,
this.isSettingsOpenNotifier})
: super(listenable: animationController);

final AnimationController animationController;
final FlareController flareController;
final VoidCallback toggleSettings;
final ValueNotifier<bool> isSettingsOpenNotifier;

Expand Down Expand Up @@ -324,16 +289,7 @@ class _SettingsIcon extends AnimatedWidget {
},
child: Padding(
padding: const EdgeInsetsDirectional.only(start: 3, end: 18),
child: FlareActor(
Theme.of(context).colorScheme.brightness == Brightness.light
? 'packages/flutter_gallery_assets/assets/icons/settings/settings_light.flr'
: 'packages/flutter_gallery_assets/assets/icons/settings/settings_dark.flr',
alignment: Directionality.of(context) == TextDirection.ltr
? Alignment.bottomLeft
: Alignment.bottomRight,
fit: BoxFit.contain,
controller: flareController,
),
child: settings_icon.SettingsIcon(animationController.value),
),
),
),
Expand Down
193 changes: 193 additions & 0 deletions lib/pages/settings_icon/icon.dart
@@ -0,0 +1,193 @@
// Copyright 2019 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:gallery/pages/settings_icon/metrics.dart';

class SettingsIcon extends StatelessWidget {
const SettingsIcon(this.time);

final double time;

@override
Widget build(BuildContext context) {
return CustomPaint(
painter: _SettingsIconPainter(time: time, context: context),
);
}
}

class _SettingsIconPainter extends CustomPainter {
_SettingsIconPainter({@required this.time, @required this.context});

final double time;
final BuildContext context;

Offset _center;
double _scaling;
Canvas _canvas;

/// Computes [_center] and [_scaling], parameters used to convert offsets
/// and lengths in relative units into logical pixels.
///
/// The icon is aligned to the bottom-start corner.
void _computeCenterAndScaling(Size size) {
_scaling = min(size.width / unitWidth, size.height / unitHeight);
_center = Directionality.of(context) == TextDirection.ltr
? Offset(
unitWidth * _scaling / 2, size.height - unitHeight * _scaling / 2)
: Offset(size.width - unitWidth * _scaling / 2,
size.height - unitHeight * _scaling / 2);
}

/// Transforms an offset in relative units into an offset in logical pixels.
Offset _transform(Offset offset) {
return _center + offset * _scaling;
}

/// Transforms a length in relative units into a dimension in logical pixels.
double _size(double length) {
return length * _scaling;
}

/// A rectangle with a fixed location, used to locate gradients.
Rect get _fixedRect {
final topLeft = Offset(-_size(stickLength / 2), -_size(stickWidth / 2));
final bottomRight = Offset(_size(stickLength / 2), _size(stickWidth / 2));
return Rect.fromPoints(topLeft, bottomRight);
}

/// Black or white paint, depending on brightness.
Paint get _monoPaint {
final monoColor =
Theme.of(context).colorScheme.brightness == Brightness.light
? Colors.black
: Colors.white;
return Paint()..color = monoColor;
}

/// Pink paint with horizontal gradient.
Paint get _pinkPaint {
const shader = LinearGradient(colors: [pinkLeft, pinkRight]);
final shaderRect = _fixedRect.translate(
_size(-(stickLength - colorLength(time)) / 2),
0,
);

return Paint()..shader = shader.createShader(shaderRect);
}

/// Teal paint with horizontal gradient.
Paint get _tealPaint {
const shader = LinearGradient(colors: [tealLeft, tealRight]);
final shaderRect = _fixedRect.translate(
_size((stickLength - colorLength(time)) / 2),
0,
);

return Paint()..shader = shader.createShader(shaderRect);
}

/// Paints a stadium-shaped stick.
void _paintStick({
@required Offset center,
@required double length,
@required double width,
double angle = 0,
@required Paint paint,
}) {
// Convert to pixels.
center = _transform(center);
length = _size(length);
width = _size(width);

// Paint.
width = min(width, length);
final stretch = length / 2;
final radius = width / 2;

_canvas.save();

_canvas.translate(center.dx, center.dy);
_canvas.rotate(angle);

final leftOval = Rect.fromCircle(
center: Offset(-stretch + radius, 0),
radius: radius,
);

final rightOval = Rect.fromCircle(
center: Offset(stretch - radius, 0),
radius: radius,
);

_canvas.drawPath(
Path()
..arcTo(leftOval, pi / 2, pi, false)
..arcTo(rightOval, -pi / 2, pi, false),
paint,
);

_canvas.restore();
}

@override
void paint(Canvas canvas, Size size) {
_computeCenterAndScaling(size);
_canvas = canvas;

if (isTransitionPhase(time)) {
_paintStick(
center: upperColorOffset(time),
length: colorLength(time),
width: stickWidth,
paint: _pinkPaint,
);

_paintStick(
center: lowerColorOffset(time),
length: colorLength(time),
width: stickWidth,
paint: _tealPaint,
);

_paintStick(
center: upperMonoOffset(time),
length: monoLength(time),
width: knobDiameter,
paint: _monoPaint,
);

_paintStick(
center: lowerMonoOffset(time),
length: monoLength(time),
width: knobDiameter,
paint: _monoPaint,
);
} else {
_paintStick(
center: upperKnobCenter,
length: stickLength,
width: knobDiameter,
angle: -knobRotation(time),
paint: _monoPaint,
);

_paintStick(
center: knobCenter(time),
length: stickLength,
width: knobDiameter,
angle: knobRotation(time),
paint: _monoPaint,
);
}
}

@override
bool shouldRepaint(CustomPainter oldDelegate) =>
oldDelegate is! _SettingsIconPainter ||
(oldDelegate as _SettingsIconPainter).time != time;
}

0 comments on commit 92b31c4

Please sign in to comment.