Giter Club home page Giter Club logo

flutter_animate's Introduction

tests

Flutter Animate is a Flutter Favorite

Flutter Animate

A performant library that makes it simple to add almost any kind of animated effect in Flutter.

  1. Pre-built effects like fade, scale, slide, align, flip, blur, shake, shimmer, shadows, crossfades, follow path, and color effects (saturation, color, and tint)
  2. Apply animated GLSL fragment shaders to widgets
  3. Easy custom effects and simplified animated builders
  4. Synchronize animations to scroll, notifiers, or anything
  5. Integrated events

All via a simple, unified API without fussing with AnimationController and StatefulWidget.

Basic Animations Visual Effects Synchronized Animations

Above: The included example app.

Duration extensions

Extension methods for num, to make specifying durations easier. For example: 2.seconds, 0.1.minutes, or 300.ms.

AnimatedController extensions

A loop extension method for AnimatedController which is identical to repeat, but adds a count parameter to specifiy how many times to play.

Basics

Syntax

To apply effects, wrap the target widget in Animate, and specify a list of effects:

Animate(
  effects: [FadeEffect(), ScaleEffect()],
  child: Text("Hello World!"),
)

It also adds an .animate() extension method to all widgets, which wraps the widget in Animate(). Each effect also adds a chainable extension method to Animate to enable a shorthand syntax:

Text("Hello World!").animate().fade().scale()

NOTE: The shortform style is used in this README, but all functionality is available in either format.

Delay, duration, curve

Effects have optional delay, duration, and curve parameters. Effects run in parallel, but you can use a delay to run them sequentially:

Text("Hello").animate()
  .fade(duration: 500.ms)
  .scale(delay: 500.ms) // runs after fade.

Note that effects are "active" for the duration of the full animation, so for example, two fade effects on the same target can have unexpected results (SwapEffect detailed below, can help address this).

If not specified (or null), these values are inherited from the previous effect, or from Animate.defaultDuration and Animate.defaultCurve if it is the first effect:

Text("Hello World!").animate()
  .fadeIn() // uses `Animate.defaultDuration`
  .scale() // inherits duration from fadeIn
  .move(delay: 300.ms, duration: 600.ms) // runs after the above w/new duration
  .blurXY() // inherits the delay & duration from move

Animate has its own delay parameter, which defines a delay before the animation begins playing. Unlike the delay on an Effect, it is only applied once if the animation repeats.

Text("Hello").animate(
    delay: 1000.ms, // this delay only happens once at the very start
    onPlay: (controller) => controller.repeat(), // loop
  ).fadeIn(delay: 500.ms) // this delay happens at the start of each loop

Other Effect Parameters

Most effects include begin and end parameters, which specify the start/end values. These are usually "smart" in the sense that if only one is specified then the other will default to a "neutral" value (ie. no visual effect). If both are unspecified the effect should use visually pleasing defaults.

// an opacity of 1 is "neutral"
Text("Hello").animate().fade() // begin=0, end=1
Text("Hello").animate().fade(begin: 0.5) // end=1
Text("Hello").animate().fade(end: 0.5) // begin=1

Many effects have additional parameters that influence their behavior. These should also use pleasant defaults if unspecified.

Text('Hello').animate().tint(color: Colors.purple)

Sequencing with ThenEffect

ThenEffect is a special convenience "effect" that makes it easier to sequence effects. It does this by establishing a new baseline time equal to the previous effect's end time and its own optional delay. All subsequent effect delays are relative to this new baseline.

In the following example, the slide would run 200ms after the fade ended.

Text("Hello").animate()
  .fadeIn(duration: 600.ms)
  .then(delay: 200.ms) // baseline=800ms
  .slide()

Animating lists

The AnimateList class offers similar functionality for lists of widgets, with the option to offset each child's animation by a specified interval:

Column(children: AnimateList(
  interval: 400.ms,
  effects: [FadeEffect(duration: 300.ms)],
  children: [Text("Hello"), Text("World"),  Text("Goodbye")],
))

// or shorthand:
Column(
  children: [Text("Hello"), Text("World"),  Text("Goodbye")]
    .animate(interval: 400.ms).fade(duration: 300.ms),
)

Shared effects

Because Effect instances are immutable, they can be reused. This makes it easy to create a global collection of effects that are used throughout your app and updated in one place. This is also useful for design systems.

MyGlobalEffects.transitionIn = <Effect>[
  FadeEffect(duration: 100.ms, curve: Curves.easeOut),
  ScaleEffect(begin: 0.8, curve: Curves.easeIn)
]

// then:
Text('Hello').animate(effects: MyGlobalEffects.transitionIn)

Custom effects & builders

It is easy to write new resuable effects by extending Effect, but you can also easily create one-off custom effects by using CustomEffect, ToggleEffect, and SwapEffect.

CustomEffect

CustomEffect lets you build custom animated effects. Simply specify a builder function that accepts a context, value, and child. The child is the target of the animation (which may already have been wrapped in other effects).

For example, this would add a background behind the text and fade it from red to blue:

Text("Hello World").animate().custom(
  duration: 300.ms,
  builder: (context, value, child) => Container(
    color: Color.lerp(Colors.red, Colors.blue, value),
    padding: EdgeInsets.all(8),
    child: child, // child is the Text widget being animated
  )
)

By default it provides a value from 0-1 (though some curves can generate values outside this range), based on the current time, duration, and curve. You can also specify begin and end values as demonstrated in the example below.

Animate can be created without a child, so you use CustomEffect as a simplified builder. For example, this would build text counting down from 10, and fading out:

Animate().custom(
  duration: 10.seconds,
  begin: 10,
  end: 0,
  builder: (_, value, __) => Text(value.round()),
).fadeOut()

ToggleEffect

ToggleEffect also provides builder functionality, but instead of a double, it provides a boolean value equal to true before the end of the effect and false after (ie. after its duration).

Animate().toggle(
  duration: 2.seconds,
  builder: (_, value, __) => Text(value ? "Before" : "After"),
)

This can also be used to activate "Animated" widgets, like AnimatedContainer, by toggling their values with a minimal delay:

Animate().toggle(
  duration: 1.ms,
  builder: (_, value, __) => AnimatedContainer(
    duration: 1.seconds,
    color: value ? Colors.red : Colors.green,
  ),
)

SwapEffect

SwapEffect lets you swap out the whole target widget at a specified time:

Text("Before").animate()
  .swap(duration: 900.ms, builder: (_, __) => Text("After"))

This can also be useful for creating sequential effects, by swapping the target widget back in, effectively wiping all previous effects:

text.animate().fadeOut(300.ms) // fade out & then...
  // swap in original widget & fade back in via a new Animate:
  .swap(builder: (_, child) => child.animate().fadeIn())

ShaderEffect

ShaderEffect makes it easy to apply animated GLSL fragment shaders to widgets. See the docs for details.

myWidget.animate()
  .shader(duration: 2.seconds, shader: myShader)
  .fadeIn(duration: 300.ms) // shader can be combined with other effects

Events & callbacks

Animate includes the following callbacks:

  • onInit: the internal AnimationController has been initialized
  • onPlay: the animation has started playing after any Animate.delay
  • onComplete: the animation has finished

These callbacks return the AnimationController, which can be used to manipulate the animation (ex. repeat, reverse, etc).

Text("Horrible Pulsing Text")
  .animate(onPlay: (controller) => controller.repeat(reverse: true))
  .fadeOut(curve: Curves.easeInOut)

For more nuanced callbacks, use CallbackEffect or ListenEffect.

CallbackEffect

CallbackEffect lets you add a callback to an arbitrary postion in your animations. For example, adding a callback halfway through a fade:

Text("Hello").animate().fadeIn(duration: 600.ms)
  .callback(duration: 300.ms, callback: (_) => print('halfway'))

As with other effects, it will inherit the delay and duration of prior effects:

Text("Hello").animate().scale(delay: 200.ms, duration: 400.ms)
  .callback(callback: (_) => print('scale is done'))

ListenEffect

ListenEffect lets you register a callback to receive the animation value (as a double) for a given delay, duration, curve, begin, and end.

Text("Hello").animate().fadeIn(curve: Curves.easeOutExpo)
  .listen(callback: (value) => print('current opacity: $value'))

The above example works, because the listen effect inherits duration and curve from the fade, and both use begin=0, end=1 by default.

Adapters and Controllers

By default, all animations are driven by an internal AnimationController, and update based on elapsed time. For more control, you can specify your own external controller, or use an adapter. You can also set autoPlay=false if you want to start the animation manually.

Adapters synchronize the AnimationController to an external source. For example, the ScrollAdapter updates an animation based on a ScrollController so you can run complex animations based on scroll interactions.

You still define animations using durations, but the external source must provide a 0-1 value.

Flutter Animate ships with a collection of useful adapters. Check them out for more information.

Reacting to State Changes

Animate can react to state changes similar to "Animated" widgets (ex. AnimatedOpacity). Simply set up your animation normally, but set a target value. When the value of target changes, it will automatically animate to the new target position (where 0 is the beginning and 1 is the end).

For example, combined with logic that toggles _over via setState, this will fade and scale the button on roll over:

MyButton().animate(target: _over ? 1 : 0)
  .fade(end: 0.8).scaleXY(end: 1.1)

You can also update the value property to jump to that position.

Testing Animations

When testing animations, you can set Animate.restartOnHotReload=true which will cause all animations to automatically restart every time you hot reload your app.

Installation

Grab it from pub.dev.

flutter_animate's People

Contributors

arthurbcd avatar domesticmouse avatar drown0315 avatar ellet0 avatar esdotdev avatar gskinner avatar lootwig avatar nohli avatar storm265 avatar thithip avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flutter_animate's Issues

Swaping a sequence of images and apply effects

Flutter Web

Running the code below, I am trying to swap a sequence of images and apply effects.

However, due to setState() a new animate object gets initialized. Resulting in numerous calls to update the image after time.
I was not able to cancel the old animation using onComplete: (controller) => controller.stop(), the number of calls keeps piling up.

I thought about leaving out controller.repeat() but then nextImage() gets never called. It is a bit of strange behavior. For me onComplete does not work consistently, I need to use callback() to call nextImage().

Also tried to isolate everything in an Animator object in a semi-singleton, but then the current image gets not updated.

Also tried swap() but it did not work for me either.

Is there a correct approach to solve this?

Thank you!

class _FadeState extends State<Fade> {
  final c = ImageService();
  late Image img;

  @override
  initState() {
    super.initState();
    nextImage();
  }

  nextImage() {
    setState(() => img = c.nextImage());
  }

  @override
  Widget build(BuildContext context) {
    return Container(
            width: ..., height: ..., child: img)
        .animate(
          onPlay: ((controller) => controller.repeat()),
        )
        .tint(
            duration: c.crossFadeDuration.ms,
            color: c.color,
            begin: 1.0,
            end: 0.0)
        .then()
        .tint(
            duration: c.crossFadeDuration.ms,
            color: c.color2,
            begin: 0.0,
            end: 1.0)
        .callback(
            duration: c.crossFadeDuration.ms, callback: (_) => nextImage());
  }
}

Optionally wrap `Animate` with `RepaintBoundary`

Reasoning

As discussed in this talk about flutter_animate, the performance of animations might get significantly improved by wrapping them in a RepaintBoundary.
I argue that this is not apparent to everyone. Especially newer Flutter developers might not be aware of this performance improvement opportunity.

Proposal

Add an optional bool? useRepaintBoundary to the Animate() widget. If set true, the animation should be automatically wrapped with a RepaintBoundary. The parameter should be false by default, as RepaintBoundaries also come with a cost and the usage should be transparent.

It would be vital to also add extensive explanation in the documentation of the parameter about when to use RepaintBoundary and when not to, optimally with examples. This way, people not knowing about the widget would get educated and situations where RepaintBoundary is applied suboptimally get minimized (although not eliminated!).

If the addition of such a parameter comes with too high of a risk of missusage, I propose to at least add a section to the ReadMe/Docs of this package explaining the potential benefits and drawbacks of using a RepaintBoundary in combination with animations.

Additional Context

This matter was discussed briefly in this tweet with the author of the package.

Should TintEffect use a color value?

Right now TintEffect has a color param, and uses a double for its value to indicate the strength of the tint.

It might make sense to update it to remove the color param completely, and change its value to be of type Color. This would be a tiny bit more work for a monochromatic tint since the strength would be determined by the color's alpha, but would enable the ability to animate between different colors of tint.

It's worth noting though that you get a somewhat similar effect now by nesting tints (though this is not identical, and is more expensive).

// current (verbose):
foo.animate().tint(color: Colors.red, begin: 0, end: 1);
foo.animate().tint(color: Colors.blue, begin: 1, end: 0).tint(color: Colors.red, begin: 0, end: 1);

// proposed equivalent (verbose):
foo.animate().tint(begin: Colors.transparent, end: Colors.red);
foo.animate().tint(begin: Colors.blue, end: Colors.red);

I need to retest this, but I believe that Colors.transparent is equivalent to Color(0), and that the default interpolation between a color and transparent affects the rgb channels and not just the alpha channel, leading to a darkening of the color. If that's the case, then devs would likely need to be more specific, like: Colors.red.withOpacity(0). Though the effect could automatically handle this case if begin or end is omitted or null.

Pros: can tint between different colors, and the value type is perhaps more appropriate.
Cons: likely a bit fussier to implement and use.
Considerations: How common is the use case for animating between different colored tints? I'm guessing fairly rare.

Unable to stop animation using a provided `AnimationController`

Consider the following animation:

typedef ValueType = Article;

class AnimatedZoomImage extends StatelessWidget {
  const AnimatedZoomImage({
    Key? key,
    required this.scaleDuration,
    required this.animationController,
  }) : super(key: key);

  final Duration scaleDuration;
  
  
  final AnimationController animationController;

  @override
  Widget build(BuildContext context) {
    return CachedNetworkImage(
      fit: BoxFit.cover,
      imageUrl: 'some-url',
      alignment: Alignment.topCenter,
    ).animate(
      controller: animationController,
      onComplete: (c) => c.repeat(reverse: true),
    ).custom(
        duration: scaleDuration,
        builder: (context, value, child) {
          final scale = 1.0 + (value * .5);
          return Transform(
            alignment: Alignment.center,
            transform: Matrix4.identity().scaled(scale, scale, scale),
            child: child,
          );
        });
  }
}

Calling animationController.stop() does not stop the animation from running aka the builder method is still being called, is that by design?

An error is thrown by ScrollAdapter when an animated widget from ListView.builder is disposed.

I am trying to use ScrollAdapter inside one of the children of a ListView.builder, but when the widget is disposed (out of the screen) and we keep scrolling, it will throw an error.

image

Here is the minimum reproducible example:

ListView.builder(
  controller: _scrollController,
  itemCount: 3,
  itemBuilder: (context, index) {
    final text = Text(
      index.toString(),
    );

    return SizedBox(
      height: MediaQuery.of(context).size.height,
      child: index == 0
          ? text
              .animate(adapter: ScrollAdapter(_scrollController))
              .slideY(end: 2.5)
          : text,
    );
  },
)

As you can see, I added an animation to the first child. When the first child goes out of view and is disposed, the error occurs.

Typo in example for AnimatedContainer

Currently reads...

Animate().toggle(
  duration: 1.ms,
  builder: (_, value, __) => AnimatedContainer(
    duration: 1.second,
    color: value ? Colors.red : Colors.green,
  ),
)
Animate().toggle(
  duration: 1.ms,
  builder: (_, value, __) => AnimatedContainer(
    duration: 1.seconds,
    color: value ? Colors.red : Colors.green,
  ),
)

Triggering the animation when a condition is met

A common use-cases is to trigger an animation stored in a widget when an event is triggered, such as

  • on the press of a button for a visual feedback. E.g. Shake something on the press of a button

Since, the documentation doesn't cover this use-case, the one simple solution I could think of is to make the parent widget stateful to capture the state of a toggle flag. The flag is set to true on the press of a button and false once the playback is complete. While this works, the code is not concise and certainly doesn't make for a good reading.

Is there a better way to accomplish this that may be documented?

How to reverse an animation? E.g., slideX?

Love the package. I can make widgets slide in and get the AnimationController:

Text.animate(onPlay: (controller) {
              _animationController = controller;
            }).slideX(),

Is there a what to make the widget then slide out with the controller? And then back in again?

Swap effect for a range of values

Hey! I'm new to the party but really enjoy flutter_animate so far. The only effect, I've missed so far (and maybe I'm just blind) is a way to swap/fade through a range of widgets/builders similar to https://pub.dev/packages/cross_fade. Would that be something that's already easy to emulate with flutter_animate or something you've considered?

Cheers and keep up the great work!

Add support for declarative playback using `end` field

There are certain cases where it would be nice to just set the end value for a set of tweens, and have the tweens run when that end value changes.

For example:

GridView(
  children: imageWidgets.map((i){
    bool selected = imageWidgets.indexOf(i) == selectedIndex;
    return i.animate().scale(
       duration: 200.ms,
       curve: Curves.easeOut,
       begin: 1, 
       end: selected? 1.2 : 1);
})

We could then easily create this effect where the selected image scales in, while the previously selected image scales out. The others do nothing.

X6VrKADEzP.mp4

Currently the easiest way to do this is still with the built in TweenAnimationBuilder, which is a fine API, but it cant hook into any of Animates effects:

imageWidgets.map((i){
  bool selected = imageWidgets.indexOf(i) == selectedIndex;
    
  return TweenAnimationBuilder<double>(
    tween: Tween(begin: 1, end: selected ? 1.15 : 1),
    duration: 200.ms,
    curve: Curves.easeOut,
    builder: (_, value, child) => Transform.scale(scale: value, child: img),
  )
}

If you matched the declarative behavior of TAB, then the old end would become the new begin if end ever changes, which is quite nice for continuous effects (go from A > B > A). Changes to begin are ignored until the widget state is reloaded.

Add directionality to ScrollAdapter

There are scenarios where you would only want the animation to run in one way when controlled by a ScrollAdapter.

It could use ScrollDirection.forward/reverse. Though it's worth considering whether this should be added to all adapters.

Prevent initial state to get applied when a controller is attached

Is there a way to prevent the initial state of any effect to get applied before it's triggered using the controller? In the following example, I don't want to show the container as faded to 0.5 initially:

Animate(
  controller: controller,
  adapter: adapter,
  effects: [
    FadeEffect(
      delay: 10.ms,
      duration: 1000.ms,
      begin: 0.5,
      end: 1.0,
    )
  ],
  child: Container(
    height: 100,
    width: 100,
    decoration: BoxDecoration(
      color: Colors.blueGrey,
      borderRadius: BorderRadius.circular(16),
    ),
  ),
)

The above example might not be a good use case, but I need to do it with some other animation effects.

Matrix4 entries must be finite

'dart:ui/painting.dart': Failed assertion: line 50 pos 10: '<optimized out>': Matrix4 entries must be finite., #0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
#1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
#2      _matrix4IsValid (dart:ui/painting.dart:50:10)
#3      new Gradient.linear (dart:ui/painting.dart:3726:34)
#4      LinearGradient.createShader (package:flutter/src/painting/gradient.dart:436:24)
#5      ShimmerEffect.build.<anonymous closure>.<anonymous closure> (package:flutter_animate/effects/shimmer_effect.dart:70:48)
#6      RenderShaderMask.paint (package:flutter/src/rendering/proxy_box.dart:1188:35)
#7      RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#8      PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#9      PaintingContext.pushLayer (package:flutter/src/rendering/object.dart:460:12)
#10     PaintingContext.pushClipPath (package:flutter/src/rendering/object.dart:600:7)
#11     RenderClipPath.paint (package:flutter/src/rendering/proxy_box.dart:1830:25)
#12     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#13     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#14     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:144:15)
#15     RenderDecoratedBox.paint (package:flutter/src/rendering/proxy_box.dart:2371:11)
#16     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#17     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#18     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:144:15)
#19     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#20     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#21     RenderBoxContainerDefaultsMixin.defaultPaint (package:flutter/src/rendering/box.dart:2900:15)
#22     RenderStack.paintStack (package:flutter/src/rendering/stack.dart:654:5)
#23     RenderStack.paint (package:flutter/src/rendering/stack.dart:670:7)
#24     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#25     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#26     RenderShiftedBox.paint (package:flutter/src/rendering/shifted_box.dart:84:15)
#27     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#28     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#29     PaintingContext.pushLayer (package:flutter/src/rendering/object.dart:460:12)
#30     PaintingContext.pushOpacity (package:flutter/src/rendering/object.dart:693:5)
#31     RenderOpacity.paint (package:flutter/src/rendering/proxy_box.dart:954:21)
#32     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#33     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#34     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:144:15)
#35     RenderTransform.paint (package:flutter/src/rendering/proxy_box.dart:2617:17)
#36     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#37     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#38     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:144:15)
#39     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#40     PaintingContext._repaintCompositedChild (package:flutter/src/rendering/object.dart:155:11)
#41     PaintingContext.repaintCompositedChild (package:flutter/src/rendering/object.dart:98:5)
#42     PipelineOwner.flushPaint (package:flutter/src/rendering/object.dart:1116:31)
#43     RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:515:19)
#44     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:884:13)
#45     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:378:5)
#46     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1175:15)
#47     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1104:9)
#48     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1015:5)
#49     _invoke (dart:ui/hooks.dart:148:13)
#50     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:318:5)#51     _drawFrame (dart:ui/hooks.dart:115:31)

Unfortunately i can not provide reproducible steps because it's very hard to recreate this issue in a new project. It happens on Windows when i minimize app with custom PostMessage method and use ShimmerEfect on a GridView child. Windows makes app smaller to show hiding animation and this issue appears.

image
It seems that width of gridview's child become 0 after minimizing the app.

Can something like this be implemented as a fix or do not play animation at all if one of child's constraints is 0?
image

To get the same error output you can just create a container with 0 width and apply this effect to it

Animate(
            onPlay: (controller) => controller.repeat(reverse: true),
            effects: [
              ShimmerEffect(
                color: Colors.white.withOpacity(0.40),
                size: 2,
                blendMode: BlendMode.srcATop,
                delay: const Duration(milliseconds: 350),
                duration: const Duration(milliseconds: 1000),
              ),
            ],
            child: Container(
              color: Colors.black,
              width: 0,
              height: 80,
              child: Text('Hello World'),
            ),
          )

Implement better smoothing in Adapter

Adapter currently uses AnimationController.animateTo to smooth value changes when animate=true. It works for infrequent changes, but Flutter's implementation gets "change locked" when the value changes constantly.

It'll need access to a tick in a non-Widget class, should respect the animation duration, and should be optimized to disable the tick when it isn't needed.

Add additional presets

Once issue #31 is finalized, it would be nice to add an initial layer of additional presets/variations using the architecture.

While there are unlimited tween behaviors one could think of, some common ones stand out at the top of the list:
(inspired by https://pub.dev/packages/animate_do and https://animate.style/)

FadeInUp
FadeInDown
FadeInLeft
FadeInRight

FadeOutUp
FadeOutDown
FadeOutLeft
FadeOutRight

SlideInUp
SlideInDown
SlideInLeft
SlideInRight

SlideOutUp
SlideOutDown
SlideOutLeft
SlideOutRight

FlipInX
FlipInY
FlipOutX
FlipOutY

ZoomIn
ZoomOut

BounceIn
BounceOut

Notably, some of these are simple variations of existing effects (SlideDown or ZoomIn), while others are composed of multiple effects (FadeInDown). Ideally the new system supports composition, so FadeInDown uses FadeIn + SlideInDown rather than re-creating their logic.

This issue could be thought of as a first pass at the most obvious presets, and then other presets could be considered in the future. Mostly this can serve as dogfood for issue #31

Make it easier to set ScrollAdapter begin/end relative to a specific widget

When creating scroll driven animations, it is often a lot easier to set anchor points relative to an element in the scroll region (versus absolute pixel values). This is especially true with dynamic or responsive content.

Quick sketch follows for illustration, naming and implementation specifics TBD.

ScrollAdapter(
  begin: ScrollAdapter.getPosition(myWidget, alignment: Alignment.center, offset: Offset(y: -100),
  ...
)

The above would set the begin property to 100px above the middle of myWidget.

Add a way to clear or reset the animation.

It would be useful to have an effect similar to swap, but which returns the original child:

Currently to create a complex sequence you can do something like this (from the README):

Widget text = Text("Hello World!");

// then:
text.animate().fadeOut(300.ms) // fade out & then...
  .swap(builder: (_) => text.animate().fadeIn()) // swap in original widget & fade back in

But the need to save off the original child is a pain point. We could do something like this:

Text("Hello World!").animate().fadeOut(300.ms) // fade out & then...
  .clear(builder: (child) => child.animate().fadeIn()) // swap in original widget & fade back in

Effects triggered by unmounting a Widget

I'm seeing my animation being applied when the widget is added to my UI, but how would I indicate the animation which should play when the widget is removed from the render tree?

Applying Effects within an Animate executes all Effects at once, ignores ThenEffect

I have started a project using this package. I'm implementing a strategy where media assets (images, rive animations, custom painters) can be manipulated as a child within an Animate(). I'm dynamically creating effects from json (which could be another feature) and adding them to the effects: property.

So a list of json objects like below is pushed thru an Effect factory based on this context to get a List that becomes the value for the effects: property.

"onLoadEffects": [
                      {"name": "FadeEffect",
                        "duration": 2000,
                        "delay": 0,
                        "curve": "Curves.easeIn",
                        "begin": 1.0,
                        "end": 0.0
                      },
                      {"name": "MoveEffect",
                        "duration": 2000,
                        "delay": 0,
                        "curve": "Curves.easeOut",
                        "begin_dx": 800.0,
                        "begin_dy": 80.0,
                        "end_dx": 0.0,
                        "end_dy": 80.0
                      },
                      {"name": "ThenEffect",
                        "duration": 0,
                        "delay": 1000
                      },
                      {"name": "ScaleEffect",
                        "delay": 0,
                        "duration": 2000,
                        "curve": "Curves.easeOut",
                        "begin_dx": 0.0,
                        "begin_dy": 0.0,
                        "end_dx": 2.5,
                        "end_dy": 2.5,
                        "alignment_x": 0.0,
                        "alignment_y": 0.0
                      }
                    ],
                    "onFrameEffects": [],
                    "onExitEffects": []

Can be applied like this:

               Animate(
                  effects: effects,
                  controller: _animator,
                  onComplete: _onCompleteLoadEffects,
                  child: asset
                );

The asset, being the media asset, is manipulated by the Effects list.

The problem I am seeing during testing is the "thenEffect" doesn't get respected amongst the list. The animate should apply the move and fade effects for 2 sec, then apply the "wait" or ThenEffect for 1 sec, and finally apply the scale effect.

It seems to apply all of the effects at once. I can manually change the delay on each effect which works, but then there is no real purpose for the "thenEffect".

I could be missing something or expecting the wrong results, but this seems to be a bug based on the initial approach.

I added a callbackEffect to the end of the list and the callback was executed at the onset of the animation, not at the end.

Blur is using a inexistent "enabled" attribute

flutter_animated version 2.0.1 has Blur effect with a bug.

In blur_effect.dart ImageFiltered is using a inexistent "enabled" attribute on BlurEffect class.

I downgraded Dart SDK from 2.17.1 to 2.15.1 and not resolved.

blur_error

flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 3.0.1, on Microsoft Windows [versÆo 10.0.22621.819], locale pt-BR)
[√] Android toolchain - develop for Android devices (Android SDK version 33.0.0-rc1)
[√] Chrome - develop for the web
[√] Visual Studio - develop for Windows (Visual Studio Community 2022 17.2.3)
[√] Android Studio (version 2021.1)
[√] Connected device (4 available)
[√] HTTP Host Availability

• No issues found!

dart --version
Dart SDK version: 2.15.1 (stable) (Tue Dec 14 13:32:21 2021 +0100) on "windows_x64"

Allow for AnimateLists with different effects

Currently, AnimateLists required setting the effects at the level of the list, so all children will be animated using the same effect. I couldn't find a straightforward way of overriding the effects for specific children (e.g. the third child in the list is animated using a different effect from the others) without magic numbers or manually calculating the delay.

Animation reset issue

Animation is reset when Animate is used on a Tooltip's child and cursor is leaving->re-entering app's window.

studio64_Fk8tsMUcq5

import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Material App',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Material App Bar'),
        ),
        body: Wrap(
          spacing: 40.0,
          children: [
            Animate(
              onPlay: (controller) => controller.repeat(reverse: true),
              effects: const [
                ShakeEffect(
                  hz: 2,
                  curve: Curves.easeInOutCubic,
                  duration: Duration(milliseconds: 1800),
                  delay: Duration(milliseconds: 450),
                ),
                TintEffect(
                  curve: Curves.easeInOutCubic,
                  color: Colors.blue,
                ),
              ],
              child: const Text('Normal'),
            ),
            Tooltip(
              message: '123',
              child: Animate(
                onPlay: (controller) => controller.repeat(reverse: true),
                effects: const [
                  ShakeEffect(
                    hz: 2,
                    curve: Curves.easeInOutCubic,
                    duration: Duration(milliseconds: 1800),
                    delay: Duration(milliseconds: 450),
                  ),
                  TintEffect(
                    curve: Curves.easeInOutCubic,
                    color: Colors.blue,
                  ),
                ],
                child: const Text('Issue'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Animate.autoPlay

Currently, to create an animation that doesn't play automatically, you need to use onInit:

foo.animate(onInit: (controller) => controller.stop()).fade()

This works, but less semantically clear than having an autoPlay param.

However, implementation of such a feature has some ambiguities / concerns:

  1. Should Animate.delay be respected? It is a delay before the animation starts playing, so it's confusing either way.
  2. If the answer to (1) is yes, then how would you start it playing? A method like Animate.play? Thus far all playback control is delegated to the AnimationController, so this would be a possibly confusing departure.
  3. Because onInit runs when the animation plays, you wouldn't have a mechanism to access to controller if there was a delay.
  4. Even with autoPlay you still need to save off the Animate or AnimationController in order to play it later, so I'm not certain this is providing a ton of value.
  5. AnimateList uses Animate.delay for interval, which could cause confusionautoPlay if didn't respect delay

Something to consider, but for now I'm leaning towards not adding this. It's an advanced convenience feature, that may add more confusion than value.

Weird behavior when widget is not `const`

I do not have a smaller sample but below is some example where I encountered a weird error. If I don't have Positioned marked as const the hearts are not appearing at all (animation not starting). If I make it const, it works just fine.

Video shows how it should behave (it has hardcoded svgImage set to null so it can be const)

          if (rightButtonActive)
            /// does not animate
            Positioned(
              bottom: 105,
              right: 20,
              child: _HeartSpray(svgImage: Assets.illustrations.games.swing.rightWindHeart),
            ),
          if (leftButtonActive)
            /// works as expected, as in the video
            const Positioned(
              bottom: 105,
              left: 20,
              child: _HeartSpray(svgImage: null),
            ),
class _HeartSpray extends StatelessWidget {
  const _HeartSpray({required this.svgImage});

  final SvgGenImage? svgImage;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        // spawn 10 hearts
        for (var i = 0; i < 10; i++)
          Padding(
            padding: EdgeInsets.only(
              left: i.isEven ? math.Random().nextDouble() * 50 : 0,
              right: i.isEven ? 0 : math.Random().nextDouble() * 50,
            ),
            child: (svgImage ?? Assets.illustrations.games.swing.rightWindHeart)
                .svg()
                .animate(
                  delay: (i * 0.1).seconds,
                  onPlay: (controller) {
                    // repeat animation when finished
                    controller.repeat();
                  },
                )
                .fade(duration: 0.3.seconds)
                .scale(duration: 0.15.seconds)
                .slideY(begin: 1.0, end: -15, duration: 0.9.seconds)
                .shakeX(hz: 1)
                .fadeOut(delay: 0.2.seconds, duration: (0.9 - 0.4).seconds),
          ),
      ],
    );
  }
}
Screenrecorder-2023-02-04-19-32-27-541.mp4

Thanks for this package btw. Doing animations is much easier and more joyful and we can do magic stuff in just a few lines

Blur is not working on flutter web

Hi,

The blur effect is not working on the flutter web giving the following error.

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following TypeErrorImpl was thrown building AnimatedBuilder(animation:
AnimationController#697d7(⏮ 0.000; paused)➩Interval(0.6⋯1)➩Tween<Offset>(Offset(0.0, 0.0) →
Offset(4.0, 4.0))➩Offset(0.0, 0.0), dirty, state: _AnimatedState#47719):
Expected a value of type 'JavaScriptObject', but got one of type 'Null'

The relevant error-causing widget was:
  AnimatedBuilder
  AnimatedBuilder:file:///C:/src/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_animate-2.0.1/lib/effects/effect.dart:84:12

Can you look on this @gskinner

Thanks and Regards.

ShakeEffect does not work if the duration is less than 1000 milliseconds.

Working Code

const Icon(Icons.favorite, size: 28.0)
            .animate()
            .shake(duration: 1000.ms),  // <--- work fine

NOT working Code

const Icon(Icons.favorite, size: 28.0)
            .animate()
            .shake(duration: 999.ms),  // <--- NOT work

I think it because entry.duration.inSeconds return 0 if it is less than 1000 milliseconds.

final int count = (entry.duration.inSeconds * hz).floor();

(Bug link)

Fade out then fade in results in invisible widget

Hi – I'm noticing that following a fade out with a fade in, results in the widget not appearing at all.

  return Text('example').animate().fadeOut().then().fadeIn();

The other way around, fading in and then out, seems to work as expected.

Add a test or two :)

A couple of example tests that demonstrate how to use flutter_test would be a great starting point. It would then be easy for others in the community to extend this to a broader list that provides good coverage for the package as a whole.

loop extension causes RTE when leaving view

I am using

.animate(
  onPlay: (controller) =>
   controller.loop(count: 2, reverse: true))
  .shimmer(
    delay: 400.ms,
    duration: 1800.ms,
    color: Colors.deepPurple.shade900)
  .shake(hz: 5, curve: Curves.easeInOutCubic)
  .scaleXY(end: 1.2, duration: 600.ms)
  .then(delay: 600.ms)
  .scaleXY(end: 1 / 1.1)

to animate an icon. When I navigate away from the page, the following crash is seen. How do I say cancel the pending animation?

2023-01-29 19:39:42.737004+0530 Runner[15014:4806242] [VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: 'package:flutter/src/animation/animation_controller.dart': Failed assertion: line 533 pos 7: '_ticker != null': AnimationController.animateTo() called after AnimationController.dispose()
AnimationController methods should not be used after calling dispose.
#0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
#1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
#2      AnimationController.animateTo (package:flutter/src/animation/animation_controller.dart:533:7)
#3      AnimationControllerLoopExtensions.loop.<anonymous closure> (package:flutter_animate/src/extensions/animation_controller_loop_extensions.dart:51:9)
#4      _RootZone.run (dart:async/zone.dart:1654:54)
#5      Future.timeout.<anonymous closure> (dart:async/future_impl.dart:865:34)
#6      Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
#7      _Timer._runTimers (dart:isolate-patch/timer_impl.dart:398:19)
#8      _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:429:5)
#9      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:192:26)
2023-01-29 19:40:01.785378+0530 Runner[15014:4813608] [tcp] tcp_input [C12.1.1.10:3] flags=[R] seq=3463997066, ack=0, win=0 state=FIN_WAIT_1 rcv_nxt=3463997066, snd_una=4155211570

Change ThenEffect to establish a new baseline time

Currently ThenEffect works by establishing a new inheritable delay:

foo.animate()
  .fadeIn(duration: 700.ms) // begins at 0ms, ends at 700ms
  .then() // sets inheritable delay of 700ms (end of previous)
  .slide() // inherits 700ms delay, begins at 700ms, ends at 1400ms
  .flipX(delay: 0.ms) // overrides delay, begins at 0ms, ends at 700ms

It might be better if ThenEffect instead establishes a new "baseline time", that subsequent effects can modify with relative delays (including negative delays):

foo.animate()
  .fadeIn(duration: 700.ms) // begins at 0ms, ends at 700ms
  .then() // baseline is now 700ms (end of previous)
  .slide(delay: 0.ms) // begins at 700ms, ends at 1400ms
  .flipX(delay: -200.ms) // begins at 500ms, ends at 1200ms
  .then(delay: 300.ms) // baseline is now 1500ms (1200+300)
  .shake() // begins at 1500ms

This would require adding a new property to Animate to track this, and could break existing animations in rare cases (ex. if someone has overridden delay after using then).

Open to feedback on this.

[Question] - How to restart animation?

I love your package so much it hides almost the boilerplate part. So declarative animation.
I make this effect but I got an issue about restarting the animation.

An animation I'm trying to replicate this animation.

Currently, my implementation is pretty close but I don't know how to restart animation so It's a bit weird.

My reproduce: https://gist.github.com/definev/d1aacf50122f62e79a956eb7245e495c

Screen.Recording.2022-06-10.at.22.15.17.mov

The UI is separated into two-part:

  • Center: white block.
  • Border: yellow block.
HYNH0059.mp4

My border is regenerating every user increasing depth so it auto animates.
I want to trigger the center to re-animate when changing depth.

Allow Scale Effect for only one Axis

It would be great if the existing Scale effect could be used on only one axis as well, either by allowing (x, y) values be defined for the parameters begin and end or by adding ScaleX and ScaleY effects as done for the Shake effect.

The Flame game engine allows this and therefore e.g. a game card flip effect (which is done solely on the x-axis) is possible with these lines of code:

animalSprite.add(SequenceEffect([
  ScaleEffect.to(Vector2(0, 1), EffectController(duration: 0.2)),
  ScaleEffect.to(Vector2(1, 1), EffectController(duration: 0.2))
]));
backSideSprite.add(ScaleEffect.to(Vector2(0, 1), EffectController(duration: 0.2)));

adaptor driven animations with "delay" and "then" semantics?

I know the adaptor is providing a 0 to 1 value, but how does that map in to the time-based effects like delay or duration or then? It's not obvious to me how to set up what would be a time-based animation and drive it by an adaptor. Or, if that isn't supported, could we get that supported somehow?

Allow pixel values for SlideEffect

In the current SlideEffect implementation, it's possible to set the begin and end offsets only with respect to the widget width and height:

SlideEffect(
  duration: animInfo.duration.ms,
  begin: Offset(-1, 0),
  end: Offset(0, 0),
  curve: animInfo.curve,
)

So, in the above example, if the effect is applied on a widget having height: 100 and width: 100 then beginning Offset(-1, 0) signifies that the slide animation will start from 100 pixels to the left of the widget.

I believe there should be a way to allow setting the begin and end offsets in terms of raw pixel values, so that we can use something like begin Offset(-85, 0) to signify the slide starts from 85 pixels left of the widget.

support slivers

Would be grate if we could do this:

CustomScrollView(
  slivers: [
     ...
  ].animate(interval: 100.ms)
    .move(
      curve: Curves.easeOut,
      duration: 300.ms,
      begin: const Offset(100, 0))
    .fade(duration: 100.ms),
)

Support passing in controller

Could be interesting to support passing in a ValueNotifier, and only instantiate an AnimationController if it is not provided. This would facilitate two things:

  1. Letting the parent widget own creating and destroying the controller at appropriate times.
  2. Support other notifiers (ex. animation on scroll).
foo.animate(controller: myController).etc()

Right now, we update the duration of the controller as new effects are added, so we'd need to decide if this still happens if the controller that's passed in is an AnimationController.

Alternative architecture for variations / presets

The current approach to effect variations / presets has some drawbacks. Use shake as an example.

  • shakeX and shakeY variations exist, but only in the form of method extensions
  • This favors the extension syntax, and does not provide access to these presets using declarative syntax, ie, we can not do: Animate(effects: [ ShakeXEffect() ]). This is confusing / annoying.
  • As more variations are created, this will build up more and more functionality that is closed off from the declarative syntax.
  • It will become a mish-mash of EffectName and effect.variations(), for example, you might have .fadeIn() and FadeUpAndIn both existing. With the class FadeIn not existing... quite weird.

Instead of using extension-method-first approach, a more component-based approach could be adopted. This would standardize the API, and not favor one syntax over the other.

psuedo code, eg:

class FadeEffect {} // core effect, adds .fade() extension

// presets/defaults are defined in the class
class FadeInEffect {
   build() => FadeEffect(begin: 0, end: 1)
}

// extension method just uses the class
extension FadeInEffect on Animate {
   fadeIn() => this.addEffect(FadeInEffect());
}

With this approach the presets are defined at the class level, the extension methods can still exist, but now the declarative form is also unlocked. There will also be no mish-mash, as everything will exist both as a FullEffect and .extensionEffect

Non Breaking

This approach would be non-breaking as the existing extension methods would continue to exist, they would just be augmented an additional syntax for their use.

eg fadeIn() still exists, but now there is also FadeInEffect.

Composition

More complicated presets need to support composition. Not sure exactly how implementation would look, but conceptually we want one effect to be composed of two or more core effects:

class FadeInUpEffect {
   build() => FadeInEffect(
      ...
      child: SlideInUpEffect( ... ));

   // adds .fadeInUp() extension 
}

The fundamental design challenge here is: How can 1 effect use 2 or more other effects? Is this even possible with the current architecture?

Restructure src files

Fairly minor thing, but it would be nice if the lib followed the best practices on file structure:
image

Typically the outer file, provider.dart in this case is the name of the lib, and exports all library classes.

Another provider.dart often exists inside of the src file, defining package level methods etc

So we'd be looking for:

flutter_animate.dart  // barrel file, all exports
/src
   flutter_animate.dart // global stuff like `AnimateManager` and `buildSubAnimation`
   /effects
   /adapters
   etc

How to record animation frames

Hi, Thanks for the awesome library, but just wondering how to capture every frame of the animation as png, so that I can create a movie?

Feature requests

I'm not sure if you have a road map, but I would love to help with some features and learn from you all as a contributor.

What appears to be important to me, based on a project I'm working on, are the items below.

  • JSON Serialization of Effects
  • Dynamic Effects (based on a stateful container that applies the effects)
  • Effects factory to create Effects from a context (JSON)
  • Finite State Machine as an extention to widgets being animated as a child of animate()
  • Understand Global Effects and how to build/use them (it is on the readme, but I couldn't find an example or implementation in the docs)
  • Effect Patterns (solutions) that can be applied, stored, and repeated.

My project is inspired from my Flash days. I'm using components like Stage, Scene, and MovieClip with assets and symbols, as the structure. I'm using that to create a sequential art framework for iOS and Android apps. The list above is on my list for the project.

Something wrong with `buildSubAnimation`

It's hard to produce minimal. You can run my project to get this error.

If I try to decrease the grid cell when the grid cell increase animation has not been completed, it generates this error.

═══════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building Animate-[<'point : 0 | depth : 0 | false'>](state: _AnimateState#0613f(ticker active)):
'package:flutter/src/animation/curves.dart': Failed assertion: line 183 pos 12: 'end <= 1.0': is not true.
package:flutter/…/animation/curves.dart:183
2

Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=2_bug.md

The relevant error-causing widget was
Animate-[<'point : 0 | depth : 0 | false'>]
lib/grid_zoom.dart:554
When the exception was thrown, this was the stack
#2      Interval.transformInternal
package:flutter/…/animation/curves.dart:183
#3      ParametricCurve.transform
package:flutter/…/animation/curves.dart:38
#4      Curve.transform
package:flutter/…/animation/curves.dart:92
#5      CurvedAnimation.value
package:flutter/…/animation/animations.dart:462
#6

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.