how to make this animation on flutter
icon and text
the default state is the icon is shown and the text is disappears
when click the icon: the icon goes up and text goes under icon and appears
otherwise the icon goes in center and the text disappears
like this video
https://i.imgur.com/S0LXr3o.mp4
https://drive.google.com/file/d/1nwpgjOM_6TUaaVaSdsIZp0oYi4CdWSMR/view?usp=sharing
you can use AnimationController and AnimationBuilder combined with Stack + Positioned
or you can even use the Transform Widget with the same concept!
I've write an example to make the animation
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class AnimationExerciseScreen extends StatefulWidget {
const AnimationExerciseScreen({Key? key}) : super(key: key);
#override
_AnimationExerciseScreenState createState() =>
_AnimationExerciseScreenState();
}
class _AnimationExerciseScreenState extends State<AnimationExerciseScreen>
with SingleTickerProviderStateMixin {
#override
void initState() {
super.initState();
animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 3),
);
animationController.forward();
}
#override
void dispose() {
animationController.dispose();
super.dispose();
}
late final AnimationController animationController;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 100,
child: AnimatedBuilder(
animation: animationController,
builder: (context, child) => Stack(
children: [
Positioned(
top: 0 + (40 * animationController.value),
child: Icon(Icons.cloud),
),
Positioned(
bottom: 0 + (40 * animationController.value),
child: Opacity(
opacity: 1 - animationController.value,
child: Text('Cloud'),
),
),
],
),
),
),
],
),
),
);
}
}
video link:
https://imgur.com/RJg5PWw
Explanation:
the animation controller has a value of 0 to 1 with the type of double, which will represent the amount percentage of the animation
in the above example, I'm using 3 seconds duration of the animation controller so the animation will be visible to our eyes easily, so I use animationController.forward to play the animation at the initState
note: the placement of the animation is not optimized for performance, this example is just for example to understand how the animation works
if you want to optimize the animation, you can put your widget to child attribute of the AnimationBuilder for more info you can read them here and here and here and so much more! you can explore tons of articles to improve your flutter app's performance!
Related
With the code below
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) => MaterialApp(
home: const MyHomePage(),
);
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) => DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: const Center(
child: Text('use the mouse wheel to scroll')),
bottom: TabBar(
tabs: const [
Center(child: Text('ScrollView')),
Center(child: Text('PageView'))
],
),
),
body: TabBarView(
children: [
SingleChildScrollView(
child: Column(
children: [
for (int i = 0; i < 10; i++)
Container(
height: MediaQuery.of(context).size.height,
child: const Center(
child: FlutterLogo(size: 80),
),
),
],
),
),
PageView(
scrollDirection: Axis.vertical,
children: [
for (int i = 0; i < 10; ++i)
const Center(
child: FlutterLogo(size: 80),
),
],
),
],
),
),
);
}
You can see, running it on dartpad or from this video,
that using the mouse wheel to scroll a PageView provides a mediocre experience (at best),
This is a known issue #35687 #32120, but I'm trying to find a workaround
to achieve either smooth scrolling for the PageView or at least prevent the "stutter".
Can someone help me out or point me in the right direction?
I'm not sure the issue is with PageScrollPhysics;
I have a gut feeling that the problem might be with WheelEvent
since swiping with multitouch scroll works perfectly
The problem arises from chain of events:
user rotate mouse wheel by one notch,
Scrollable receives PointerSignal and calls jumpTo method,
_PagePosition's jumpTo method (derived from ScrollPositionWithSingleContext) updates scroll position and calls goBallistic method,
requested from PageScrollPhysics simulation reverts position back to initial value, since produced by one notch offset is too small to turn the page,
another notch and process repeated from step (1).
One way to fix issue is perform a delay before calling goBallistic method. This can be done in _PagePosition class, however class is private and we have to patch the Flutter SDK:
// <FlutterSDK>/packages/flutter/lib/src/widgets/page_view.dart
// ...
class _PagePosition extends ScrollPositionWithSingleContext implements PageMetrics {
//...
// add this code to fix issue (mostly borrowed from ScrollPositionWithSingleContext):
Timer timer;
#override
void jumpTo(double value) {
goIdle();
if (pixels != value) {
final double oldPixels = pixels;
forcePixels(value);
didStartScroll();
didUpdateScrollPositionBy(pixels - oldPixels);
didEndScroll();
}
if (timer != null) timer.cancel();
timer = Timer(Duration(milliseconds: 200), () {
goBallistic(0.0);
timer = null;
});
}
// ...
}
Another way is to replace jumpTo with animateTo. This can be done without patching Flutter SDK, but looks more complicated because we need to disable default PointerSignalEvent listener:
import 'dart:async';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class PageViewLab extends StatefulWidget {
#override
_PageViewLabState createState() => _PageViewLabState();
}
class _PageViewLabState extends State<PageViewLab> {
final sink = StreamController<double>();
final pager = PageController();
#override
void initState() {
super.initState();
throttle(sink.stream).listen((offset) {
pager.animateTo(
offset,
duration: Duration(milliseconds: 200),
curve: Curves.ease,
);
});
}
#override
void dispose() {
sink.close();
pager.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Mouse Wheel with PageView'),
),
body: Container(
constraints: BoxConstraints.expand(),
child: Listener(
onPointerSignal: _handlePointerSignal,
child: _IgnorePointerSignal(
child: PageView.builder(
controller: pager,
scrollDirection: Axis.vertical,
itemCount: Colors.primaries.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(color: Colors.primaries[index]),
);
},
),
),
),
),
);
}
Stream<double> throttle(Stream<double> src) async* {
double offset = pager.position.pixels;
DateTime dt = DateTime.now();
await for (var delta in src) {
if (DateTime.now().difference(dt) > Duration(milliseconds: 200)) {
offset = pager.position.pixels;
}
dt = DateTime.now();
offset += delta;
yield offset;
}
}
void _handlePointerSignal(PointerSignalEvent e) {
if (e is PointerScrollEvent && e.scrollDelta.dy != 0) {
sink.add(e.scrollDelta.dy);
}
}
}
// workaround https://github.com/flutter/flutter/issues/35723
class _IgnorePointerSignal extends SingleChildRenderObjectWidget {
_IgnorePointerSignal({Key key, Widget child}) : super(key: key, child: child);
#override
RenderObject createRenderObject(_) => _IgnorePointerSignalRenderObject();
}
class _IgnorePointerSignalRenderObject extends RenderProxyBox {
#override
bool hitTest(BoxHitTestResult result, {Offset position}) {
final res = super.hitTest(result, position: position);
result.path.forEach((item) {
final target = item.target;
if (target is RenderPointerListener) {
target.onPointerSignal = null;
}
});
return res;
}
}
Here is demo on CodePen.
Quite similar but easier to setup:
add smooth_scroll_web ^0.0.4 to your pubspec.yaml
...
dependencies:
...
smooth_scroll_web: ^0.0.4
...
Usage:
import 'package:smooth_scroll_web/smooth_scroll_web.dart';
import 'package:flutter/material.dart';
import 'dart:math'; // only for demo
class Page extends StatefulWidget {
#override
PageState createState() => PageState();
}
class PageState extends State<Page> {
final ScrollController _controller = new ScrollController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("SmoothScroll Example"),
),
body: SmoothScrollWeb(
controller: controller,
child: Container(
height: 1000,
child: ListView(
physics: NeverScrollableScrollPhysics(),
controller: _controller,
children: [
// Your content goes here, thoses children are only for demo
for (int i = 0; i < 100; i++)
Container(
height: 60,
color: Color.fromARGB(1,
Random.secure().nextInt(255),
Random.secure().nextInt(255),
Random.secure().nextInt(255)),
),
],
),
),
),
);
}
}
Thanks you hobbister !
Refer to flutter's issue #32120 on Github.
I know that it has been almost 1.5 year from this question, but I found a way that works smoothly. Maybe this will be very helpful whoever read it. Add a listener to your pageview controller with this code (You can make adjustments on duration or nextPage/animateToPage/jumpToPage etc.):
pageController.addListener(() {
if (pageController.position.userScrollDirection == ScrollDirection.reverse) {
pageController.nextPage(duration: const Duration(milliseconds: 60), curve: Curves.easeIn);
} else if (pageController.position.userScrollDirection == ScrollDirection.forward) {
pageController.previousPage(duration: const Duration(milliseconds: 60), curve: Curves.easeIn);
}
});
The issue is with the user settings, how the end-user has set the scrolling to happen with his mouse. I have a Logitech mouse that allows me to turn on or off the smooth scrolling capability via Logitech Options. When I enable smooth scrolling it works perfectly and scrolls as required but in case of disabling the smooth scroll it gets disabled on the project as well. The behavior is as set by the end-user.
Still, if there's a requirement to force the scroll to smooth scroll than can only be done by setting relevant animations. There's no direct way as of now.
I'm trying to animate a panel coming up from the bottom of the screen. My setup is to have a column with two Containers, then animate the size of the lower Container to get a "sliding up" panel effect. The panel shouldn't be on top of the other so I can't use a stack.
Anyway, the problem is using the SizeTransition. I create an AnimationController to control the sizeFactor of the SizeTransition. The problem is that using any Curves in the animation makes it horribly laggy and I don't know why. Obviously, there must be a way to make this run smoothly but I'm at a loss. Here is a minimal example of what I mean:
class WelcomePage extends StatefulWidget {
#override
_WelcomePageState createState() => _WelcomePageState();
}
class _WelcomePageState extends State<WelcomePage>
with SingleTickerProviderStateMixin {
AnimationController _controller;
#override
void initState() {
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 500),
);
// Setting to curve: Curves.linear here makes it run smoothly but anything else brings us to choppy town
_controller.animateTo(210.0, curve: Curves.ease);
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Expanded(child: Container(color: Colors.blue)),
SizeTransition(
sizeFactor: _controller,
child: Container(
height: 200,
color: Colors.green,
))
],
);
}
}
I was trying to add a button to this page that will (play or pause) the waves animation in the background.
code link: https://github.com/apgapg/flutter_profile/blob/master/lib/page/intro_page.dart
I've tried many things but since I still bad with flutter animations I still without result.
Thanks in advance.
Short answer:
AnimationController _controller = ...;
// To stop animation
_controller.stop();
// To start from beginning
_controller.forward();
// To start from a point other than the very beginning.
_controller.forward(from: 0.8);
Long answer:
I wasn't aware of that code, here is how I did. All you need is Controller.reset() to stop the animation and Controller.repeat() to start it.
However if you need to start the animation just once, use Controller.forward() and Controller.reverse().
void main() => runApp(MaterialApp(home: Scaffold(body: HomePage())));
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
AnimationController _controller;
bool _isPlaying = true;
#override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
lowerBound: 0.3,
duration: Duration(seconds: 3),
)..repeat();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Animation")),
body: Stack(
alignment: Alignment.center,
children: <Widget>[
_buildCircularContainer(200),
_buildCircularContainer(250),
_buildCircularContainer(300),
Align(child: CircleAvatar(backgroundImage: AssetImage("assets/images/profile.png"), radius: 72)),
Align(
alignment: Alignment(0, 0.5),
child: RaisedButton(
child: Text(_isPlaying ? "STOP" : "START"),
onPressed: () {
if (_isPlaying) _controller.reset();
else _controller.repeat();
setState(() => _isPlaying = !_isPlaying);
},
),
),
],
),
);
}
Widget _buildCircularContainer(double radius) {
return AnimatedBuilder(
animation: CurvedAnimation(parent: _controller, curve: Curves.fastLinearToSlowEaseIn),
builder: (context, child) {
return Container(
width: _controller.value * radius,
height: _controller.value * radius,
decoration: BoxDecoration(color: Colors.black54.withOpacity(1 - _controller.value), shape: BoxShape.circle),
);
},
);
}
}
When you directly have access to the AnimationController this snippet will start/stop the animation, wherever it left off.
animationController.isAnimating
? animationController.stop()
: animationController.forward();
Here the .isAnimating property is of type bool and is true when the animationController is animating at the moment. Depending on the result .stop()/.forward() will stop/start the animation respectively.
I need a carousel of widgets which will contain some text and an image per widget plus dot navigation should be shown at the bottom. Plus there should be space between the carousel and dot navigation as I need to add few content and button which will be fixed between the carousel and dot navigation. So I have used Column widget and created two containers one for carousel and other for dot navigation.
Now the issue is when I use below code to change carousel after 5sec I get a weird behavior of dot navigation. The widget change from 1st to 2nd after 5sec the dot navigation goes like this 1 -> 2 -> 1 -> 2. I don't understand why it goes back to 1st dot and comes to 2nd again. This works fine on finger swipe gesture. I need a solution for this weird behavior.
Timer.periodic(new Duration(seconds: 5), (_) {
_controller.animateTo(
_controller.index == _controller.length - 1
? 0
: _controller.index++);
});
Here is the code.
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with TickerProviderStateMixin{
TabController _controller;
Timer _time;
#override
void initState() {
_controller = TabController(length: 5, vsync: this);
_time = Timer.periodic(new Duration(seconds: 5), (_) {
_controller.animateTo(
_controller.index == _controller.length - 1
? 0
: _controller.index++);
});
super.initState();
}
#override
void dispose() {
_controller.dispose();
_time.cancel();
super.dispose();
}
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Column(
children: <Widget>[
Container(
color: Colors.red, height:100.0, width:100.0,
child: DefaultTabController(
length: 5,
child: TabBarView(
controller: _controller,
children: <Widget>[
Container(color: Colors.red, height:100.0, width:100.0),
Container(color: Colors.blue, height:100.0, width:100.0),
Container(color: Colors.green, height:100.0, width:100.0),
Container(color: Colors.yellow, height:100.0, width:100.0),
Container(color: Colors.grey, height:100.0, width:100.0),
],
),
),
),
Container(
child: TabPageSelector(
controller: _controller,
selectedColor: Colors.grey,
color: Colors.white,
),
)
]
)
),
);
}
}
I ran your code but excluded the timer and it worked as it's supposed to, so the problem probably lies in your timer.
It could be that you tell the program to add one frame (: _controller.index++);) and the next time it runs, you tell it to go back one frame ( _controller.length - 1 ? 0).
Now I'm not an expert on this, so don't take my word for granted, but maybe worth a try.
Good luck
I was playing with the fling animation based on the grid demo in Flutter Gallery. I made the example below work, but the animation plays very fast. I could barely see it unless I slow it down by using timeDilation. Changing the value of velocity doesn't seem to have much effect. Should I look at something else? Thanks!
import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart' show timeDilation;
const kLogoUrl =
"https://raw.githubusercontent.com/dart-lang/logos/master/logos_and_wordmarks/dart-logo.png";
class LogoWidget extends StatelessWidget {
// Leave out the height and width so it fills the animating parent
build(BuildContext context) {
return new Container(
margin: new EdgeInsets.symmetric(vertical: 10.0),
child: new Image.network(kLogoUrl));
}
}
class TranslateTransition extends StatelessWidget {
TranslateTransition({this.child, this.animation});
Widget child;
Animation<Offset> animation;
Widget build(BuildContext context) {
return new AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) {
return new Center(
child: new Transform(
transform: new Matrix4.identity()
..translate(animation.value.dx, animation.value.dy),
child: new Container(
height: 100.0,
width: 100.0,
child: child,
),
),
);
},
child: child);
}
}
class LogoApp extends StatefulWidget {
LogoAppState createState() => new LogoAppState();
}
class LogoAppState extends State<LogoApp> with TickerProviderStateMixin {
Animation<Offset> _flingAnimation;
AnimationController _controller;
initState() {
super.initState();
timeDilation = 5.0;
_controller = new AnimationController(
vsync: this,
);
_flingAnimation = new Tween<Offset>(
begin: new Offset(-150.0, -150.0),
end: new Offset(150.0, 150.0),
)
.animate(_controller);
_controller
..value = 0.0
..fling(velocity: 0.1)
..addListener(() {
// print(_flingAnimation.value);
});
}
Widget build(BuildContext context) {
return new TranslateTransition(
child: new LogoWidget(), animation: _flingAnimation);
}
#override
dispose() {
_controller.dispose();
}
}
void main() {
runApp(new LogoApp());
}
fling uses a SpringSimulation with default parameters, one of which is the spring constant. Even if you start with velocity zero, a spring will spring at a speed determined by the spring constant. So what's happening is that you're going from 0.0 to 1.0 with a pretty tight critically-damped string.
Also, because you're using a NetworkImage, you don't see anything because the image takes longer to load than the animation takes to run.
If you replace LogoWidget with FlutterLogo, you'll see what's happening better.
If you want it to go slower, you can use animateWith instead of fling to pass it a specific SpringSimulation with your own custom parameters.
The existence of fling is a bit of a historical accident. It's designed to be used primarily with AnimationControllers with a lowerBound and upperBound given in pixels, rather than over the 0.0...1.0 default range.