Code
// Member variables
AnimationController _controller;
Animation _animation1;
Animation _animation2;
#override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 400),
);
_animation1 = Tween<double>(begin: 0.2, end: 1).animate(_controller);
_animation2 = Tween<double>(begin: 0.4, end: 1).animate(_controller);
}
Here both _animation1 and _animation2 gets 400 milliseconds of duration. However I want to change the duration for _animation2. How can I do that without creating a new AnimationController.
Interval as suggested by Rémi is the solution.
_animation1 = Tween<double>(begin: 0.2, end: 1).animate(CurvedAnimation(
parent: _controller,
curve: Interval(0.5, 1.0),
));
_animation2 = Tween<double>(begin: 0.2, end: 1).animate(CurvedAnimation(
parent: _controller,
curve: Interval(0.8, 1.0),
));
Related
When I finish dragging a Positioned widget I notice that there's a small shift in the final position when I lift the finger off the screen on the iOS device (iOS 14.6). This behaviour also present in the iOS emulator but very visible on the physical device:
– notice the slight position shift after the red cube stops moving.
Demo's source code:
import 'package:flutter/material.dart';
class GestureDemo extends StatefulWidget {
const GestureDemo({Key? key}) : super(key: key);
#override
GestureDemoState createState() => GestureDemoState();
}
class GestureDemoState extends State<GestureDemo> {
late Offset _startingFocalPoint;
late Offset _previousOffset;
Offset _offset = Offset.zero;
late double _previousZoom;
double _zoom = 1.0;
final containerSize = Size(100, 100);
void _handleScaleStart(ScaleStartDetails details) {
setState(() {
_startingFocalPoint = details.focalPoint;
_previousOffset = _offset;
_previousZoom = _zoom;
});
}
void _handleScaleUpdate(ScaleUpdateDetails details) {
setState(() {
_zoom = _previousZoom * details.scale;
// Ensure that item under the focal point stays in the same place despite zooming
final Offset normalizedOffset = (_startingFocalPoint - _previousOffset) / _previousZoom;
_offset = details.focalPoint - normalizedOffset * _zoom;
});
}
#override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: <Widget>[
Positioned(
top: _offset.dy,
left: _offset.dx,
child: GestureDetector(
onScaleStart: _handleScaleStart,
onScaleUpdate:_handleScaleUpdate,
child: Container(
color: Colors.red,
width: containerSize.width * _zoom,
height: containerSize.height * _zoom,
),
)
),
],
);
}
}
void main() {
runApp(MaterialApp(
theme: ThemeData.dark(),
home: Scaffold(
appBar: AppBar(title: const Text('Gestures Demo')),
body: const GestureDemo(),
),
));
}
This is a simplified version of the official widget gestures example from Flutter.
When I log ScaleUpdateDetails, I see that the last details have a small shift
ScaleUpdateDetails(focalPoint: Offset(174.0, 260.5), localFocalPoint: Offset(28.5, 73.0), scale: 1.0, horizontalScale: 1.0, verticalScale: 1.0, rotation: 0.0, pointerCount: 1)
ScaleUpdateDetails(focalPoint: Offset(174.0, 260.0), localFocalPoint: Offset(28.5, 72.5), scale: 1.0, horizontalScale: 1.0, verticalScale: 1.0, rotation: 0.0, pointerCount: 1)
ScaleUpdateDetails(focalPoint: Offset(173.0, 258.0), localFocalPoint: Offset(27.5, 70.5), scale: 1.0, horizontalScale: 1.0, verticalScale: 1.0, rotation: 0.0, pointerCount: 1)
How to remove this position shift?
I've been trying to implement a gyroscope image viewer using the sensors package, however, the result seems to be very laggy. I have found a similar project on YouTube which is trying to achieve a similar goal, but as you can see in the video the animation is also very laggy.
The following code is simply outputting the data from the event, I notice how the data is being updated lags like 50ms in between updates.
Is there a way to smoothen the animation or update the data faster? Or is this a Flutter limitation?
NOTE:
I have tried --release version as suggested by other posts but the result stays the same.
import 'package:sensors/sensors.dart';
class MyGyro extends StatefulWidget {
final Widget child;
MyGyro({this.child});
#override
_MyGyroState createState() => _MyGyroState();
}
class _MyGyroState extends State<MyGyro> {
double gyroX = 0;
double gyroY = 0;
#override
void initState() {
super.initState();
gyroscopeEvents.listen((GyroscopeEvent event) {
setState(() {
gyroX = ((event.x * 100).round() / 100).clamp(-1.0, 1.0) * -1;
gyroY = ((event.y * 100).round() / 100).clamp(-1.0, 1.0);
});
});
}
#override
Widget build(BuildContext context) {
return Container(
height: 100,
width: 100,
child: Transform.translate(
offset: Offset(gyroY, 0),
child: Container(
child: Center(
child: Column(
children: [Text("X: ${gyroX}"), Text("Y: ${gyroY}"),],
),
),
),
),
);
}
}
I have found that is purely the problem of the sensors package I was using, either they have hard coded a slower interval when listening to the sensor event, or they are just using the default interval by the IOS channel.
So, I have found another package called flutter_sensors which had solved the problem. It's a very simple API to access the sensor events, but it allows you to change the interval.
I have 1 Gesture Detector that wraps 10 different cards and it allows swiping them. Each card has 3 on tap Gesture Detectors(buttons basically). Swipe works as expected but first 6 cards don't trigger the tap action when tapped (Gesture Arena tells me horizontal drag is fighting vertical drag -> so the tap gesture is perceived as vertical drag).
I find it Strange that the last 4 cards trigger on tap and swipe works perfect
I've tried changing GestureDetectors to InkWell but it didn't work out.
#override
Widget build(BuildContext context) {
return GestureDetector(
onHorizontalDragStart: _onHorizontalDragStart,
onHorizontalDragUpdate: _onHorizontalDragUpdate,
onHorizontalDragEnd: _onHorizontalDragEnd,
behavior: HitTestBehavior.translucent,
child: Stack(
children: _buildCards(),
),
);
}
List<Widget> _buildCards() {
return [
_buildCard(0, 10, scrollPercent),
_buildCard(1, 10, scrollPercent),
_buildCard(2, 10, scrollPercent),
_buildCard(3, 10, scrollPercent),
_buildCard(4, 10, scrollPercent),
_buildCard(5, 10, scrollPercent),
_buildCard(6, 10, scrollPercent),
_buildCard(7, 10, scrollPercent),
_buildCard(8, 10, scrollPercent),
_buildCard(9, 10, scrollPercent),
];
}
Widget _buildCard(int cardIndex, int cardCount, double scrollPercent) {
final cardScrollPercent = scrollPercent / (1 / cardCount);
final parallax = scrollPercent - (cardIndex / cardCount);
return FractionalTranslation(
translation: Offset(cardIndex - cardScrollPercent, 0.0),
child: Transform(
transform: _buildCardProjection(cardScrollPercent - cardIndex),
child: card1(parallax, cardIndex
),
)
);
}
Card1 is appealed last and it represents the card itself..
Here is the code for the onTap property of GestureDetector of Card1
onTap: () {
showDialog(
context: context,
builder: (BuildContext context) => CustomDialog(
title: "Title",
description:"Test",
buttonText: "Okay",
),
);
},
It sounds like you might be interested in replacing your setup with a PageView widget. It uses a PageController to manage the swipe gestures. Check out the video on it Widget of the Week - PageView
I have a Container widget inside of a ClipPath which uses a CustomClipper. Everything works fine, I have the desired widget shape.
However, I could not find a way to make a shadow for this custom shaped Widget.
Also, I want to have an outline(border) that follows the edges of this custom widget automatically.
Again no luck. I tried BoxDecoration:border, BoxDecoration:boxShadow, ShapeDecoration:shape, ShapeDecoration:shadows, Material:Elevation, etc..
based on #Bohdan Uhrynovskiy I investigated further and came up with this solution:
CustomPaint(
painter: BoxShadowPainter(),
child: ClipPath(
clipper: MyClipper(), //my CustomClipper
child: Container(), // my widgets inside
)));
class BoxShadowPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
Path path = Path();
// here are my custom shapes
path.moveTo(size.width, size.height * 0.14);
path.lineTo(size.width, size.height * 1.0);
path.lineTo(size.width - (size.width *0.99) , size.height);
path.close();
canvas.drawShadow(path, Colors.black45, 3.0, false);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
You must need to provide your own custom paths in paint() method of BoxShadowPainter
Look at source code of the library. Feature implemented in this library seems very similar to your task.
You have to implement CustomPainter that draws shadows and borders.
return AspectRatio(
aspectRatio: 1.0,
child: CustomPaint(
painter: BoxShadowPainter(specs, boxShadows),
child: ClipPath(
clipper: Polygon(specs),
child: child,
)));
I'm new to Flutter and Dart. Hopes to get some guide on a tutorial exercise I'm stuck.
I'm following Flutter Codelab https://codelabs.developers.google.com/codelabs/flutter/index.html?index=..%2F..index#6 and able to do everything.
There's an exercise it ask us to do that is
Create a fade-in animation effect by wrapping the Container in a FadeTransition widget instead of a SizeTransition.
The code as below
#override
Widget build(BuildContext context) {
return new SizeTransition(
sizeFactor: new CurvedAnimation(
parent: animationController, curve: Curves.easeOut),
axisAlignment: 0.0,
child: new Container(
// ... other codes ...
),
);
}
So I change to FadeTransition, which requires opacity of type Animation<Double>
#override
Widget build(BuildContext context) {
return new FadeTransition(
opacity: animation
child: new Container(
// ... other codes ...
),
);
}
How could I create or send in the animation? (the above code will have animation unrecognizable).
You can try this
opacity: Tween<double>(
begin: 0.0,
end: 1.0,
).animate(animationController),
CurvedAnimation is used for non-linear animation.
See more detail here https://flutter.dev/docs/development/ui/animations/tutorial
Found the answer, by referring to https://proandroiddev.com/getting-your-hands-dirty-with-flutter-basic-animations-6b9f21fa7d17 and modify accordingly.
So to have FadeTransition, basically just replace
opacity: animation
with
opacity: new CurvedAnimation(parent: animationController, curve: Curves.easeIn),
This is not ideal as every time a message is inserted, it is creating a new CurveAnimation, but for the sake of concise solution, I make it so.