diff --git a/packages/auto_animated/CHANGELOG.md b/packages/auto_animated/CHANGELOG.md
index 28adac52..c33f137f 100644
--- a/packages/auto_animated/CHANGELOG.md
+++ b/packages/auto_animated/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.4.0-dev.1
+
+* Added `AnimateOnVisibilityChange` & `AnimateOnVisibilityWrapper`
+
## 1.3.0
* Fixed lexical error in `AutoAnimatedIconButton` (`firstToolip` & `secondToolip` renamed to `firstTooltip` & secondTooltip)
diff --git a/packages/auto_animated/README.md b/packages/auto_animated/README.md
index 155797c4..b9bef614 100644
--- a/packages/auto_animated/README.md
+++ b/packages/auto_animated/README.md
@@ -7,6 +7,7 @@ Already added:
- `AutoAnimatedGrid`
- `AutoAnimatedSliverGrid`
- `AutoAnimatedIconButton`
+- `AnimateOnVisibilityChange`
## Screenshots
@@ -21,7 +22,7 @@ In your flutter project add the dependency:
[![pub package](https://img.shields.io/pub/v/auto_animated.svg)](https://pub.dartlang.org/packages/auto_animated)
-```dart
+```yaml
dependencies:
...
auto_animated: any
diff --git a/packages/auto_animated/example/lib/main.dart b/packages/auto_animated/example/lib/main.dart
index 45130893..c0e712f9 100644
--- a/packages/auto_animated/example/lib/main.dart
+++ b/packages/auto_animated/example/lib/main.dart
@@ -1,3 +1,4 @@
+import 'package:auto_animated_example/screens/animate_on_visibility.dart';
import 'package:auto_animated_example/screens/grid.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -29,6 +30,7 @@ class _MyAppState extends State {
AutoAnimatedGridExample(),
SliverExample(),
AutoAnimatedIconButtonExample(),
+ AnimateOnVisibilityExample(),
];
void _onItemTapped(int index) {
@@ -66,6 +68,10 @@ class _MyAppState extends State {
icon: Icon(Icons.check_circle),
title: Text('IconButton'),
),
+ BottomNavigationBarItem(
+ icon: Icon(Icons.remove_red_eye),
+ title: Text('On visibility'),
+ ),
],
currentIndex: _selectedIndex,
// selectedItemColor: Colors.amber[800],
diff --git a/packages/auto_animated/example/lib/screens/animate_on_visibility.dart b/packages/auto_animated/example/lib/screens/animate_on_visibility.dart
new file mode 100644
index 00000000..2f5f4da1
--- /dev/null
+++ b/packages/auto_animated/example/lib/screens/animate_on_visibility.dart
@@ -0,0 +1,42 @@
+import 'package:auto_animated_example/utils.dart';
+import 'package:flutter/material.dart';
+import 'package:auto_animated/auto_animated.dart';
+
+class AnimateOnVisibilityExample extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ final textStyle =
+ Theme.of(context).textTheme.title.copyWith(color: Colors.black);
+
+ return Scaffold(
+ body: SafeArea(
+ // Wrapper before Scroll view!
+ child: AnimateOnVisibilityWrapper(
+ showItemInterval: Duration(milliseconds: 150),
+ child: SingleChildScrollView(
+ child: Column(
+ children: [
+ SizedBox(height: 16),
+ Text('Animate elements on visibility', style: textStyle),
+ for (int i = 0; i < 20; i++)
+ AnimateOnVisibilityChange(
+ key: Key('$i'),
+ builder: animationBuilder(
+ SizedBox(
+ width: double.infinity,
+ height: 200,
+ child: HorizontalItem(
+ title: '$i',
+ ),
+ ),
+ xOffset: i.isEven ? 0.15 : -0.15,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/auto_animated/example/lib/utils.dart b/packages/auto_animated/example/lib/utils.dart
index 6a022325..46628972 100644
--- a/packages/auto_animated/example/lib/utils.dart
+++ b/packages/auto_animated/example/lib/utils.dart
@@ -81,3 +81,27 @@ Widget Function(
),
),
);
+
+Widget Function(
+ BuildContext context,
+ Animation animation,
+) animationBuilder(Widget child, {double xOffset = 0}) => (
+ BuildContext context,
+ Animation animation,
+ ) =>
+ FadeTransition(
+ opacity: Tween(
+ begin: 0,
+ end: 1,
+ ).animate(animation),
+ child: SlideTransition(
+ position: Tween(
+ begin: Offset(xOffset, 0.1),
+ end: Offset.zero,
+ ).animate(animation),
+ child: Padding(
+ padding: EdgeInsets.all(16),
+ child: child,
+ ),
+ ),
+ );
diff --git a/packages/auto_animated/example/pubspec.lock b/packages/auto_animated/example/pubspec.lock
index b91e3f3d..b601101e 100644
--- a/packages/auto_animated/example/pubspec.lock
+++ b/packages/auto_animated/example/pubspec.lock
@@ -14,7 +14,7 @@ packages:
path: ".."
relative: true
source: path
- version: "1.3.0"
+ version: "1.4.0-dev.1"
boolean_selector:
dependency: transitive
description:
@@ -36,6 +36,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.14.11"
+ csslib:
+ dependency: transitive
+ description:
+ name: csslib
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.16.1"
cupertino_icons:
dependency: "direct main"
description:
@@ -53,6 +60,20 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ flutter_widgets:
+ dependency: transitive
+ description:
+ name: flutter_widgets
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.1.7+1"
+ html:
+ dependency: transitive
+ description:
+ name: html
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.14.0+3"
matcher:
dependency: transitive
description:
@@ -150,4 +171,5 @@ packages:
source: hosted
version: "2.0.8"
sdks:
- dart: ">=2.2.2 <3.0.0"
+ dart: ">=2.3.0 <3.0.0"
+ flutter: ">=1.9.1 <2.0.0"
diff --git a/packages/auto_animated/example/pubspec.yaml b/packages/auto_animated/example/pubspec.yaml
index 0c57674c..075b3b0e 100644
--- a/packages/auto_animated/example/pubspec.yaml
+++ b/packages/auto_animated/example/pubspec.yaml
@@ -4,7 +4,7 @@ version: 1.0.0
publish_to: 'none'
environment:
- sdk: ">=2.1.0 <3.0.0"
+ sdk: ">=2.3.0 <3.0.0"
dependencies:
flutter:
diff --git a/packages/auto_animated/lib/auto_animated.dart b/packages/auto_animated/lib/auto_animated.dart
index 2b51b474..567102b4 100644
--- a/packages/auto_animated/lib/auto_animated.dart
+++ b/packages/auto_animated/lib/auto_animated.dart
@@ -3,5 +3,6 @@ export 'src/icon_button.dart';
export 'src/list.dart';
export 'src/list_animation.dart'
show AutoAnimatedListItemBuilder, AutoAnimatedListRemovedItemBuilder;
+export 'src/on_visibility_change.dart';
export 'src/sliver_grid.dart';
export 'src/sliver_list.dart';
diff --git a/packages/auto_animated/lib/src/on_visibility_change.dart b/packages/auto_animated/lib/src/on_visibility_change.dart
new file mode 100644
index 00000000..532cb69e
--- /dev/null
+++ b/packages/auto_animated/lib/src/on_visibility_change.dart
@@ -0,0 +1,219 @@
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_widgets/flutter_widgets.dart';
+
+typedef AutoAnimatedBuilder = Widget Function(
+ BuildContext context,
+ Animation animation,
+);
+
+class AnimateOnVisibilityChange extends StatefulWidget {
+ const AnimateOnVisibilityChange({
+ @required Key key,
+ @required this.builder,
+ this.duration = const Duration(milliseconds: 300),
+ this.delay = Duration.zero,
+ }) : super(key: key);
+
+ final AutoAnimatedBuilder builder;
+ final Duration duration, delay;
+
+ @override
+ _AnimateOnVisibilityChangeState createState() =>
+ _AnimateOnVisibilityChangeState();
+}
+
+class _AnimateOnVisibilityChangeState extends State
+ with SingleTickerProviderStateMixin {
+ AnimationController _controller;
+ _VisibilityStackProvider _wrapper;
+
+ @override
+ void initState() {
+ _wrapper = _VisibilityStackProvider.of(context);
+ _controller = AnimationController(
+ duration: widget.duration,
+ vsync: this,
+ );
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ _controller.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) => VisibilityDetector(
+ key: widget.key,
+ child: widget.builder(
+ context,
+ _controller.view,
+ ),
+ onVisibilityChanged: _visibilityChanged,
+ );
+
+ void _visibilityChanged(VisibilityInfo info) {
+ if (_controller.isAnimating) {
+ return;
+ }
+ if (info.visibleFraction > 0.025 && !_controller.isCompleted) {
+ Future.delayed(widget.delay, () {
+ if (!mounted) {
+ return;
+ }
+ if (_wrapper != null) {
+ _wrapper.stack.add(() {
+ _controller.forward();
+ });
+ } else {
+ _controller.forward();
+ }
+ });
+ } else if (info.visibleFraction <= 0.025 && mounted) {
+ _controller.reverse();
+ }
+ }
+}
+
+class AnimateOnVisibilityWrapper extends StatefulWidget {
+ const AnimateOnVisibilityWrapper({
+ @required this.child,
+ this.showItemInterval = const Duration(milliseconds: 150),
+ Key key,
+ }) : super(key: key);
+
+ final Widget child;
+ final Duration showItemInterval;
+
+ @override
+ _AnimateOnVisibilityWrapperState createState() =>
+ _AnimateOnVisibilityWrapperState();
+}
+
+class _AnimateOnVisibilityWrapperState
+ extends State {
+ _VisibilityStack _stack;
+ double _lastScrollExtend = 0;
+
+ @override
+ void initState() {
+ _stack = _VisibilityStack(widget.showItemInterval);
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ _stack.dispose();
+ super.dispose();
+ }
+
+ @override
+ void didUpdateWidget(AnimateOnVisibilityWrapper oldWidget) {
+ if (oldWidget.showItemInterval != widget.showItemInterval) {
+ _stack.showItemInterval = widget.showItemInterval;
+ }
+ super.didUpdateWidget(oldWidget);
+ }
+
+ @override
+ Widget build(BuildContext context) => _VisibilityStackProvider(
+ stack: _stack,
+ child: NotificationListener(
+ child: widget.child,
+ onNotification: _onScroll,
+ ),
+ );
+
+ bool _onScroll(ScrollNotification scrollInfo) {
+ if (scrollInfo.metrics.pixels > _lastScrollExtend + 2.5) {
+ // to end
+ _stack.direction = AnimationDirection.toEnd;
+ } else if (scrollInfo.metrics.pixels < _lastScrollExtend - 2.5) {
+ // to start
+ _stack.direction = AnimationDirection.toStart;
+ }
+ _lastScrollExtend = scrollInfo.metrics.pixels;
+ return true;
+ }
+}
+
+class _VisibilityStackProvider extends InheritedWidget {
+ _VisibilityStackProvider({
+ @required Widget child,
+ @required this.stack,
+ Key key,
+ }) : assert(child != null),
+ super(key: key, child: child);
+
+ final _VisibilityStack stack;
+
+ static _VisibilityStackProvider of(BuildContext context) => context
+ .ancestorInheritedElementForWidgetOfExactType(_VisibilityStackProvider)
+ ?.widget;
+
+ @override
+ bool updateShouldNotify(_VisibilityStackProvider oldWidget) =>
+ oldWidget.stack != stack;
+}
+
+class _VisibilityStack {
+ _VisibilityStack(this.showItemInterval);
+
+ Duration showItemInterval;
+ AnimationDirection direction = AnimationDirection.toEnd;
+ bool _isAnimated = false;
+
+ final List _stack = [];
+
+ void add(VoidCallback callback) {
+ _stack.add(callback);
+ _animate();
+ }
+
+ void _show() {
+ if (direction == AnimationDirection.toEnd) {
+ _stack
+ ..first.call()
+ ..removeAt(0);
+ } else {
+ _stack
+ ..last.call()
+ ..removeLast();
+ }
+ }
+
+ void _animate() {
+ if (_isAnimated) {
+ return;
+ }
+ _isAnimated = true;
+
+ Future.delayed(showItemInterval, () {
+ if (_stack.isNotEmpty) {
+ _show();
+ _isAnimated = false;
+ _animate();
+ } else {
+ _isAnimated = false;
+ }
+ });
+ }
+
+ void dispose() {}
+
+ @override
+ bool operator ==(Object o) =>
+ o is _VisibilityStack && showItemInterval == o.showItemInterval;
+
+ @override
+ int get hashCode => showItemInterval.hashCode;
+}
+
+enum AnimationDirection {
+ toStart,
+ toEnd,
+}
diff --git a/packages/auto_animated/pubspec.lock b/packages/auto_animated/pubspec.lock
index aebdafa4..3e26d855 100644
--- a/packages/auto_animated/pubspec.lock
+++ b/packages/auto_animated/pubspec.lock
@@ -29,6 +29,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.14.11"
+ csslib:
+ dependency: transitive
+ description:
+ name: csslib
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.16.1"
flutter:
dependency: "direct main"
description: flutter
@@ -39,6 +46,20 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ flutter_widgets:
+ dependency: "direct main"
+ description:
+ name: flutter_widgets
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.1.7+1"
+ html:
+ dependency: transitive
+ description:
+ name: html
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.14.0+3"
matcher:
dependency: transitive
description:
@@ -136,4 +157,5 @@ packages:
source: hosted
version: "2.0.8"
sdks:
- dart: ">=2.2.2 <3.0.0"
+ dart: ">=2.3.0 <3.0.0"
+ flutter: ">=1.9.1 <2.0.0"
diff --git a/packages/auto_animated/pubspec.yaml b/packages/auto_animated/pubspec.yaml
index 6a5486b5..a665a61c 100644
--- a/packages/auto_animated/pubspec.yaml
+++ b/packages/auto_animated/pubspec.yaml
@@ -1,6 +1,6 @@
name: auto_animated
description: Widgets starting auto play animation when mounted. It is already possible to animate the list and icons.
-version: 1.3.0
+version: 1.4.0-dev.1
author: Serge Shkurko
homepage: https://github.com/rbcprolabs/packages.flutter/tree/master/packages/auto_animated
@@ -10,6 +10,7 @@ environment:
dependencies:
flutter:
sdk: flutter
+ flutter_widgets: ^0.1.7+1
dev_dependencies:
flutter_test:
diff --git a/packages/native_pdf_renderer/README.md b/packages/native_pdf_renderer/README.md
index ab8872cb..dc861a76 100644
--- a/packages/native_pdf_renderer/README.md
+++ b/packages/native_pdf_renderer/README.md
@@ -9,7 +9,7 @@ In your flutter project add the dependency:
[![pub package](https://img.shields.io/pub/v/native_pdf_renderer.svg)](https://pub.dartlang.org/packages/native_pdf_renderer)
-```dart
+```yaml
dependencies:
...
native_pdf_renderer: any