I have made one container with scale transition which grows from 0 height and width to 90 height and width.
Now what,I wanted to do is it should slowly fade out as it grows.Do i need to create another animation controller for opacity ? what is the best way to do it ? Can Some one Help?
My Code Look Like This
import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';
void main() => runApp(new MyAnimationApp());
class MyAnimationApp extends StatefulWidget {
#override
_MyAnimationAppState createState() => _MyAnimationAppState();
}
class _MyAnimationAppState extends State<MyAnimationApp>
with TickerProviderStateMixin {
Animation<double> animation;
AnimationController _controller;
#override
void initState() {
super.initState();
_controller =
new AnimationController(vsync: this, duration: Duration(seconds: 3))
..repeat();
animation = new CurvedAnimation(parent: _controller, curve: Curves.linear);
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
body: new Container(
child: new Center(
child: new ScaleTransition(
scale: animation,
child: new Container(
decoration: new BoxDecoration(
color: Color(0XFFEC3457), shape: BoxShape.circle),
height: 90.0,
width: 90.0,
),
),
),
),
),
);
}
}
SS is here
Thanks !!! Hoping for a reply ......
You'd need to add a double value to your code:
double _opacity = 1;
And a listener for your animation controller at the end of the initState
_controller.addListener(() {
setState(() {
_opacity = 1 - _controller.value;
});
});
And on your widget tree, you can add this:
ScaleTransition(
scale: animation,
child: Opacity(
opacity: _opacity,
child: Container(
Related
I wonder how to animate a container widget to leave the screen to the left.
How do I do that?
Thanks!
This is a way you can do it with SlideTransition
class SlideContainerToTheLeft extends StatefulWidget {
#override
_SlideContainerToTheLeftState createState() =>
_SlideContainerToTheLeftState();
}
class _SlideContainerToTheLeftState extends State<SlideContainerToTheLeft>
with SingleTickerProviderStateMixin {
var tween = Tween<Offset>(begin: Offset.zero, end: Offset(-2, 0))
.chain(CurveTween(curve: Curves.ease));
AnimationController animationController;
#override
void initState() {
// TODO: implement initState
super.initState();
animationController =
AnimationController(vsync: this, duration: Duration(seconds: 2));
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SlideTransition(
position: animationController.drive(tween),
child: Container(
width: 300,
height: 400,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20), color: Colors.blue),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
animationController.forward();
},
),
);
}
}
Hope this helps you.
I am quite new to app development and I had a few questions about Flutter I was hoping someone could help me with!
First, I am trying to code an intro-slide part in my code. I have found this code online (https://flutterawesome.com/simple-and-configurable-app-introduction-slider-for-flutter/) and when I tried executing it, using my own images, only the background color seems to print. When I remove the background colors, it is just a white screen. Is my pageImage part correct? I saved an assert folder everywhere, so I'm unsure if that is the problem. I have included my code at the end.
Thank you for your time!
class _MyHomePageState extends State<MyHomePage> {
List<Slide> slides = new List();
#override
void initState() {
super.initState();
slides.add(
new Slide(
title: "ERASER",
description: "Allow miles wound place the leave had. To sitting subject no improve studied limited",
pathImage: "assets/images/1.png",
backgroundColor: Colors.pink[200],
),
);
slides.add(
new Slide(
title: "PENCIL",
description: "Ye indulgence unreserved connection alteration appearance",
pathImage: "assets/images/1.png",
backgroundColor: Colors.blue[200],
),
);
slides.add(
new Slide(
title: "RULER",
description:
"Much evil soon high in hope do view. Out may few northward believing attempted. Yet timed being songs marry one defer men our. Although finished blessing do of",
pathImage: "assets/images/3.jpg",
),
);
}
void onDonePress() {
// TODO: go to next screen
}
void onSkipPress() {
// TODO: go to next screen
}
#override
Widget build(BuildContext context) {
return new IntroSlider(
slides: this.slides,
onDonePress: this.onDonePress,
onSkipPress: this.onSkipPress,
);
}
}
**Solution: edit assets in pubspec page
Edit:
On the left (the orange part) is how I want the blue image to appear: No scrolling and fills the whole page. However, I tried to make my image (on the right) fill the page by editing the width and height and I started having to scroll where there is the pink background below and above the image (I assume it is because it keeps having to center the image).
Is there any way to make my image my background so it is like the picture on the left? I understand the orange color background is the background color, but hopefully, by comparing the two it makes sense. Thank you!
I create new intro widget. Here is the code.
import 'package:flutter/material.dart';
class MyIntroView extends StatefulWidget {
final List<Widget> pages;
final VoidCallback onIntroCompleted;
const MyIntroView({
Key key,
#required this.pages,
#required this.onIntroCompleted,
}) : assert(pages != null),
assert(onIntroCompleted != null),
super(key: key);
#override
_MyIntroViewState createState() => _MyIntroViewState();
}
class _MyIntroViewState extends State<MyIntroView> {
PageController _pageController;
int _currentPage = 0;
#override
void initState() {
_pageController = PageController(
initialPage: _currentPage,
);
super.initState();
}
#override
void dispose() {
_pageController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
NotificationListener<ScrollEndNotification>(
onNotification: (x) {
setState(() {
_currentPage = _pageController.page.round();
});
return false;
},
child: PageView(
children: widget.pages,
controller: _pageController,
),
),
Align(
alignment: Alignment.bottomCenter,
child: _buildBottomButtons(),
),
],
);
}
bool get _isFinalPage => _currentPage == widget.pages.length - 1;
Widget _buildBottomButtons() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Opacity(
opacity: _isFinalPage ? 0.0 : 1.0,
child: _buildButton("SKIP", _gotoLastPage),
),
_buildNavIndicator(),
_isFinalPage
? _buildButton("DONE", widget.onIntroCompleted)
: _buildButton("NEXT", _gotoNextPage),
],
),
);
}
Widget _buildButton(String title, VoidCallback callback) {
return FlatButton(
child: Text(
title,
style: TextStyle(color: Colors.white),
),
onPressed: callback,
);
}
void _gotoLastPage() {
_pageController.animateToPage(
widget.pages.length - 1,
duration: const Duration(milliseconds: 600),
curve: Curves.ease,
);
}
void _gotoNextPage() {
_pageController.nextPage(
duration: const Duration(milliseconds: 600),
curve: Curves.easeInOut,
);
}
Widget _buildNavIndicator() {
final indicatorList = <Widget>[];
for (int i = 0; i < widget.pages.length; i++)
indicatorList.add(_buildIndicator(i == _currentPage));
return Row(children: indicatorList);
}
Widget _buildIndicator(bool isActive) {
return Padding(
padding: const EdgeInsets.all(5.0),
child: DecoratedBox(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isActive ? Colors.white : Colors.white30,
),
child: SizedBox(width: 8, height: 8),
),
);
}
}
Usage:
import 'package:flutter/material.dart';
import 'package:flutter_app_test3/my_intro_view.dart';
Future<void> main() async {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MyIntroView(
pages: <Widget>[
Image.asset("assets/images/1.png", fit: BoxFit.cover),
Image.asset("assets/images/2.png", fit: BoxFit.cover),
Image.asset("assets/images/3.jpg", fit: BoxFit.cover),
],
onIntroCompleted: () {
print("Into is Completed");
//To the navigation stuff here
},
);
}
}
Ask me if you have any doubts in the comment
just try wrapping your Widget into Scaffold Widget and return
#override
Widget build(BuildContext context) {
return Scaffold(body:IntroSlider(
slides: this.slides,
onDonePress: this.onDonePress,
onSkipPress: this.onSkipPress,
));
}
I was facing the same issue and I fixed it by setting fit:Boxfit.fill for the image.
I have a button with a text, and when I pressed the button, a text widget with the same text is added to the same page.
I'd like to add Hero like animation between them.
I guess what I need is SlideTransition, but I don't know how to slide from one widget position to another widget position.
Is it possible to do? What widget (or class) should I look into?
Here's the code I want to do (but doesn't work since Hero doesn't work on the same page widgets):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> text = [];
String buttonTag = "0";
#override
Widget build(BuildContext context) {
List<Widget> textWidgets = [];
for (int i = 0; i < text.length; ++i) {
textWidgets.add(
Padding(
padding: const EdgeInsets.all(8.0),
child: Hero(tag: "${i}", child: Text(text[i])),
)
);
}
return SafeArea(
child: Scaffold(
body: Center(
child: Column(
children: <Widget>[
RaisedButton(
child: Hero(
tag: buttonTag,
child: Text("abcde${text.length}")),
onPressed: () {
setState(() {
text.add("abcde${text.length}");
buttonTag = "${text.length}";
});
},
)
] + textWidgets,
),
),
),
);
}
}
Not exactly the answer to the question but instead of Hero (if it's possible) you can use AnimatedList to get the same result.
Code snippet
import 'package:flutter/material.dart';
import 'dart:math';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
final List<Text> _textWidgets = [];
var rng = new Random();
_addItem() {
setState(() {
_listKey.currentState.insertItem(_textWidgets.length,
duration: const Duration(milliseconds: 500));
int id = rng.nextInt(100);
_textWidgets.add(Text('item $id'));
});
}
Widget _buildItem(
BuildContext context, Text item, Animation<double> animation) {
final offsetAnimation = Tween<Offset>(
begin: Offset(1.0, 0.0),
end: Offset(0.0, 0.0),
).animate(animation);
return SlideTransition(
position: offsetAnimation,
child: SizedBox(
height: 50.0,
child: Center(
child: item,
),
),
);
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Column(
children: <Widget>[
RaisedButton(
child: Text("Add Item"),
onPressed: () {
setState(() {
_addItem();
});
},
),
Expanded(
child: AnimatedList(
key: _listKey,
initialItemCount: _textWidgets.length,
itemBuilder: (context, index, animation) {
return _buildItem(context, _textWidgets[index], animation);
},
),
),
],
),
),
);
}
}
I think it's possible by this llibrary which named: LocalHero
Implement your self
The AnimationController, Tween, AnimatedBuilder are key components.
This is a sample and code about this.
Don't use AnimatedController.animate in AnimatedBuilder builder.
evaluate is enough. Because the builder function is called every ticker frame.
Use AnimationController.animate as class member field.
class _AuthorizedState extends State<_Authorized> with SingleTickerProviderStateMixin {
late final _menuAC = AnimationController(vsync: this, duration: 200.ms);
late final isFilterOpen = ValueNotifier(false)..addListener(_handleFilterOpenChanged);
late final filterColor =
ColorTween(begin: context.color.primaryContainer, end: context.color.secondaryContainer)
.animate(_menuAC);
late final filterBorderRadius = Tween<double>(begin: 12, end: 0).animate(_menuAC);
void _handleFilterOpenChanged() {
print(isFilterOpen.value);
if (isFilterOpen.value) {
_menuAC.forward();
} else {
_menuAC.reverse();
}
}
#override
void dispose() {
_menuAC.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return AppScaffold(
child: LayoutBuilder(
builder: (context, cons) => Stack(
children: [
AnimatedBuilder(
animation: _menuAC,
builder: (context, child) {
return Positioned(
bottom: Tween(begin: 16.0, end: 0.0).evaluate(_menuAC),
right: Tween(begin: 16.0, end: 0.0).evaluate(_menuAC),
child: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(12.0),
topRight: const Radius.circular(12.0),
bottomLeft:
Radius.circular(Tween<double>(begin: 12, end: 0).evaluate(_menuAC)),
bottomRight:
Radius.circular(Tween<double>(begin: 12, end: 0).evaluate(_menuAC)),
),
child: Container(
width: Tween(begin: 56.0, end: cons.maxWidth).evaluate(_menuAC),
height: Tween(begin: 56.0, end: min(cons.maxHeight * 0.7, 500.0))
.evaluate(_menuAC),
decoration: BoxDecoration(
color: filterColor.value,
),
child: child,
),
),
);
},
child: Material(
elevation: 24,
color: Colors.transparent,
child: InkWell(
onTap: () {
isFilterOpen.value = !isFilterOpen.value;
},
child: const Center(child: Icon(MdiIcons.filter)),
),
),
),
],
),
),
);
}
}
In some Flutter animation tutorials, some uses a Tween and an Animation object. Some uses AnimationController only. Both code below seems to output the same result. So when do we use a Tween with animation and when do we use AnimationController only?
With Tween and animation
import 'dart:core';
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
#override
_State createState() {
return _State();
}
}
class _State extends State<Test> with TickerProviderStateMixin {
AnimationController _animationController;
Animation _animation;
bool faded = true;
#override
void initState() {
super.initState();
_animationController = new AnimationController(
value:0.0,
vsync: this,
upperBound: 1.0,
lowerBound: 0.0,
duration: new Duration(seconds:1),
);
_animation = Tween(begin: 0.0, end: 1.0).animate(_animationController);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
elevation: 0.5,
title: new Text(
"Testing views",
style: Theme.of(context).textTheme.title,
),
),
body: _buildBodyAnimationTest(),
// body: _buildTuto(),
);
}
Widget _buildBodyAnimationTest(){
return FadeTransition(
opacity: _animation, //here is the difference
child: InkWell(
onTap: (){
if(faded){
faded = false;
_animationController.reverse();
}else{
faded = true;
_animationController.forward();
}
},
child: new Container(
color: Colors.red,
),
),
);
}
}
Without Tween and Animation
import 'dart:core';
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
#override
_State createState() {
return _State();
}
}
class _State extends State<Test> with TickerProviderStateMixin {
AnimationController _animationController;
bool faded = true;
#override
void initState() {
super.initState();
_animationController = new AnimationController(
value:0.0,
vsync: this,
upperBound: 1.0,
lowerBound: 0.0,
duration: new Duration(seconds:1),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
elevation: 0.5,
title: new Text(
"Testing views",
style: Theme.of(context).textTheme.title,
),
),
body: _buildBodyAnimationTest(),
// body: _buildTuto(),
);
}
Widget _buildBodyAnimationTest(){
return FadeTransition(
opacity: _animationController, //here is the difference
child: InkWell(
onTap: (){
if(faded){
faded = false;
_animationController.reverse();
}else{
faded = true;
_animationController.forward();
}
},
child: new Container(
color: Colors.red,
),
),
);
}
}
Tweens are objects used to transform the output of an Animation (such as AnimationController).
With AnimationController, you typically have a 0-1 floating value. But you rarely want that.
Tweens allows to map that 0-1 to something more concrete such as red to blue, left to right, ...
Background is my tween sequence color.
Animatable<Color> background = TweenSequence<Color>(
[
TweenSequenceItem(
weight: 1.0,
tween: ColorTween(
begin: colors[_Substance.dayEndBackground],
end: colors[_Substance.dayStartBackground],
),
),
TweenSequenceItem(
weight: 1.0,
tween: ColorTween(
begin: colors[_Substance.dayStartBackground],
end: colors[_Substance.dayEndBackground],
),
),
],
);
This is my controller defined in initState() and updated in every one second:
bgUpdateController = AnimationController(
value: _currentDateTime.hour / 24,
upperBound: 1,
lowerBound: 0,
duration: const Duration(hours: 24),
vsync: this,
)..repeat();
I have used the above background and controller as AnimatedBuilder like below:
AnimatedBuilder(
animation: bgUpdateController,
builder: (context, child) {
return Scaffold(
backgroundColor: background
.chain(
CurveTween(curve: Curves.easeInOutCirc),
)
.evaluate(
AlwaysStoppedAnimation(bgUpdateController.value),
),
...
and the result of my code is:
Conclusion
AnimationController is for how long the animation would be and how to control from time, upper and lower boundary, how to control data with time, length, sequence, etc. while AnimationTween is for the range of animation with time, colour, range, sequence, etc as long the animation would be while.
I want to animate my container height after launching the application (after the widget tree is rendered and shown on screen). For example, animate height from 86 to 210.
What I have tried:
class MyAppState extends State<HomePage> with TickerProviderStateMixin {
double appBarHeight = 86.0;
#override
void initState() {
super.initState();
WidgetsBinding.instance
.addPostFrameCallback((_) => animate());
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
AnimatedContainer(
curve: Curves.fastOutSlowIn,
child: Container(
color: Colors.red,
),
height: appBarHeight,
duration: Duration(milliseconds: 400),
),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(right: 16.0, bottom: 16.0),
child: FloatingActionButton(
child: Icon(Icons.brush),
onPressed: () {
animate();
},
),
))
],
),
);
}
void animate(){
setState(() {
if (appBarHeight == 210.0) appBarHeight = 86.0;
else appBarHeight = 210.0;
});
}
}
But it does not work, because widget is animated before appearing on screen. I see white screen while app is launching and then my widget appears on screen with a final height.
In Android for this purpose we can use addOnLayoutChangeListener().
Is there an analogue of addOnLayoutChangeListener() in Flatter?
As you mentioned, looks like it's an issue on Android, I tested on iOS and the animation runs at the beginning.
You can try this workaround :
_startAnimation(_) async {
await Future.delayed(Duration(milliseconds: 200));
animate();
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback(_startAnimation);
}