{"componentChunkName":"component---src-templates-lesson-post-js","path":"/lesson/custom-widget-peg","result":{"data":{"strapiLesson":{"id":"Lesson_84","author":{"email":"eric@ericwindmill.com","username":"Eric Windmill","twitter":"ericwindmill"},"content":"To start, build the `Peg` widget, which is mostly just a dumb widget that we'll create four of in our app. Each one will be animated separately. \n\nThis widget is built by manipulating a `Container` widget. It's a container widget that's designed with a specific size, color, border radius, and box shadow. \n\n```dart\nimport 'package:flutter/material.dart';\n\nclass Peg extends StatelessWidget {\n  final double marginLeft;\n  final double marginRight;\n\n  const Peg({Key key, this.marginLeft, this.marginRight}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    return new Container(\n      width: 35.0,\n      height: 15.0,\n      margin: new EdgeInsets.only(left: marginLeft, right: marginRight),\n      decoration: new BoxDecoration(\n        color: const Color.fromRGBO(0, 0, 255, 1.0),\n        borderRadius: new BorderRadius.circular(10.0),\n        boxShadow: [\n          new BoxShadow(\n            color: Colors.black12,\n            blurRadius: 8.0,\n            spreadRadius: 1.0,\n            offset: new Offset(1.0, 0.0),\n          ),\n          new BoxShadow(\n            color: Colors.black26,\n            blurRadius: 6.0,\n            spreadRadius: 1.5,\n            offset: new Offset(1.0, 0.0),\n          ),\n        ],\n      ),\n    );\n  }\n}\n```\n\nThe properties `Peg.marginLeft` and `Peg.marginRight` are used to position the pegs next to each other correctly. They will be discussed in more detail later in this tutorial.","updated_at":"Friday, 24th of July, 2020","slug":"custom-widget-peg","strapiId":84,"title":"Custom Widget: Peg","tutorial":{"category":"Flutter","title":"Custom Animation: Progress Indicator"}},"strapiTutorial":{"lessons":[{"author":1,"content":"The following code example will render the four pegs to the screen. They won't animate. In the remaining lessons, I will focus on animations.\n\n```dart\n// This example app is only one page\nvoid main() {\n  runApp(\n    MaterialApp(\n      home: Scaffold(\n        body: PegProgressIndicator(),\n      ),\n    ),\n  );\n}\n\n// This widget is the progress indicator container\nclass PegProgressIndicator extends StatefulWidget {\n  @override\n  _PegProgressIndicatorState createState() => _PegProgressIndicatorState();\n}\n\n// NOTE: If you look at the source code for this app, you'll notice that \n// all animation related code is omitted here. I will be added in as the lesson\n// progresses.\n\nclass _PegProgressIndicatorState extends State<PegProgressIndicator> {\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      child: Center(\n        child: Row(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: <Widget>[\n            Peg(),\n            Peg(),\n            Peg(),\n            Peg(),\n          ],\n        ),\n      ),\n    );\n  }\n}\n```\n\nOnce you have this code copy + pasted into a text editor, move on so we can talk about the meat of this example: animations.\n\n","created_at":"2020-07-24T17:01:01.352Z","id":83,"slug":"build-the-example-app-boiler-plate","title":"Build the example app boiler-plate","tutorial":9,"updated_at":"2020-07-24T17:01:01.364Z"},{"author":1,"content":"Go ahead and add this to your boiler plate. It's a small detour to see the most\nbasic animation in Flutter.\n\n```dart\n// You have to add this class mixin in order for flutter to know to treat it as\n// an animation containing widget\nclass _PegProgressIndicatorState extends State<PegProgressIndicator>\n    with SingleTickerProviderStateMixin {                             // new\n  AnimationController _controller;                                    // new\n  Animation<Color> animation;                                         // new\n\n  @override\n  initState() {\n    super.initState();\n    // Because this class has now mixed in a TickerProvider\n    // It can be its own vsync. This is what you need almost always\n    _controller = new AnimationController(\n      duration: const Duration(milliseconds: 3000),\n      vsync: this,\n    );\n    // A tween that begins at grey and ends at a green\n    // The chained 'animate' function is required\n    animation = new ColorTween(\n      begin: const Color.fromRGBO(10, 10, 10, 0.5),\n      end: const Color.fromRGBO(0, 200, 100, 0.5),\n    ).animate(_controller)\n    // This is a another chained method for Animations.\n    // It will call the callback passed to it everytime the\n    // value of the tween changes. Call setState within it\n    // to repaint the widget with the new value\n    ..addListener(() {\n      setState((){});\n    });\n    // Tell the animation to start\n    _controller.forward();\n  }\n\n  // This is important for perf. When the widget is gone, remove the controller.\n  @override\n  dispose() {\n    _controller?.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return new Container(\n       // This is where you pass the animation value\n       // Each time set state gets called,\n       // this widget gets rebuilt,\n       // and the value of the animation is something in-between\n       // the starting grey and the ending green\n       // thanks to our ColorTween\n      decoration: new BoxDecoration(color: animation.value),\n      child: new Center(\n        child: new Row(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: <Widget>[\n            Peg()\n            Peg()\n            Peg()\n            Peg()\n          ],\n        ),\n      ),\n    );\n  }\n}\n```\n\nDo a full refresh on your app. Look at the background change color.","created_at":"2020-07-24T17:41:14.728Z","id":86,"slug":"tween-by-example","title":"Tween by example","tutorial":9,"updated_at":"2020-07-24T17:41:14.736Z"},{"author":1,"content":"Another awesome built in Flutter feature is `AnimatedWidgets`. These provide\ntwo neat things:\n\n1. You don't have to use `addListener` and `setState` on your animations to\n   tell Flutter to rebuild. AnimatedWidgets have a different technique.\n2. There are some built in classes that extend `AnimatedWidget` and provide\n   some common 'transformations'.\n\nThis is the entire AnimatedWidget. That I built for this animation. Don't get\nbogged down in the common details(like margins): I highlighted the pieces that\nare important to understand.\n\n```dart\nclass PegAnimation extends AnimatedWidget {\n  // Animated Widgets need to be passed an animation,\n  // Or in this case, multiple animations.\n  final List<Animation<double>> animations;\n  // They also need the controller.\n  final Animation<double> controller;\n\n  // These are properties specific to this case.\n  final FractionalOffset alignment;\n  final bool isClockwise;\n  final double marginLeft;\n  final double marginRight;\n\n  PivotBar({\n    Key key,\n    this.alignment: FractionalOffset.centerRight,\n    @required this.controller,\n    @required this.animations,\n    @required this.isClockwise,\n    this.marginLeft = 15.0,\n    this.marginRight = 0.0,\n  }) : super(key: key, listenable: controller);\n\n  // The AnimatedWidget in this case is animating a relatively unused value.\n  // Which is the transform value on the transform widget.\n  // Transforms are much like CSS transform. It accepts a variety of functions\n  // on it's Transform property. This specific property will rotate a widget\n  // around a designated point.\n  // The most important part to understand here is that it relies\n  // on the value of the animation (Interval), so it's constantly being updated\n  // by the AnimatedWidget\n  Matrix4 clockwiseHalf(animation) =>\n      new Matrix4.rotationZ((animation.value * math.pi * 2.0) * .5);\n  Matrix4 counterClockwiseHalf(animation) =>\n      new Matrix4.rotationZ(-(animation.value * math.pi * 2.0) * .5);\n\n  @override\n  Widget build(BuildContext context) {\n    // Tell the widget which way to rotate based on its position\n    var transformOne;\n    var transformTwo;\n    if (isClockwise) {\n      transformOne = clockwiseHalf(animations[0]);\n      transformTwo = clockwiseHalf(animations[1]);\n    } else {\n      transformOne = counterClockwiseHalf(animations[0]);\n      transformTwo = counterClockwiseHalf(animations[1]);\n    }\n\n    // This is the real trick. Just wrap the Bar widget in two transforms, one\n    // for each transformation (or Interval passed in as an Animation).\n    return new Transform(\n      transform: transformOne,\n      alignment: alignment,\n      child: new Transform(\n        transform: transformTwo,\n        alignment: alignment,\n        child: new Peg(marginLeft: marginLeft, marginRight: marginRight),\n      ),\n    );\n  }\n}\n```\n\nIn order to get this all working perfectly, you'll also need to update your\n`Peg` class to respect the passed in margins.\n\n```dart\nclass Peg extends StatelessWidget {\n  final double marginLeft;\n  final double marginRight;\n\n  const Peg({Key key, this.marginLeft, this.marginRight}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    return new Container(\n      width: 35.0,\n      height: 15.0,\n      margin: new EdgeInsets.only(left: marginLeft, right: marginRight),\n      decoration: new BoxDecoration(\n        color: const Color.fromRGBO(0, 0, 255, 1.0),\n        borderRadius: new BorderRadius.circular(10.0),\n        boxShadow: [\n          new BoxShadow(\n            color: Colors.black12,\n            blurRadius: 8.0,\n            spreadRadius: 1.0,\n            offset: new Offset(1.0, 0.0),\n          ),\n          new BoxShadow(\n            color: Colors.black26,\n            blurRadius: 6.0,\n            spreadRadius: 1.5,\n            offset: new Offset(1.0, 0.0),\n          ),\n        ],\n      ),\n    );\n  }\n}\n```\n\nIn the next lesson, we'll complete this animation by wiring everything up in the parent class.\n","created_at":"2020-07-24T17:42:02.927Z","id":88,"slug":"wrap-the-pegs-in-animated-widgets","title":"Wrap the Pegs in AnimatedWidgets","tutorial":9,"updated_at":"2020-08-05T19:12:37.252Z"},{"author":1,"content":"All Flutter Animations need two things:\n\n1. An AnimationController. This controller has two important purposes. First,\n   it defines how long the animation will last via it's `duration` property. It's\n   other purpose is to provide a handful of methods that tell the animation how to\n   behave. i.e. `repeat()`, `forward()` and `reverse()`.\n\n2. Tweens. `Tween` is short for 'in between', and it represents the value of\n   the property changing in between frames. For example, if you're animating the\n   opacity of a container from 0.0 to 1.0, your tween will represent the values at\n   `0.1`, `0.2`, and so on.\n\nYou set up Tweens by creating new Tween class and passing the starting and\nending values. In the opacity example, because opacity values are `doubles`\nyou'd do something like this:\n\n```dart\nTween<double> tween = new Tween<double>(begin: 0.0, end: 1.0);\n// then you'd animate it, but more on that in a bit\n```\n\nBut if you wanted to animate from blue to green, Flutter Tweens can do that too:\n\n```dart\nColorTween colorTween = new ColorTween(\n  begin: Colors.blue[400],\n  end: Colors.green[400],\n);\n\n// then you'd animate it, but more on that in bit.\n```\n\nThe point is, Tween's return values at periods between start and finish, which\nyou can pass as props to whatever you're animating, so it's always getting\nupdated. \n\nIn the next lesson, we'll see an example of using these classes.","created_at":"2020-07-24T17:40:51.308Z","id":85,"slug":"tween-and-animation-controller-classes","title":"Tween and AnimationController classes","tutorial":9,"updated_at":"2020-07-24T17:40:51.317Z"},{"author":1,"content":"In this example, you're going to build a simple animation that looks like this:\n\n![custom animation gif](https://res.cloudinary.com/ericwindmill/image/upload/c_scale,w_250/v1525022858/flutter_by_example/animation.gif)\n\nWhile this looks like one fluid animation, this is actually 8 animation definitions. Each of the four pegs is animated twice. \n\n> #### What'll you learn?\n>\n> * AnimatedWidget\n> * Tweens\n> * AnimationController\n> * Transform widget\n\nThis 'app' will have 4 classes:\n\n1. `PegProgressIndicator extends StatefulWidget`\n2. `_PegProgressIndicatorState extends State<PegProgressIndicator> with TickerProviderStateMixin`\n\n* This is basically the highest widget in the tree that we care about for this example. \n* It will eventually hold the `AnimationController`\n* Its Basically the entire brains of the animation\n\n3. `Peg extends StatelessWidget`\n\n* The widget for display.\n\n4. `PegAnimation extends AnimatedWidget`\n\n* This is the wrapper for the bar of pegs. It's responsible for applying animations to the individual pegs.\n* It gives the tweens and animations to `Transform` widgets, which is a built in widget that rotates or offsets a widget. We'll be using it to animate the peg.\n","created_at":"2020-07-24T17:00:31.617Z","id":82,"slug":"progress-indicator-intro-and-overview","title":"Intro and Overview","tutorial":9,"updated_at":"2020-07-24T17:00:31.627Z"},{"author":1,"content":"Now that you have 8 intervals to your animation, and an `AnimatedWidget` to\nfeed em to, the last step is just adding the `PivotBar` to your build method in\n`_BarLoadingScreenState`.\n\n```dart\n// in the [_PegProgressIndicatorState] class\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: Row(\n        mainAxisAlignment: MainAxisAlignment.center,\n        children: <Widget>[\n          PegAnimation(\n            alignment: FractionalOffset.centerLeft,\n            controller: _controller,\n            animations: [\n              stepOne,\n              stepTwo,\n            ],\n            marginRight: 0.0,\n            marginLeft: 0.0,\n            isClockwise: true,\n          ),\n          PegAnimation(\n            controller: _controller,\n            animations: [\n              stepThree,\n              stepEight,\n            ],\n            marginRight: 0.0,\n            marginLeft: 0.0,\n            isClockwise: false,\n          ),\n          PegAnimation(\n            controller: _controller,\n            animations: [\n              stepFour,\n              stepSeven,\n            ],\n            marginRight: 0.0,\n            marginLeft: 32.0,\n            isClockwise: true,\n          ),\n          PegAnimation(\n            controller: _controller,\n            animations: [\n              stepFive,\n              stepSix,\n            ],\n            marginRight: 0.0,\n            marginLeft: 32.0,\n            isClockwise: false,\n          ),\n        ],\n      ),\n    );\n  }\n```\n\nAnd that's it.","created_at":"2020-08-06T16:10:41.270Z","id":122,"slug":"bring-it-all-together-2","title":"Bring it all together","tutorial":9,"updated_at":"2020-08-06T16:15:56.759Z"},{"author":1,"content":"Back on track, time to make those bars dance. First, lets add the AnimationController and Tween objects to the app.\n\n```dart\nclass PegProgressIndicator extends StatefulWidget {\n  @override\n  _PegProgressIndicatorState createState() => _PegProgressIndicatorState();\n}\n\nclass _PegProgressIndicatorState extends State<PegProgressIndicator> with TickerProviderStateMixin {\n  AnimationController _controller;\n  Tween<double> tween;\n\n  @override\n  initState() {\n    super.initState();\n    _controller = AnimationController(\n      duration: const Duration(milliseconds: 3000),\n      vsync: this,\n    );\n    tween = Tween<double>(begin: 0.0, end: 1.00);\n    _controller.repeat().orCancel;\n  }\n\n  @override\n  dispose() {\n    _controller?.dispose();\n    super.dispose();\n  }\n\n  @override\n    Widget build(BuildContext context) {\n      return  Container(\n        child: Center(\n          child: Row(\n            mainAxisAlignment: MainAxisAlignment.center,\n            children: <Widget>[\n             Peg()\n             Peg()\n             Peg()\n             Peg()\n            ],\n          ),\n        ),\n      );\n    }\n  }\n```\n\n### Adding Tweens and Intervals\n\nThe main challenge of this animation is that it requires 8 separate\nsteps to the overall animation, controlled by one AnimationController, distributed over only 4 widgets.\n\nThere are 8 steps in this animation because each of the four bars makes a\n180degree pivot twice, and by the end of the animation they've all turned a\nfull turn.\n\nLuckily, Flutter also provides a way to make animations that only occur during\ncertain times of a `Tween`. It's called an Interval.\n\nYou're going to have to write an Interval for each of the eight steps. Here's\nthe explanation of one:\n\n```dart\nclass _PegProgressIndicatorState extends State<PegProgressIndicator> \n    with TickerProviderStateMixin {\n  AnimationController _controller;\n  Tween<double> tween;\n\n  @override\n  initState() {\n    super.initState();\n    _controller = new AnimationController(\n      duration: const Duration(milliseconds: 3000),\n      vsync: this,\n    );\n    tween = new Tween<double>(begin: 0.0, end: 1.00);\n    _controller.repeat().orCancel;\n  }\n\n  @override\n  dispose() {\n    _controller?.dispose();\n    super.dispose();\n  }\n\n  // START NEW\n  // Animations always start with tweens, but you can reuse tweens.\n  Animation<double> get stepOne => tween.animate(\n        // For intervals, you can pass in an Animation rather than\n        // the controller\n        new CurvedAnimation(\n          // But pass in the controller here\n          parent: _controller,\n          // The interval is basically what point of the tween\n          // to start at, and what point to end at\n          // this tween is 0 to 1,\n          // so step one should only animate the first 1/8 of the tween\n          // which is 0 to 0.125\n          curve: new Interval(\n            0.0,\n            0.125,\n            // the style curve to pass.\n            curve: Curves.linear,\n          ),\n        ),\n      );\n// ...\n```\n\nOnce you've written all 8 intervals, you're going to need the widgets that\nactually animate during each of these 8 steps. Below you can find all 8 intervals for convenient copy and pasting.\n\n```dart\n  Animation<double> get stepOne => tween.animate(\n        CurvedAnimation(\n          parent: _controller,\n          curve: Interval(\n            0.0,\n            0.125,\n            curve: Curves.linear,\n          ),\n        ),\n      );\n  Animation<double> get stepTwo => tween.animate(\n        CurvedAnimation(\n          parent: _controller,\n          curve: Interval(\n            0.125,\n            0.26,\n            curve: Curves.linear,\n          ),\n        ),\n      );\n  Animation<double> get stepThree => tween.animate(\n        CurvedAnimation(\n          parent: _controller,\n          curve: Interval(\n            0.25,\n            0.375,\n            curve: Curves.linear,\n          ),\n        ),\n      );\n  Animation<double> get stepFour => tween.animate(\n        CurvedAnimation(\n          parent: _controller,\n          curve: Interval(\n            0.375,\n            0.5,\n            curve: Curves.linear,\n          ),\n        ),\n      );\n  Animation<double> get stepFive => tween.animate(\n        CurvedAnimation(\n          parent: _controller,\n          curve: Interval(\n            0.5,\n            0.625,\n            curve: Curves.linear,\n          ),\n        ),\n      );\n  Animation<double> get stepSix => tween.animate(\n        CurvedAnimation(\n          parent: _controller,\n          curve: Interval(\n            0.625,\n            0.75,\n            curve: Curves.linear,\n          ),\n        ),\n      );\n  Animation<double> get stepSeven => tween.animate(\n        CurvedAnimation(\n          parent: _controller,\n          curve: Interval(\n            0.75,\n            0.875,\n            curve: Curves.linear,\n          ),\n        ),\n      );\n  Animation<double> get stepEight => tween.animate(\n        CurvedAnimation(\n          parent: _controller,\n          curve: Interval(\n            0.875,\n            1.0,\n            curve: Curves.linear,\n          ),\n        ),\n      );\n```","created_at":"2020-07-24T17:41:39.161Z","id":87,"slug":"using-tweens-and-intervals","title":"Using Tweens and Intervals","tutorial":9,"updated_at":"2020-07-24T17:41:39.173Z"},{"author":1,"content":"To start, build the `Peg` widget, which is mostly just a dumb widget that we'll create four of in our app. Each one will be animated separately. \n\nThis widget is built by manipulating a `Container` widget. It's a container widget that's designed with a specific size, color, border radius, and box shadow. \n\n```dart\nimport 'package:flutter/material.dart';\n\nclass Peg extends StatelessWidget {\n  final double marginLeft;\n  final double marginRight;\n\n  const Peg({Key key, this.marginLeft, this.marginRight}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    return new Container(\n      width: 35.0,\n      height: 15.0,\n      margin: new EdgeInsets.only(left: marginLeft, right: marginRight),\n      decoration: new BoxDecoration(\n        color: const Color.fromRGBO(0, 0, 255, 1.0),\n        borderRadius: new BorderRadius.circular(10.0),\n        boxShadow: [\n          new BoxShadow(\n            color: Colors.black12,\n            blurRadius: 8.0,\n            spreadRadius: 1.0,\n            offset: new Offset(1.0, 0.0),\n          ),\n          new BoxShadow(\n            color: Colors.black26,\n            blurRadius: 6.0,\n            spreadRadius: 1.5,\n            offset: new Offset(1.0, 0.0),\n          ),\n        ],\n      ),\n    );\n  }\n}\n```\n\nThe properties `Peg.marginLeft` and `Peg.marginRight` are used to position the pegs next to each other correctly. They will be discussed in more detail later in this tutorial.","created_at":"2020-07-24T17:01:38.537Z","id":84,"slug":"custom-widget-peg","title":"Custom Widget: Peg","tutorial":9,"updated_at":"2020-07-24T17:01:38.546Z"}]},"strapiTableOfContents":{"contents":"{\n  \"Dart\": {\n    \"Getting Started with Dart\": [\n      \"About Dart\",\n      \"Install Dart on your machine\",\n      \"Dartpad\",\n      \"Text Editors: Intellij and VSCode\",\n      \"Resources: Documentation and Pub.dev\",\n      \"Hello World\",\n      \"The main function\",\n      \"Print to the console\"\n    ],\n    \"Dart Fundamentals\": [\n      \"Values and variables\",\n      \"Comments\",\n      \"const and final variables\",\n      \"Arithmetic and Comparison Operators\",\n      \"Assignment Operators\",\n      \"Logical Operators\",\n      \"Null Aware Operators\",\n      \"Type Test Operators\",\n      \"Bitwise and Shift Operators\",\n      \"Control Flow: if, else, else if\",\n      \"Switch statements and case\",\n      \"Ternary Conditional operator\",\n      \"Loops: for and while\",\n      \"Anatomy of Dart Functions\",\n      \"Arrow functions\",\n      \"Function arguments: default, optional, named\",\n      \"Lexical Scope\",\n      \"Cascade notation\"\n    ],\n    \"Data Types\": [\n      \"Intro to Dart's Type System\",\n      \"Numbers\",\n      \"Strings\",\n      \"Booleans\",\n      \"dynamic\",\n      \"lists\",\n      \"sets\",\n      \"maps\"\n    ],\n    \"Object-Oriented Programming\": [\n      \"Intro to OOP\",\n      \"Classes\",\n      \"Constructors\",\n      \"Properties and methods\",\n      \"Methods: static, private, etc\",\n      \"Getters and setters\",\n      \"Extending classes (inheritance)\",\n      \"Initializer lists and final properties\",\n      \"Factory methods\",\n      \"Singletons\",\n      \"Abstract classes (and interfaces)\",\n      \"Mixins\",\n      \"Extension methods\"\n    ],\n    \"Iterables, Iterators, and Collections\": [\n      \"What are collections (and iterables)?\",\n      \"Looping: for-in and forEach\",\n      \"Reading elements pt 1: first, last\",\n      \"Adding elements: add and insert (all)\",\n      \"Checking for elements: contains, indexOf, any, every\",\n      \"Removing elements: remove, clear, removeWhere\",\n      \"Filtering elements: where, takeWhile, and skipWhile\",\n      \"Changing elements: map and expand\",\n      \"Deriving values from elements: fold, reduce, join\",\n      \"Type casting collections: cast, as, retype, toSet, toList\",\n      \"Iterators: understanding and creating your own\",\n      \"Iterable-like methods on maps (and putIfAbsent)\"\n    ]\n  },\n  \"Flutter\": {\n    \"Getting started with Flutter\": [\n      \"About Flutter\",\n      \"IDEs and resources\"\n    ],\n    \"Widgets\": [\n      \"Intro to Widgets\",\n      \"Widget types: Stateful and Stateless\",\n      \"StatefulWidget lifecycle\",\n      \"The Widget tree\",\n      \"BuildContext\",\n      \"Inherited Widgets\",\n      \"Thinking in widgets\"\n    ],\n    \"Intro Flutter App\": [\n      \"Intro and Setup\",\n      \"Data Model and HTTP\",\n      \"Build a custom widget\",\n      \"ListView and builder pattern\",\n      \"Gradient Backgrounds\",\n      \"Routing: Add a detail page\",\n      \"Routing 2: Add a form page\",\n      \"User Input\",\n      \"Sliders and Buttons\",\n      \"Snackbars and Dialogs\",\n      \"Built-in Animation: AnimatedCrossFade\",\n      \"Built-in Animation: Hero transition\"\n    ],\n    \"Custom Animation: Progress Indicator\": [\n      \"Intro and Overview\",\n      \"Build the example app boiler-plate\",\n      \"Custom Widget: Peg\",\n      \"Tween and AnimationController classes\",\n      \"Tween by example\",\n      \"Using Tweens and Intervals\",\n      \"Wrap the Pegs in AnimatedWidgets\",\n      \"Bring it all together\"\n    ],\n    \"State Management: Blocs without Libraries\": [\n      \"What are blocs?\",\n      \"Calendar App introduction\",\n      \"Create bloc one\",\n      \"Create a bloc provider\",\n      \"Using StreamBuilders with blocs\",\n      \"Create bloc two: Add/Edit Tasks\",\n      \"Consume the second bloc's streams\",\n      \"Complete source code\"\n    ],\n    \"State Management: Provider\": [\n      \"What is Provider?\",\n      \"The most basic example using Provider\",\n      \"ChangeNotifierProvider\",\n      \"Rebuilding widgets with Consumer\",\n      \"Finer build control with Selector\",\n      \"Future Provider\",\n      \"MultiProvider micro lesson\",\n      \"Stream Provider\",\n      \"Using context extensions for more control\",\n      \"ProxyProvider\",\n      \"Using .value constructors\",\n      \"The final example (A shopping cart app)\",\n      \"For the curious: How is provider implemented\"\n    ],\n    \"Handling Data with Brick: Offline First with Rest\": [\n      \"About Brick and setup\",\n      \"Adding a Repository\",\n      \"Adding a Model\",\n      \"Generating Code\",\n      \"Rendering Models\",\n      \"Adding an Association\"\n    ]\n  }\n}"}},"pageContext":{"slug":"custom-widget-peg","tutorialTitle":"Custom Animation: Progress Indicator","previous":{"author":1,"content":"Back on track, time to make those bars dance. First, lets add the AnimationController and Tween objects to the app.\n\n```dart\nclass PegProgressIndicator extends StatefulWidget {\n  @override\n  _PegProgressIndicatorState createState() => _PegProgressIndicatorState();\n}\n\nclass _PegProgressIndicatorState extends State<PegProgressIndicator> with TickerProviderStateMixin {\n  AnimationController _controller;\n  Tween<double> tween;\n\n  @override\n  initState() {\n    super.initState();\n    _controller = AnimationController(\n      duration: const Duration(milliseconds: 3000),\n      vsync: this,\n    );\n    tween = Tween<double>(begin: 0.0, end: 1.00);\n    _controller.repeat().orCancel;\n  }\n\n  @override\n  dispose() {\n    _controller?.dispose();\n    super.dispose();\n  }\n\n  @override\n    Widget build(BuildContext context) {\n      return  Container(\n        child: Center(\n          child: Row(\n            mainAxisAlignment: MainAxisAlignment.center,\n            children: <Widget>[\n             Peg()\n             Peg()\n             Peg()\n             Peg()\n            ],\n          ),\n        ),\n      );\n    }\n  }\n```\n\n### Adding Tweens and Intervals\n\nThe main challenge of this animation is that it requires 8 separate\nsteps to the overall animation, controlled by one AnimationController, distributed over only 4 widgets.\n\nThere are 8 steps in this animation because each of the four bars makes a\n180degree pivot twice, and by the end of the animation they've all turned a\nfull turn.\n\nLuckily, Flutter also provides a way to make animations that only occur during\ncertain times of a `Tween`. It's called an Interval.\n\nYou're going to have to write an Interval for each of the eight steps. Here's\nthe explanation of one:\n\n```dart\nclass _PegProgressIndicatorState extends State<PegProgressIndicator> \n    with TickerProviderStateMixin {\n  AnimationController _controller;\n  Tween<double> tween;\n\n  @override\n  initState() {\n    super.initState();\n    _controller = new AnimationController(\n      duration: const Duration(milliseconds: 3000),\n      vsync: this,\n    );\n    tween = new Tween<double>(begin: 0.0, end: 1.00);\n    _controller.repeat().orCancel;\n  }\n\n  @override\n  dispose() {\n    _controller?.dispose();\n    super.dispose();\n  }\n\n  // START NEW\n  // Animations always start with tweens, but you can reuse tweens.\n  Animation<double> get stepOne => tween.animate(\n        // For intervals, you can pass in an Animation rather than\n        // the controller\n        new CurvedAnimation(\n          // But pass in the controller here\n          parent: _controller,\n          // The interval is basically what point of the tween\n          // to start at, and what point to end at\n          // this tween is 0 to 1,\n          // so step one should only animate the first 1/8 of the tween\n          // which is 0 to 0.125\n          curve: new Interval(\n            0.0,\n            0.125,\n            // the style curve to pass.\n            curve: Curves.linear,\n          ),\n        ),\n      );\n// ...\n```\n\nOnce you've written all 8 intervals, you're going to need the widgets that\nactually animate during each of these 8 steps. Below you can find all 8 intervals for convenient copy and pasting.\n\n```dart\n  Animation<double> get stepOne => tween.animate(\n        CurvedAnimation(\n          parent: _controller,\n          curve: Interval(\n            0.0,\n            0.125,\n            curve: Curves.linear,\n          ),\n        ),\n      );\n  Animation<double> get stepTwo => tween.animate(\n        CurvedAnimation(\n          parent: _controller,\n          curve: Interval(\n            0.125,\n            0.26,\n            curve: Curves.linear,\n          ),\n        ),\n      );\n  Animation<double> get stepThree => tween.animate(\n        CurvedAnimation(\n          parent: _controller,\n          curve: Interval(\n            0.25,\n            0.375,\n            curve: Curves.linear,\n          ),\n        ),\n      );\n  Animation<double> get stepFour => tween.animate(\n        CurvedAnimation(\n          parent: _controller,\n          curve: Interval(\n            0.375,\n            0.5,\n            curve: Curves.linear,\n          ),\n        ),\n      );\n  Animation<double> get stepFive => tween.animate(\n        CurvedAnimation(\n          parent: _controller,\n          curve: Interval(\n            0.5,\n            0.625,\n            curve: Curves.linear,\n          ),\n        ),\n      );\n  Animation<double> get stepSix => tween.animate(\n        CurvedAnimation(\n          parent: _controller,\n          curve: Interval(\n            0.625,\n            0.75,\n            curve: Curves.linear,\n          ),\n        ),\n      );\n  Animation<double> get stepSeven => tween.animate(\n        CurvedAnimation(\n          parent: _controller,\n          curve: Interval(\n            0.75,\n            0.875,\n            curve: Curves.linear,\n          ),\n        ),\n      );\n  Animation<double> get stepEight => tween.animate(\n        CurvedAnimation(\n          parent: _controller,\n          curve: Interval(\n            0.875,\n            1.0,\n            curve: Curves.linear,\n          ),\n        ),\n      );\n```","created_at":"2020-07-24T17:41:39.161Z","id":87,"slug":"using-tweens-and-intervals","title":"Using Tweens and Intervals","tutorial":9,"updated_at":"2020-07-24T17:41:39.173Z"},"next":null}},"staticQueryHashes":["2185715291","3564968493","63159454"]}