I'm trying to make a flip card, what would be the best way to get the effect
I would use an AnimatedBuilder or AnimatedWidget to animate the values of a Transform widget. ScaleTransition almost does this for you, but it scales both directions, and you only want one.
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePageState createState() => new MyHomePageState();
}
class MyCustomCard extends StatelessWidget {
MyCustomCard({ this.colors });
final MaterialColor colors;
Widget build(BuildContext context) {
return new Container(
alignment: FractionalOffset.center,
height: 144.0,
width: 360.0,
decoration: new BoxDecoration(
color: colors.shade50,
border: new Border.all(color: new Color(0xFF9E9E9E)),
),
child: new FlutterLogo(size: 100.0, colors: colors),
);
}
}
class MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
AnimationController _controller;
Animation<double> _frontScale;
Animation<double> _backScale;
#override
void initState() {
super.initState();
_controller = new AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
_frontScale = new Tween(
begin: 1.0,
end: 0.0,
).animate(new CurvedAnimation(
parent: _controller,
curve: new Interval(0.0, 0.5, curve: Curves.easeIn),
));
_backScale = new CurvedAnimation(
parent: _controller,
curve: new Interval(0.5, 1.0, curve: Curves.easeOut),
);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
ThemeData theme = Theme.of(context);
return new Scaffold(
appBar: new AppBar(),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.flip_to_back),
onPressed: () {
setState(() {
if (_controller.isCompleted || _controller.velocity > 0)
_controller.reverse();
else
_controller.forward();
});
},
),
body: new Center(
child: new Stack(
children: <Widget>[
new AnimatedBuilder(
child: new MyCustomCard(colors: Colors.orange),
animation: _backScale,
builder: (BuildContext context, Widget child) {
final Matrix4 transform = new Matrix4.identity()
..scale(1.0, _backScale.value, 1.0);
return new Transform(
transform: transform,
alignment: FractionalOffset.center,
child: child,
);
},
),
new AnimatedBuilder(
child: new MyCustomCard(colors: Colors.blue),
animation: _frontScale,
builder: (BuildContext context, Widget child) {
final Matrix4 transform = new Matrix4.identity()
..scale(1.0, _frontScale.value, 1.0);
return new Transform(
transform: transform,
alignment: FractionalOffset.center,
child: child,
);
},
),
],
),
),
);
}
}
I used simple approach, rotated it on X axis. Here is the full code.
void main() => runApp(MaterialApp(home: HomePage()));
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
AnimationController _controller;
bool _flag = true;
Color _color = Colors.blue;
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: Duration(seconds: 1), value: 1);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.crop_rotate),
onPressed: () async {
if (_flag) {
await _controller.reverse();
setState(() {
_color = Colors.orange;
});
await _controller.forward();
} else {
await _controller.reverse();
setState(() {
_color = Colors.blue;
});
await _controller.forward();
}
_flag = !_flag;
},
),
body: Center(
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Transform(
transform: Matrix4.rotationX((1 - _controller.value) * math.pi / 2),
alignment: Alignment.center,
child: Container(
height: 100,
margin: EdgeInsets.symmetric(horizontal: 20),
padding: EdgeInsets.symmetric(vertical: 12),
alignment: Alignment.center,
decoration: BoxDecoration(color: _color.withOpacity(0.2), border: Border.all(color: Colors.grey)),
child: FlutterLogo(colors: _color, size: double.maxFinite),
),
);
},
),
),
);
}
}
You can use the flip_card Flutter package. It lets you define a front and back widget and can be flipped horizontally or vertically.
Related
I am trying to move the container on the screen by giving begin and end offset like from Offset(0.0,0.0) to Offset(400.0,300.0). I am using Slide Transition to animate the container I am using Tween<Offset>(begin: const Offset(3.0, 4.0), end: Offset(0.0, 0.0)) to move it on the screen I want to pass these Offset(400.0,300.0) and animate it.
Here is my code
class MoveContainer extends StatefulWidget {
MoveContainer({Key key, }) : super(key: key);
#override
State<StatefulWidget> createState() {
return new _MyMoveContainer();
}
}
class _MyMoveContainer extends State<MoveContainer>
with TickerProviderStateMixin {
GlobalKey _globalKey = new GlobalKey();
AnimationController _controller;
Animation<Offset> _offset;
Offset local;
#override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 3),
);
_offset =
Tween<Offset>(begin: const Offset(3.0, 4.0), end: Offset(0.0, 0.0))
.animate(_controller);
_offset.addListener(() {
setState(() {});
});
_controller.forward();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SlideTransition(
position: _offset,
child: GestureDetector(
onPanStart: (start) {
RenderBox getBox = context.findRenderObject();
local = getBox.localToGlobal(start.globalPosition);
print('point are $local');
},
child: Container(
color: Colors.cyan,
height: 200.0,
width: 200.0,
child: Text("hello ")),
),
);
}
}
Probably this question is not actual for the author. (Asked 7 months ago).
But maybe my answer will help someone else.
Usually Slide Transition is used for transitions between pages. That is why, one unit of position value here is the size of one page. When you put there Offset(400.0,300.0) it's equal 400 screen right, and 300 pages down.
For your case it better to use AnimatedPositioned Widget.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
backgroundColor: Colors.blue,
body: MoveContainer(),
),
);
}
}
class MoveContainer extends StatefulWidget {
#override
_MoveContainerState createState() => _MoveContainerState();
}
class _MoveContainerState extends State<MoveContainer> {
Offset offset = Offset.zero;
final double height = 200;
final double width = 200;
#override
Widget build(BuildContext context) {
return GestureDetector(
onPanStart: (details) {
RenderBox getBox = context.findRenderObject();
setState(() {
offset = getBox.localToGlobal(details.globalPosition);
});
},
child: Stack(
children: <Widget>[
AnimatedPositioned(
duration: Duration(milliseconds: 300),
top: offset.dy - (height / 2),
left: offset.dx - (width / 2),
child: Container(
color: Colors.cyan,
height: height,
width: width,
child: Text("hello "),
),
),
],
),
);
}
}
I need shaking animation like this video
I'm newbie to Flutter. I would appreciate a solution or a link to the tutorial.
I think there can be better solution. But this one works fine, maybe it'll help
class TestAnimWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() => _TestAnimWidgetState();
}
class _TestAnimWidgetState extends State<TestAnimWidget> with SingleTickerProviderStateMixin {
final TextEditingController textController = TextEditingController();
AnimationController controller;
#override
void initState() {
controller = AnimationController(duration: const Duration(milliseconds: 500), vsync: this);
super.initState();
}
#override
Widget build(BuildContext context) {
final Animation<double> offsetAnimation =
Tween(begin: 0.0, end: 24.0).chain(CurveTween(curve: Curves.elasticIn)).animate(controller)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
}
});
return Scaffold(
appBar: AppBar(),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedBuilder(
animation: offsetAnimation,
builder: (buildContext, child) {
if (offsetAnimation.value < 0.0) print('${offsetAnimation.value + 8.0}');
return Container(
margin: EdgeInsets.symmetric(horizontal: 24.0),
padding: EdgeInsets.only(left: offsetAnimation.value + 24.0, right: 24.0 - offsetAnimation.value),
child: Center(child: TextField(controller: textController, )),
);
}),
RaisedButton(onPressed: () {
if (textController.value.text.isEmpty) controller.forward(from: 0.0);
},
child: Text('Enter'),)
],
),
);
}
}
I'm attempting to create a draggable slider-like widget (like a confirm slider). My question is if there is a way to constrain the draggable area?
import 'package:flutter/material.dart';
import 'confirmation_slider.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
body: new ListView(
children: <Widget>[
new Container(
margin: EdgeInsets.only(
top: 50.0
),
),
new Container(
margin: EdgeInsets.only(
left: 50.0,
right: 50.0
),
child: new Draggable(
axis: Axis.horizontal,
child: new FlutterLogo(size: 50.0),
feedback: new FlutterLogo(size: 50.0),
),
height: 50.0,
color: Colors.green
),
],
),
),
);
}
}
I imagined that the container class would constrain the draggable area, but it doesn't appear to do that.
No. That's not the goal of Draggable widget. Instead, use a GestureDetector to detect drag. Then combine it with something like Align to move your content around
Here's a fully working slider based on your current code.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Slider(),
),
),
);
}
}
class Slider extends StatefulWidget {
final ValueChanged<double> valueChanged;
Slider({this.valueChanged});
#override
SliderState createState() {
return new SliderState();
}
}
class SliderState extends State<Slider> {
ValueNotifier<double> valueListener = ValueNotifier(.0);
#override
void initState() {
valueListener.addListener(notifyParent);
super.initState();
}
void notifyParent() {
if (widget.valueChanged != null) {
widget.valueChanged(valueListener.value);
}
}
#override
Widget build(BuildContext context) {
return Container(
color: Colors.green,
height: 50.0,
padding: EdgeInsets.symmetric(horizontal: 40.0),
child: Builder(
builder: (context) {
final handle = GestureDetector(
onHorizontalDragUpdate: (details) {
valueListener.value = (valueListener.value +
details.delta.dx / context.size.width)
.clamp(.0, 1.0);
},
child: FlutterLogo(size: 50.0),
);
return AnimatedBuilder(
animation: valueListener,
builder: (context, child) {
return Align(
alignment: Alignment(valueListener.value * 2 - 1, .5),
child: child,
);
},
child: handle,
);
},
),
);
}
}
As at 2022 here's a replica of #Remi's answer above, with minor tweaks to handle revisions to flutter/dart since 2018 (e.g. handling null-safety)
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: Slider(),
),
),
);
}
}
class Slider extends StatefulWidget {
final ValueChanged<double>? valueChanged;
const Slider({this.valueChanged});
#override
SliderState createState() {
return SliderState();
}
}
class SliderState extends State<Slider> {
ValueNotifier<double> valueListener = ValueNotifier(.0);
#override
void initState() {
valueListener.addListener(notifyParent);
super.initState();
}
void notifyParent() {
if (widget.valueChanged != null) {
widget.valueChanged!(valueListener.value);
}
}
#override
Widget build(BuildContext context) {
return Container(
color: Colors.green,
height: 50.0,
padding: const EdgeInsets.symmetric(horizontal: 40.0),
child: Builder(
builder: (context) {
final handle = GestureDetector(
onHorizontalDragUpdate: (details) {
valueListener.value = (valueListener.value + details.delta.dx / context.size!.width).clamp(.0, 1.0);
},
child: const FlutterLogo(size: 50.0),
);
return AnimatedBuilder(
animation: valueListener,
builder: (context, child) {
return Align(
alignment: Alignment(valueListener.value * 2 - 1, .5),
child: child,
);
},
child: handle,
);
},
),
);
}
}
I'm trying to create an animation in a CustomPainter in which the animation starts from the bottom to up, but it's starting at the top.
When clicking on the FloatActionButton the rectangle should rise to the maximum height of the screen, and when tap again go back to the minimum size.
I can get the size of the screen but I'm not able to insert this animation from the bottom to up. can you help me?
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
void main() {
runApp(new MaterialApp(home: new HomePage()));
}
class HomePage extends StatefulWidget {
#override
HomePageState createState() => new HomePageState();
}
class HomePageState extends State<HomePage> with TickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
bool upDown = true;
#override
void initState() {
_controller = new AnimationController(
vsync: this,
duration: const Duration(milliseconds: 180),
);
_animation = new CurvedAnimation(
parent: _controller,
curve: new Interval(0.0, 1.0, curve: Curves.linear),
);
}
#override
Widget build(BuildContext context) {
final ui.Size logicalSize = MediaQuery.of(context).size;
final double _width = logicalSize.width;
final double _height = logicalSize.height;
void _up(){
setState((){
if(upDown) {
upDown = false;
_controller.forward(from: 0.0);
} else {
upDown = true;
_controller.reverse(from: 1.0);
}
});
}
return new Scaffold(
body: new Stack(
children: <Widget>[
new Positioned(
bottom: 0.0,
child: new AnimatedBuilder(
animation: _animation,
builder: (BuildContext context, Widget child) {
return new Container(
height: _height,
child: new CustomPaint(
painter: new Sky(_width, _height * _animation.value),
//child: new Text('$_height '+ _animation.value.toString()),
),
);
},
),
),
new Positioned(
bottom: 16.0,
right: 16.0,
child: new FloatingActionButton(
backgroundColor: new Color(0xFFE57373),
child: new Icon(Icons.add),
onPressed: (){
_up();
},
)
)
]
)
);
}
}
class Sky extends CustomPainter {
final double _width;
double _rectHeight;
Sky(this._width, this._rectHeight);
#override
void paint(Canvas canvas, Size size) {
canvas.drawRect(
new Rect.fromLTRB(
0.0, 0.0, this._width, _rectHeight
),
new Paint()..color = new Color(0xFF0099FF),
);
}
#override
bool shouldRepaint(Sky oldDelegate) {
return _width != oldDelegate._width || _rectHeight != oldDelegate._rectHeight;
}
}
You can use an AnimatedBuilder to do this. Also, make sure that you provide a working implementation of shouldRepaint. Your upDown member variable should be a member of the State rather than part of the build function.
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
void main() {
runApp(new MaterialApp(home: new HomePage()));
}
class HomePage extends StatefulWidget {
#override
HomePageState createState() => new HomePageState();
}
class HomePageState extends State<HomePage> with TickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
bool upDown = true;
#override
void initState() {
_controller = new AnimationController(
vsync: this,
duration: const Duration(milliseconds: 180),
);
_animation = new CurvedAnimation(
parent: _controller,
curve: new Interval(0.0, 1.0, curve: Curves.linear),
);
}
#override
Widget build(BuildContext context) {
final ui.Size logicalSize = MediaQuery.of(context).size;
final double _width = logicalSize.width;
final double _height = logicalSize.height;
void _up(){
setState((){
if(upDown) {
upDown = false;
_controller.forward(from: 0.0);
} else {
upDown = true;
_controller.reverse(from: 1.0);
}
});
}
return new Scaffold(
body: new Stack(
children: <Widget>[
new Positioned(
bottom: 0.0,
child: new AnimatedBuilder(
animation: _animation,
builder: (BuildContext context, Widget child) {
return new Container(
height: _height,
child: new CustomPaint(
painter: new Sky(_width, _height * _animation.value),
),
);
},
),
),
new Positioned(
bottom: 16.0,
right: 16.0,
child: new FloatingActionButton(
backgroundColor: new Color(0xFFE57373),
child: new Icon(Icons.add),
onPressed: (){
_up();
},
)
)
]
)
);
}
}
class Sky extends CustomPainter {
final double _width;
double _rectHeight;
Sky(this._width, this._rectHeight);
#override
void paint(Canvas canvas, Size size) {
canvas.drawRect(
new Rect.fromLTRB(
0.0, size.height - _rectHeight, this._width, size.height
),
new Paint()..color = new Color(0xFF0099FF),
);
}
#override
bool shouldRepaint(Sky oldDelegate) {
return _width != oldDelegate._width || _rectHeight != oldDelegate._rectHeight;
}
}
Say I want to build a StatefulWidget named MySlideWidget that provides a public instance method: animate().
When I press a button on parent of MySlideWidget, then I can call MySlideWidget's animate() method to trigger an internal SlideTransition of MySlideWidget.
The usage would look like this:
class MySlideWidgetDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
MySlideWidget mySlideWidget = new MySlideWidget();
return new Scaffold(
body: mySlideWidget,
floatingActionButton: new FloatingActionButton(
onPressed: () {
mySlideWidget.animate();
},
tooltip: 'Start',
child: new Icon(Icons.add),
));
}
}
What I wondering is how to encapsulate the implementations of AnimationController and _controller.forward() inside MySlideWidget, so user of MySlideWidget can simply call animate().
Is this possible? Or what is the idea way to do encapsulation in Flutter?
You should use AnimationController and pass it to SlideTransition. Then call controller.forward() when needed.
Here is sample:
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
AnimationController _controller;
Animation<FractionalOffset> _slideTransitionPosition;
#override
initState() {
super.initState();
_controller = new AnimationController(
vsync: this,
duration: const Duration(milliseconds: 2000),
);
_slideTransitionPosition = new FractionalOffsetTween(
begin: const FractionalOffset(0.0, -1.0),
end: const FractionalOffset(0.0, 10.0),
).animate(new CurvedAnimation(
parent: _controller,
curve: Curves.fastOutSlowIn,
));
}
void _onPress() {
_controller.forward();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Container(
child: new SlideTransition(
position: _slideTransitionPosition,
child: new Container(
color: Colors.red,
width: 100.0,
height: 100.0,
),
),
),
floatingActionButton: new FloatingActionButton(
onPressed: _onPress,
tooltip: 'Start',
child: new Icon(Icons.add),
),
);
}
}
Also you can find examples in demos