How to get rid of overflow-error on AnimatedContainer? - flutter

I implemented a MaterialBanner. I created a slide-up-effect once the user pushes the dismiss-button. Everything works ok, except for the overflow-error 'bottom overflowed by .. pixels', which appears when you click the dismiss button. The number of pixels in the error message counts down to zero as the bottom slides up. How can I solve this last issue? I expected the MaterialBanner to respect the maxHeight of the BoxConstraint instead of overflowing.
AnimatedContainer buildAnimatedBanner(AuthViewModel vm) {
return AnimatedContainer(
constraints: BoxConstraints(maxHeight: _heightBanner),
duration: Duration(seconds: 3),
child: MaterialBanner(
backgroundColor: Colors.green,
leading: Icon(EvilIcons.bell,
size: 28, color: AppTheme.appTheme().colorScheme.onBackground),
content: Text('Please check your inbox to verify email ${vm.email}'),
actions: <Widget>[
FlatButton(
child: Text(
"Send again",
style: TextStyle(
color: AppTheme.appTheme().colorScheme.onBackground),
),
onPressed: () {},
),
FlatButton(
child: Text(
"Dismiss",
style: TextStyle(
color: AppTheme.appTheme().colorScheme.onBackground),
),
onPressed: () => setState(() => _heightBanner = 0),
),
],
),
);
}

It's overflowing because while the container is reducing in height, the content of the banner is still being rendered in full. You'll still see this error for as long as the material banner is still in view.
Looking at your code, I'm thinking the purpose of the AnimatedContainer is to make a smooth transition when the height of the child (MaterialBanner) changes.
You can use an AnimatedSize instead. It'll automatically handle the size change transitions for you and you don't have to worry about Overflow error.
It also provides an alignment param you can use to determine the direction of the transition.
Below is a code. Demo can be found in this codepen.
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: MyApp(),
),
);
}
class MyApp extends StatefulWidget {
MyWidget createState() => MyWidget();
}
class MyWidget extends State<MyApp> with SingleTickerProviderStateMixin {
int itemCount = 2;
bool pressed = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: AnimatedSize(
vsync: this,
duration: Duration(milliseconds: 500),
alignment: Alignment.topCenter,
child: Container(
color: Colors.red,
// height: 300,
width: 300,
child: ListView.builder(
itemCount: itemCount,
shrinkWrap: true,
itemBuilder: (context, index) {
return ListTile(
title: Text("$index"),
);
}
),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState((){
itemCount = pressed ? 6 : 4;
pressed = !pressed;
});
}
),
);
}
}

Related

In Flutter, what's the easiest, simplest way to make a container flashing once without involving animation widgets?

Imagine Facebook mobile app, where you tap on the notification about someone like your comment. The app will open the appropriate screen, scroll you down to the comment, and after you arrive there, the comment row will flash yellow for a while, rapidly turn transparent, and then it's done.
I just want to make the same flashing animation to a ListView/Column element to let users know that something is happening there as a result of their action. But from what I gathered, to create just a simple animation like that needs a complex elaborate contraption with Animation widgets.
There's a widget that does a much appreciated fade animation called FadeInImage. I just need to provide destination URL, placeholder image asset, and the widget will handle the rest. I'm wondering if there's such alternative where I can just provide a key to a widget, and then call from anywhere: rowKey.currentState.flash(color: Colors.yellow). Or perhaps a way to let me tell the ListView or Column to flash certain row like listViewKey.currentState.items[5].flash(color: Colors.yellow).
There is no a Widget like you are looking for, but you can create a custom widget if you know the Flutter basics. You will be able to build from simple animations to the most advanced ones.
I made a simple example, a list of elements where you can select any element from the list (index).
When you open the screen, you will see the scroll animation, after that, the blink animation will start.
class FlashingHome extends StatelessWidget {
const FlashingHome({Key? key}) : super(key: key);
void _goToWidget(BuildContext context, int index) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => FlashingList(index: index),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
MaterialButton(
color: Colors.greenAccent,
child: const Text('Go to element 5'),
onPressed: () => _goToWidget(context, 5),
),
MaterialButton(
color: Colors.greenAccent,
child: const Text('Go to element 10'),
onPressed: () => _goToWidget(context, 10),
),
],
),
),
);
}
}
class FlashingList extends StatefulWidget {
const FlashingList({required this.index, Key? key}) : super(key: key);
final int index;
#override
State<FlashingList> createState() => _FlashingListState();
}
class _FlashingListState extends State<FlashingList>
with SingleTickerProviderStateMixin {
final _scrollController = ScrollController();
late final AnimationController _animationController;
final _itemSize = 150.0;
Timer? _timer;
Future<void> _startScrolling() async {
await _scrollController.animateTo(
_itemSize * widget.index,
duration: const Duration(seconds: 1),
curve: Curves.easeOut,
);
// after the scroll animation finishes, start the blinking
_animationController.repeat(reverse: true);
// the duration of the blinking
_timer = Timer(const Duration(seconds: 3), () {
setState(() {
_animationController.stop();
_timer?.cancel();
});
});
}
#override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
);
WidgetsBinding.instance!.addPostFrameCallback((_) => _startScrolling());
super.initState();
}
#override
void dispose() {
_timer?.cancel();
_animationController.dispose();
_scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flashing List'),
),
body: ListView.builder(
controller: _scrollController,
itemCount: 15,
itemExtent: 150,
itemBuilder: (context, index) {
final item = Padding(
padding: const EdgeInsets.all(20.0),
child: Text('My Item :$index'),
);
return Padding(
padding: const EdgeInsets.all(4.0),
child: FittedBox(
child: index == widget.index && _animationController.isDismissed
? FadeTransition(
opacity: _animationController,
child: Container(
color: Colors.yellow,
child: item,
),
)
: Container(
color: Colors.grey[200],
child: item,
),
),
);
},
),
);
}
}
Result:
Now that you know how to create an automatic scrolling list, animated item, you can customize this with a more complex animation and extract into a custom widget that you can reuse in your projects.
Reference: https://docs.flutter.dev/development/ui/animations
Try shimmer
While the data is being fetched, you can create a simple animation which will give the feel that something's loading. Here's a simple example.
I am using FAB onPress to reflect the changes.
bool isApiCallProcess = false;
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
isApiCallProcess = !isApiCallProcess;
});
},
),
backgroundColor: Colors.white,
body: (isApiCallProcess == false)
? CircleAvatar(
backgroundColor:
Colors.black12,
radius: 40,
backgroundImage: AssetImage(
'images/dosa.jpg',
),
):
Shimmer.fromColors(
baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!,
child: Wrap(
children: [
Column(
children: [
const CircleAvatar(
radius: 40,
backgroundImage: AssetImage(
'',
),
),
const SizedBox(
height: 10,
),
Container(
decoration: ShapeDecoration(
color: Colors.grey[400]!,
shape: const
RoundedRectangleBorder(),
),
height: 12,
width: 60,
),
],
),
],
),
),
),
);
Here's the screenshots :

In Expandable Floating action button the gestures in the child widget is not detected in flutter

Trying to implement a floating action button that extends in two dimension and then show some more option of floating action button.
Somehow able to animated the child widget of the floating action button to their correct position, using the Transform Widget, but when I try to press on the child widget, i.e. the widgets that come out on pressing the floating action button, they do not respond to the onPressed handler.
Tried many different thing like IgnorePointer, stacked rows and Columns, AnimatedBuilder,etc. but was unable to find the correct solution.
It was like sometimes used to get the UI correct then the gesture was not detected and if the gesture were detected the UI got distorted.
And I am somewhat new to flutter. Any help in sorting out this issue would be appreciated.
Here is my Code:
main.dart
import "package:flutter/material.dart";
import 'myhome.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primaryColor: Colors.blue,
),
title: "Custom Expandable FAB",
home: MyHome(),
);
}
}
myhome.dart
import 'package:flutter/material.dart';
import 'package:tester_project/customFAB.dart';
class MyHome extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
extendBody: true,
backgroundColor: Colors.white,
floatingActionButton: CustomFab(),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: BottomAppBar(
color: Colors.blue,
shape: CircularNotchedRectangle(),
child: Container(
height: 55,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.menu),
onPressed: () {},
),
],
),
),
),
appBar: AppBar(
title: Text("Custom FAB"),
),
body: Container(
alignment: Alignment.center,
child: Text("Click on Fab to expand"),
color: Colors.white,
),
);
}
}
CustomFab.dart
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
class CustomFab extends StatefulWidget {
#override
_CustomFabState createState() => _CustomFabState();
}
class _CustomFabState extends State<CustomFab>
with SingleTickerProviderStateMixin {
AnimationController _animationController;
Animation<double> _translateAnimation;
Animation<double> _rotationAnimation;
Animation<double> _iconRotation;
bool _isExpanded = false;
void animate() {
if (!_isExpanded) {
_animationController.forward();
} else {
_animationController.reverse();
}
_isExpanded = !_isExpanded;
}
Widget fab1() {
return Container(
height: 60,
width: 60,
child: FittedBox(
child: FloatingActionButton(
heroTag: "btn3",
backgroundColor: Color(0xffFFC852),
elevation: 0,
onPressed: () {
print("pressed");
},
),
),
);
}
Widget fab2() {
return Container(
height: 60,
width: 60,
child: FittedBox(
child: FloatingActionButton(
heroTag: "btn4",
child: Transform.rotate(
angle: _iconRotation.value,
child: Icon(Icons.home),
),
elevation: _isExpanded ? 5 : 0,
backgroundColor: Color(0xffE5E4F4),
onPressed: () {
print("Pressed");
},
),
),
);
}
Widget fab3() {
return Container(
height: 60,
width: 60,
child: FittedBox(
child: FloatingActionButton(
heroTag: "btn5",
child: Transform.rotate(
angle: _rotationAnimation.value,
child: Icon(Icons.add),
),
backgroundColor: Color(0xffFFC852),
onPressed: () async {
await Permission.contacts.request();
if (await Permission.contacts.status.isGranted) {
animate();
}
},
),
),
);
}
#override
void initState() {
_animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 400))
..addListener(() {
setState(() {});
});
_translateAnimation = Tween<double>(begin: 0, end: 80)
.chain(
CurveTween(
curve: _isExpanded ? Curves.fastOutSlowIn : Curves.bounceOut,
),
)
.animate(_animationController);
_iconRotation = Tween<double>(begin: 3.14 / 2, end: 0)
.chain(
CurveTween(curve: Curves.bounceInOut),
)
.animate(_animationController);
_rotationAnimation = Tween<double>(begin: 0, end: 3 * 3.14 / 4)
.chain(
CurveTween(
curve: Curves.bounceInOut,
),
)
.animate(_animationController);
super.initState();
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
children: [
Transform(
transform:
Matrix4.translationValues(0, -_translateAnimation.value, 0),
child: fab1(),
),
Transform(
transform:
Matrix4.translationValues(-_translateAnimation.value, 0, 0),
child: fab2(),
),
fab3(),
],
);
}
}
Floating action button before and after expansion
Look at this. https://api.flutter.dev/flutter/widgets/Transform-class.html
Unlike RotatedBox, which applies a rotation prior to layout, this object applies its transformation just prior to painting, which means the transformation is not taken into account when calculating how much space this widget's child (and thus this widget) consumes.
So, your fab1(),fab2(),fab3() have the same position.
Although you animate them, it just move at painting, their real position wont change.
Just give a color to your fabs, you will know what I mean.
Container(
color:Colors.green,
child: Transform(
transform:
Matrix4.translationValues(-_translateAnimation!.value, 0, 0),
child: fab2(),
),
),
Now you know why, and you need to know how.
So I hope you can look at this. https://api.flutter.dev/flutter/animation/animation-library.html
You can use Stack&&Positioned,and with Tween, caculate each button's position, or other way. I will leave you to explore.
Here's some code of CustomFab.dart
#override
Widget build(BuildContext context) {
return Container(
// color: Colors.green, // Give this area a background color, then you know why.
height: 150,// You need to give your "action area" a bigger size.
width: 150,// make these bigger have a problem, your bar will have a bigger circle.
// if you need this effect, you need to change all your fabs "Stack on the appbar"
// or just remove `shape: CircularNotchedRectangle(),` in myhome.dart
child: Stack(
clipBehavior: Clip.none,
children: [
Positioned(// These numbers just for example, you can make your own size or position.
left: 150 / 2 - 30,
bottom: _translateAnimation.value + 40,
child: fab1(),
),
Positioned(
left: 150 / 2 - 30 -_translateAnimation.value,
bottom: 40,
child: fab2(),
),
Positioned(
left: 150 / 2 - 30,
bottom: 40,
child: fab3(),
),
],
),
);
}
Try Speed Dial using this package https://pub.dev/packages/flutter_speed_dial
SpeedDial(
marginBottom: 25,
marginEnd: 25,
backgroundColor: Colors.blue,
activeBackgroundColor: Colors.white,
activeForegroundColor: Colors.blue,
animatedIcon: AnimatedIcons.menu_close,
children: [
SpeedDialChild(
child: Icon(
Icons.filter_alt,
),
label: 'ABC',
onTap: () {
}),
SpeedDialChild(
labelBackgroundColor: Colors.white,
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
child: Icon(Icons.add),
label: 'ABC',
onTap: () {
}),
],
),

Flutter: Override 'PageView' scroll gesture with child's GestureDetector

I am using a combination of "BottomNavigationBar" and "PageView" for navigation in my app. The user can either swipe to the next page or use the navigation bar.
On one of my pages, I would like to use a gesture detector that handles pan gestures, both vertically and horizontally.
I can't find a way to override the PageView's gesture detection with the nested GestureDetector. This means only vertical pan gestures are handled, as the horizontal ones are occupied by the PageView.
How can I disable / override the PageViews gesture detection for only that page or only the widget, without completely disabling the PageViews scroll physics?
I have created a simplified version of my App to isolate the issue, and attached a video of the problem below.
Any help would be greatly appreciated!
Here is the code inside my main.dart:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: GestureIssueExample(),
);
}
}
class GestureIssueExample extends StatefulWidget {
GestureIssueExample({Key key}) : super(key: key);
#override
_GestureIssueExampleState createState() => _GestureIssueExampleState();
}
class _GestureIssueExampleState extends State<GestureIssueExample> {
int _navigationIndex;
double _xLocalValue;
double _yLocalValue;
PageController _pageController;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: null,
bottomNavigationBar: _buildBottomNavigationBar(),
backgroundColor: Colors.white,
body: PageView(
controller: _pageController,
onPageChanged: _onNavigationPageChanged,
children: [
//Just a placeholder to represent a page to the left of the "swipe cards" widget
_buildSamplePage("Home"),
//Center child of 'PageView', contains a GestureDetector that handles Pan Gestures
//Thanks to the page view however, only vertical pan gestures are detected, while both horizontal and vertical gestures
//need to be handled...
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Local X: ${_xLocalValue.toString()}\nLocal Y: ${_yLocalValue.toString()}"),
GestureDetector(
onPanStart: (details) => setState(
() {
this._xLocalValue = details.localPosition.dx;
this._yLocalValue = details.localPosition.dy;
},
),
onPanUpdate: (details) => setState(
() {
this._xLocalValue = details.localPosition.dx;
this._yLocalValue = details.localPosition.dy;
},
),
child: Container(
width: MediaQuery.of(context).size.width * 0.9,
height: 100.0,
color: Colors.red,
alignment: Alignment.center,
child: Text("Slidable Surface",
style: TextStyle(color: Colors.white)),
),
),
],
),
),
//Just a placeholder to represent a page to the right of the "swipe cards" widget
_buildSamplePage("Settings"),
],
),
);
}
#override
void initState() {
super.initState();
this._navigationIndex = 0;
this._pageController = PageController(
initialPage: _navigationIndex,
);
}
Widget _buildSamplePage(String text) {
// This simply returns a container that fills the page,
// with a text widget in its center.
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.grey[900],
alignment: Alignment.center,
child: Text(
text,
style: TextStyle(
color: Colors.white, fontSize: 30.0, fontWeight: FontWeight.bold),
),
);
}
Widget _buildBottomNavigationBar() {
//Returns the bottom navigation bar for the scaffold
return BottomNavigationBar(
backgroundColor: Colors.grey[900],
selectedItemColor: Colors.redAccent,
unselectedItemColor: Colors.white,
items: [
BottomNavigationBarItem(icon: Icon(Icons.home_outlined), label: "Home"),
BottomNavigationBarItem(
icon: Icon(Icons.check_box_outline_blank), label: "Cards"),
BottomNavigationBarItem(
icon: Icon(Icons.settings_outlined), label: "Settings"),
],
currentIndex: _navigationIndex,
onTap: _onNavigationPageChanged,
);
}
void _onNavigationPageChanged(int newIndex) {
//Set the new navigation index for the nav bar
setState(() => this._navigationIndex = newIndex);
//Animate to the selected page
_pageController.animateToPage(
newIndex,
curve: Curves.easeInOut,
duration: Duration(microseconds: 100),
);
}
}
Can you try something like this:
Add this line to your PageView:
PageView(
...
physics: _navigationIndex == 1 ? NeverScrollableScrollPhysics() : AlwaysScrollableScrollPhysics(),
...
)
Note: the number 1 is because the page with the GestureDetector is on index 1.

How to receive both onTapDown (eagerly) and onDoubleTap?

I'd like to perform some interaction:
when an initial touch (onTapDown) happens (e.g. clear existing selections), AND
when a double tap happens (onDoubleTap) happens (e.g. highlight a new selection)
Using a GestureDetector:
if only register a onTapDown, the callback is called immediately at the first touch
if I register both onTapDown and onDoubleTap, and the user performs a simple tap, it takes some considerable time until the onTapDown event is called (1/2 a second?). If I update the display following the tap, for the user, this feels like rendering jank -- but it in fact just getting the event to late.
Is there a way to eagerly receive onTapDown AND onDoubleTap? (Its OK, in fact preferred, if I get 2 events on a double tap).
import 'package:flutter/material.dart';
// 1. double tap the green box
// 2. single tap the green box again and that it takes a long
// time to update the text
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) => MaterialApp(
title: 'Flutter Playground',
home: Material(child: SafeArea(child: content())));
Widget content() {
final controller = TextEditingController(text: "-");
return Center(
child: Column(children: [
GestureDetector(
onTapDown: (_) {
controller.text = "tap down";
// print("[${DateTime.now().millisecondsSinceEpoch}] tap down");
},
onDoubleTap: () {
controller.text = "double tap ";
// print("[${DateTime.now().millisecondsSinceEpoch}] double tap");
},
child: Container(
color: Colors.green,
width: 200,
height: 200,
child: Center(child: Text("Tap me")))),
Container(
color: Colors.red,
width: 200,
height: 200,
child: Center(child: TextField(controller: controller)))
]));
}
}
main() => runApp(MyApp());
A hacky way to do it I suppose, but you can use a Listener for onPointerDown. The code below should log tap down twice when you double-tap:
import 'package:flutter/material.dart';
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) => MaterialApp(title: 'Flutter Playground', home: Material(child: SafeArea(child: content())));
Widget content() {
final controller = TextEditingController(text: "-");
return Center(
child: Column(
children: [
GestureDetector(
onDoubleTap: () {
controller.text = "double tap";
print("double tap");
},
child: Listener(
onPointerDown: (_) {
controller.text = "tap down";
print("tap down");
},
child: Container(
color: Colors.green,
width: 200,
height: 200,
child: Center(
child: Text("Tap me"),
),
),
)),
Container(
color: Colors.red,
width: 200,
height: 200,
child: Center(
child: TextField(controller: controller),
),
)
],
),
);
}
}
main() => runApp(MyApp());
Use gesture_x_detector plugin.
XGestureDetector(
bypassTapEventOnDoubleTap: true,
doubleTapTimeConsider: 170
child: ...
)

I have laggy animation because of setState() in my code

I have animated pageview and listview that connected to each other. But I have an animation problem that caused by setState(i have tested to removed the setState and the animation works well).
import 'package:flutter/material.dart';
const double _listheight = 80;
class LoyaltyPage extends StatefulWidget {
#override
_LoyaltyPageState createState() => new _LoyaltyPageState();
}
class _LoyaltyPageState extends State<LoyaltyPage> {
PageController controller;
ScrollController listcontroller;
int selected = 0;
List<String> images=[
'https://images.pexels.com/photos/67636/rose-blue-flower-rose-blooms-67636.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500',
'https://images.pexels.com/photos/67636/rose-blue-flower-rose-blooms-67636.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500',
'https://images.pexels.com/photos/67636/rose-blue-flower-rose-blooms-67636.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500',
'https://images.pexels.com/photos/67636/rose-blue-flower-rose-blooms-67636.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500',
];
#override
initState() {
super.initState();
controller = new PageController(
initialPage: selected,
keepPage: false,
viewportFraction: 0.7,
);
listcontroller = new ScrollController();
}
#override
dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
iconTheme: IconThemeData(color: Colors.black),
title: Text(
'Loyalty',
style: TextStyle(color: Colors.black),
),
),
body: Column(
children: <Widget>[
Expanded(
flex: 3,
child: new Container(
child: new PageView.builder(
onPageChanged: (value) {
//ADDING THIS SET STATE CAUSE SELECTED COLOR WORKS BUT LAGGY ANIMATION
setState(() {
selected=value;
});
listcontroller.animateTo(value*_listheight,duration: Duration(milliseconds: 500),curve: Curves.ease);
},
itemCount: images.length,
controller: controller,
itemBuilder: (context, index) => builder(index)),
),
),
Expanded(
flex: 6,
child: new Container(
child: new ListView.builder(
controller: listcontroller,
itemCount: images.length,
itemBuilder: (context,index) => _listbuilder(index),
),
),
),
],
),
);
}
builder(int index) {
return new AnimatedBuilder(
animation: controller,
builder: (context, child) {
double value = 1.0;
if (controller.position.haveDimensions) {
value = controller.page - index;
value = (1 - (value.abs() * .4)).clamp(0.0, 1.0);
}
return new Center(
child: new SizedBox(
height: Curves.easeOut.transform(value) * 180,
width: Curves.easeOut.transform(value) * 270,
child: child,
),
);
},
child: new Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))),
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
child:Container(
child: Image.network(images[index],fit: BoxFit.fill,)
),
),
);
}
_listbuilder(int index){
return Container(
height: _listheight,
child: Column(
children: <Widget>[
ListTileTheme(
selectedColor: Colors.blueAccent,
child: ListTile(
title: Text(images[index],maxLines: 1,overflow: TextOverflow.ellipsis,),
subtitle: Text('Level : Gold'),
leading: GestureDetector(
onTap: (){
//ADDING THIS SET STATE CAUSE SELECTED COLOR WORKS BUT LAGGY ANIMATION
setState(() {
selected=index;
});
controller.animateToPage(index,duration: Duration(milliseconds: 500),curve: Curves.ease);
},
child: Icon(
Icons.credit_card),
),
trailing: Icon(Icons.navigate_next),
selected: index==selected,//THIS IS THE SELECTED THAT I TRIED TO CHANGE
),
),
Divider(height: 1,color: Colors.black45,),
],
),
);
}
}
I use setState at 2 places where it's on pagechanged and on leading listtile of my app.And here is my app looks like.
the first one is the expected output of my app but it laggy,
the second one has smooth animation but the text color doesn't change.
Try running your application in Release or Profile Mode.
There are performance-issues with animations in debug-mode when only a CircularProgressIndicator() is spinning. This is due to the fact that in Debug Mode, Dart gets compiled JIT instead of AOT. Thus, the Drawing Engine would need to compile 60 times / second while drawing the UI during the animation to run as smoothly as in AOT mode. This would take up a lot of resources as one might guess.
As long as you don't call setState({}) on the AnimationController..addListener() you should be fine with your specific implementation. Please refer to this article for further details on Animations and setState({}): https://medium.com/flutter-community/flutter-laggy-animations-how-not-to-setstate-f2dd9873b8fc .