I'd like to animate a gap between two items in a list. I thought of using an AminatedContainer with a height initially at zero but I'm not familiar with how to make this work. My code at the moment is:
new AnimatedContainer(
duration: const Duration(milliseconds: 200),
height: App.itemSelected==id ? 50.0 : 0.0,
curve: Curves.fastOutSlowIn,
),
That does change the height of the Container but not in an animated way as I had hoped. Any help would be gratefully received!
I am not sure if AnimatedSize is suitable for your use case, but I have added an example on how to make a simple animation with it:
The coloring is a bit off due to the recording but you should be able to test this yourself.
class MyAppState extends State<MyApp> with TickerProviderStateMixin {
double _height = 50.0;
double _width = 20.0;
var _color = Colors.blue;
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new AnimatedSize(
curve: Curves.fastOutSlowIn, child: new Container(
width: _width,
height: _height,
color: _color,
), vsync: this, duration: new Duration(seconds: 2),),
new Divider(height: 35.0,),
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new IconButton(
icon: new Icon(Icons.arrow_upward, color: Colors.green,),
onPressed: () =>
setState(() {
_color = Colors.green;
_height = 95.0;
})),
new IconButton(
icon: new Icon(Icons.arrow_forward, color: Colors.red,),
onPressed: () =>
setState(() {
_color = Colors.red;
_width = 45.0;
})),
],
)
],)
,)
);
}
}
You can use an AnimatedSize for that purpose.
https://api.flutter.dev/flutter/widgets/AnimatedSize-class.html
Related
this is my animation code.
late AnimationController _controller;
late Animation<Color?> _color;
#override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(milliseconds: 1350),
vsync: this,
)..forward();
_color = ColorTween(begin: Colors.grey.shade400, end: Colors.amber)
.animate(_controller);
}
and I make Grid and apply animationBuilder.
GridView.builder(
...
child: cardButtons(subject[index], _color),)
return GestureDetector(
onTap: () {
doMultiSelection(item.toString());
setState(() {});
},
child: AnimatedBuilder(
animation: _color,
builder: (context, child) {
return Card(
// check if the index is equal to the selected Card integer
color: selectedItem.contains(item.toString())
? _color.value
: Colors.grey.shade400,
child: Container(
height: 200,
width: 200,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
I want to active animation(_color.value) whenever i click.
But it can olny work one time. How can I fix it?
I'm doing a backdrop style slide down similar to the Codelabs flutter example here. I'd like the foreground part to stop at the bottom of the backdrop content.
I know you can't get the height directly until after layout. Since the app starts with the foreground part at the top, I was thinking I could get the height after build and store it in the state, but I'm not exactly sure how to do this.
This is what I have now, just using a predefined 'overhang' that doesn't account for the backdrop content height:
Widget _buildForeground(BuildContext context, BoxConstraints constraints) {
Animation<RelativeRect> rectAnimation = new RelativeRectTween(
begin: new RelativeRect.fromLTRB(0.0, constraints.maxHeight - widget.overhang, 0.0, 0.0),
end: new RelativeRect.fromLTRB(0.0, 0.0, 0.0, 0.0),
).animate(_controller);
return new PositionedTransition(
rect: rectAnimation,
child: new Material(
shape: RoundedRectangleBorder(borderRadius: new BorderRadius.only(topLeft: new Radius.circular(15.0), topRight: new Radius.circular(15.0))),
elevation: 16.0,
child: widget.foreground,
)
);
}
#override
Widget build(BuildContext context) {
return new LayoutBuilder(
builder: (context, constraints) => new Stack(
children: <Widget>[
new Container(color: Theme.of(context).primaryColor,),
widget.background,
_buildForeground(context, constraints),
],
),
);
}
Thanks to Remi I came up with this solution using GlobalKey
void _toggleForeground() {
setState(() => top = backgroundKey?.currentContext?.size?.height);
_controller.fling(velocity: _isBackgroundVisible ? -widget.toggleVelocity : widget.toggleVelocity);
}
Widget _buildForeground() {
if (top == null) top = 500.0;
Animation<RelativeRect> rectAnimation = new RelativeRectTween(
begin: new RelativeRect.fromLTRB(0.0, top, 0.0, 0.0),
end: new RelativeRect.fromLTRB(0.0, 0.0, 0.0, 0.0),
).animate(_controller);
return new PositionedTransition(
rect: rectAnimation,
child: new Material(
shape: RoundedRectangleBorder(borderRadius: new BorderRadius.only(topLeft: new Radius.circular(15.0), topRight: new Radius.circular(15.0))),
elevation: 16.0,
child: widget.foreground,
)
);
}
#override
Widget build(BuildContext context) {
return new Stack(
children: <Widget>[
new Container(color: Theme.of(context).primaryColor),
new Column(
key: backgroundKey,
mainAxisSize: MainAxisSize.min,
children: <Widget>[widget.background],
),
_buildForeground(),
],
);
}
I created a row with a BackButton and a TextWidget.
I want to center the text to the middle of the screen. Actually flutter centeres the text to the containers width, but the containers width isnt the same as the screen width, because there is the backbutton. How do i fix that?
Expanded getTitle() {
return new Expanded(
child: new Text("Einloggen", style: new TextStyle(fontSize: 18.0), textAlign: TextAlign.center)
);
}
BackButton getBackButton() {
return new BackButton(
);
}
Row getHeader() {
return new Row(
children: <Widget>[
getBackButton(),
getTitle()
],
);
}
#override
Widget build(BuildContext context) {
final double statusBarHeight = MediaQuery.of(context).padding.top;
return new Material(
child: new Container(
padding: new EdgeInsets.fromLTRB(0.0, statusBarHeight, 0.0, 0.0),
child: new Column(
children: <Widget>[
getHeader()
],
),
),
);
}
You can use the Row's mainAxisAlignment parameter to center align children of a row.
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
//children Widgets
]
);
Similarly, mainAxisAligment paramter can also be used to align Column's children. For more information check this out!
You can achieve the same UI using A Scaffold with AppBar
class mytab extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
centerTitle: true,
leading: new Icon(Icons.arrow_back),
title: new Text("Einloggen",
style: new TextStyle(fontSize: 18.0)),
),
);
}
}
To make the Title in the Center :
centerTitle: true
Based on your code
Widget getTitle() {
return const Text('Einloggen',
style: TextStyle(fontSize: 18.0), textAlign: TextAlign.center);
}
BackButton getBackButton() {
return const BackButton();
}
Row getHeader() {
return Row(
children: <Widget>[
Expanded(
child: getBackButton(),
),
const Spacer(),
getTitle(),
const Spacer(flex: 2)
],
);
}
#override
Widget build(BuildContext context) {
final double statusBarHeight = MediaQuery.of(context).padding.top;
return Material(
child: Container(
padding: EdgeInsets.fromLTRB(0.0, statusBarHeight, 0.0, 0.0),
child: Column(
children: <Widget>[getHeader()],
),
),
);
}
I don't know if it is still useful, but I found a solution thanks to widegtes: Container, Stack and Align.
Widget getTitle() {
return new Text("Einloggen", style: new TextStyle(fontSize: 18.0));
}
Widget getBackButton() {
return Container(
height: MediaQuery.of(context).size.height,
child: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: moveToLogin,
));
}
Widget getHeader() {
return Container(
height: 50.0,
// you must set a size to the Conteiener to make sure that the internal Align
// widens as much as possible.
child: new Stack(
// Stack places the objects in the upper left corner
children: <Widget>[
getBackButton(),
Align(alignment: Alignment.center, child: getTitle()),
],
),
);
}
final double statusBarHeight = MediaQuery.of(context).padding.top;
return new Container(
padding: new EdgeInsets.fromLTRB(0.0, statusBarHeight, 0.0, 0.0),
child: new Column(
children: <Widget>[getHeader()],
),
);
This is the result
Image
class _DaftarMuridState extends State<DaftarMurid> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Column(
children: <Widget>[
new Flexible(
child: new FirebaseAnimatedList(//new
query: db.reference().child("Murid"),
sort: (a, b) => a.key.compareTo(b.key),
padding: new EdgeInsets.all(8.0),
itemBuilder: (_, DataSnapshot dataSnapshot, Animation<double> animations,x){
return new DaftarMuridView(
snapshot: dataSnapshot,
animation: animations,
);//new
}
),
),
],
),
);
}
}
class DaftarMuridViewState extends State<DaftarMuridView>{
DaftarMuridViewState({this.snapshot, this.animation});
final DataSnapshot snapshot;
final Animation animation;
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
String fotoUrl = snapshot.value['Foto'];
String ig = snapshot.value['Instagram'];
hash.putIfAbsent(snapshot.value['Nama'], () => false);
bool expanded = hash[snapshot.value['Nama']];
var expansionPanel = new ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
setState(() {
hash.remove(snapshot.value['Nama']);
hash.putIfAbsent(snapshot.value['Nama'], () => !isExpanded);
expanded = !expanded;
});
},
children: [new ExpansionPanel(headerBuilder: (BuildContext context, bool isExpanded) {
return new ListTile(
leading: const Icon(Icons.school),
title: new Text(
snapshot.value['Nama'],
textAlign: TextAlign.left,
style: new TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w400,
),
));
},
body: new ListView(
physics: const BouncingScrollPhysics(),
shrinkWrap: true,
padding: const EdgeInsets.all(8.0),
children: <Widget>[
new CachedNetworkImage(
imageUrl: fotoUrl == null?"https://drive.google.com/uc?export=download&id=1tkqO59S9jiWpkzHQNJRKLuCGYIn5kK_v":fotoUrl,
placeholder: new CircularProgressIndicator(),
errorWidget: new CachedNetworkImage(imageUrl: "https://drive.google.com/uc?export=download&id=1tkqO59S9jiWpkzHQNJRKLuCGYIn5kK_v"),
fadeOutDuration: new Duration(seconds: 1),
fadeInDuration: new Duration(seconds: 1),
height: size.height / 2.0,
width: size.width / 2.0,
alignment: Alignment.center,
),
new ListTile(
leading: const Icon(Icons.today),
title: const Text('Tanggal Lahir'),
subtitle: new Text(snapshot.value['Tanggal Lahir']),
),
new Row(
children: <Widget>[
ig != null ?
new FlatButton(
onPressed: () => _instagram(ig),
child: new CachedNetworkImage(imageUrl: "http://diylogodesigns.com/blog/wp-content/uploads/2016/05/Instagram-logo-png-icon.png", width: size.width / 4.0, height: size.height / 4.0, ),
)
: new Container(),
],
),
],
),
isExpanded: expanded)],
);
return new SizeTransition(
sizeFactor: new CurvedAnimation(
parent: animation, curve: Curves.easeOut),
axisAlignment: 0.0,
child: expansionPanel,
);
}
}
is my code not efficient? the process is Get Data from Firebase -> Store it to list view
it's a bit lag when open the activity, maybe because getting the data. But is there a solution for make it doesn't lag?
I cut some code that isn't important.
Use Futures to perform time consuming operations, so it will not freeze the UI.
https://www.dartlang.org/tutorials/language/futures
My goal is to create a clock similar to this. How can I achieve it using Flutter?
I would recommend the Layouts, Interactivity, and Animation tutorials. The codelab is also a good way to learn your way around Flutter.
Here's a sketch of how to build your app.
import 'dart:math' as math;
import 'package:meta/meta.dart';
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
theme: new ThemeData(
canvasColor: Colors.deepPurple,
iconTheme: new IconThemeData(color: Colors.white),
accentColor: Colors.pinkAccent,
brightness: Brightness.dark,
),
home: new MyHomePage(),
));
}
class ProgressPainter extends CustomPainter {
ProgressPainter({
#required this.animation,
#required this.backgroundColor,
#required this.color,
}) : super(repaint: animation);
/// Animation representing what we are painting
final Animation<double> animation;
/// The color in the background of the circle
final Color backgroundColor;
/// The foreground color used to indicate progress
final Color color;
#override
void paint(Canvas canvas, Size size) {
Paint paint = new Paint()
..color = backgroundColor
..strokeWidth = 5.0
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
canvas.drawCircle(size.center(Offset.zero), size.width / 2.0, paint);
paint.color = color;
double progressRadians = (1.0 - animation.value) * 2 * math.pi;
canvas.drawArc(
Offset.zero & size, math.pi * 1.5, -progressRadians, false, paint);
}
#override
bool shouldRepaint(ProgressPainter other) {
return animation.value != other.animation.value ||
color != other.color ||
backgroundColor != other.backgroundColor;
}
}
class MyHomePage extends StatefulWidget {
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
List<IconData> icons = <IconData>[
Icons.alarm, Icons.access_time, Icons.hourglass_empty, Icons.timer,
];
AnimationController _controller;
String get timeRemaining {
Duration duration = _controller.duration * _controller.value;
return '${duration.inMinutes} ${(duration.inSeconds % 60)
.toString()
.padLeft(2, '0')}';
}
#override
void initState() {
super.initState();
_controller = new AnimationController(
vsync: this,
duration: const Duration(seconds: 12),
)
..reverse(from: 0.4);
}
Widget build(BuildContext context) {
ThemeData themeData = Theme.of(context);
return new Scaffold(
body: new Padding(
padding: const EdgeInsets.all(10.0),
child:
new Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.start,
children: icons.map((IconData iconData) {
return new Container(
margin: new EdgeInsets.all(10.0),
child: new IconButton(
icon: new Icon(iconData), onPressed: () {
// TODO: Implement
}),
);
}).toList(),
),
new Expanded(
child: new Align(
alignment: FractionalOffset.center,
child: new AspectRatio(
aspectRatio: 1.0,
child: new Stack(
children: <Widget>[
new Positioned.fill(
child: new AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget child) {
return new CustomPaint(
painter: new ProgressPainter(
animation: _controller,
color: themeData.indicatorColor,
backgroundColor: Colors.white,
),
);
}
),
),
new Align(
alignment: FractionalOffset.center,
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Text(
'Label', style: themeData.textTheme.subhead),
new AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget child) {
return new Text(
timeRemaining,
style: themeData.textTheme.display4,
);
}
),
new Text('+1', style: themeData.textTheme.title),
],
),
),
],
),
),
),
),
new Container(
margin: new EdgeInsets.all(10.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new IconButton(icon: new Icon(Icons.delete), onPressed: () {
// TODO: Implement delete
}),
new FloatingActionButton(
child: new AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget child) {
return new Icon(
_controller.isAnimating
? Icons.pause
: Icons.play_arrow
);
},
),
onPressed: () {
if (_controller.isAnimating)
_controller.stop();
else {
_controller.reverse(
from: _controller.value == 0.0 ? 1.0 : _controller
.value,
);
}
},
),
new IconButton(
icon: new Icon(Icons.alarm_add), onPressed: () {
// TODO: Implement add time
}),
],
),
),
],
),
),
);
}
}