From a Flutter issue posted by Yueh Chou:
Not sure if the event will bubble up to the parent widget and trigger the animation with all the nested widgets and callbacks.
In the Original files,
Pressing on a grandchild widget(e.g. FlatButton) of a BackWidget it will trigger a ModalDialog call and pressing the button will make an async call. The Flip Effect will take place at the end of a successful async call.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
home: new Container(color: Colors.white, child: new RootWidget())));
}
class RootWidget extends StatelessWidget {
FlipWidget mainFlipWidget;
@override
Widget build(BuildContext context) {
mainFlipWidget = new FlipWidget(
frontWidget: frontWidget,
backWidget: new BackWidget(
onPressedButton: () {
_flipMainButton();
},
),
);
return new Center(child:mainFlipWidget);
}
_flipMainButton() {
mainFlipWidget.doTheFlip();
}
}
class BackWidget extends StatefulWidget {
BackWidget({
this.onPressedButton,
});
final VoidCallback onPressedButton;
Widget buttonFlipWidget;
_BackWidgetState createState() => new _BackWidgetState();
}
class _BackWidgetState extends State<BackWidget> {
@override
Widget build(BuildContext context) {
widget.buttonFlipWidget = new Container(
height: 180.0,
color: Colors.grey[200],
child:new FlatButton(
onPressed: () {
triggerCb();
},
child: new Text('Press me'),
),
);
return widget.buttonFlipWidget;
}
triggerCb() {
widget.onPressedButton();
}
}
class FlipWidget extends StatefulWidget {
FlipWidget({
@required this.frontWidget,
@required this.backWidget,
});
final Widget frontWidget;
final Widget backWidget;
final _FlipWidgetState _state = new _FlipWidgetState();
doTheFlip () {
_state.doTheFlip();
}
_FlipWidgetState createState() => _state;
}
class _FlipWidgetState extends State<FlipWidget> with TickerProviderStateMixin {
AnimationController _controller;
Animation<double> _frontScale;
Animation<double> _backScale;
void initState() {
super.initState();
_controller = new AnimationController(vsync: this, duration: const Duration(milliseconds: 1500),);
_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
Widget build(BuildContext context) {
// ThemeData theme = Theme.of(context);
return new Scaffold(
body: new Center(
child: new Stack(
children: [
new AnimatedBuilder(
child: widget.frontWidget,
animation: _backScale,
builder: (BuildContext context, Widget child) {
final Matrix4 transform = new Matrix4.identity()
..scale(_backScale.value, 1.0, 1.0);
return new Transform(
transform: transform,
alignment: FractionalOffset.center,
child: child,
);
},
),
new AnimatedBuilder(
child: widget.backWidget,
animation: _frontScale,
builder: (BuildContext context, Widget child) {
final Matrix4 transform = new Matrix4.identity()
..scale(_frontScale.value, 1.0, 1.0);
return new Transform(
transform: transform,
alignment: FractionalOffset.center,
child: child,
);
}
),
],
),
),
);
}
doTheFlip() {
setState(() {
if (_controller.isCompleted || _controller.velocity > 0)
_controller.reverse();
else
_controller.forward();
});
}
}
final Widget frontWidget = new Container(
color: Colors.white,
height: 180.0,
child: new Column(
children: [
new Text("Front Side of the card")
],
),
);
Some changes you should make to this code.
RootWidget
should be stateful. Store the flipped
state there.BackWidget
should be stateless. All it has to do is notify RootWidget
that the button was pressed.FlipWidget
should detect changes to flipped
using didUpdateWidget
.State
into a member variable of a Widget
, and avoid putting mutator methods on your State
if you can avoid it... this is often a sign that you need to store the state higher in your widget tree and pass it down with constructor arguments.Learn more about StatefulWidget
and StatelessWidget
in the Widget Framework Tour and interactivity tutorial.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
home: new Container(
color: Colors.white,
child: new RootWidget(),
),
));
}
class RootWidget extends StatefulWidget {
@override
_RootWidgetState createState() => new _RootWidgetState();
}
class _RootWidgetState extends State<RootWidget> {
bool _flipped = false;
@override
Widget build(BuildContext context) {
return new Center(
child: new FlipWidget(
flipped: _flipped,
frontWidget: new Container(
color: Colors.white,
height: 180.0,
child: new Column(
children: [
new Text("Front Side of the card")
],
),
),
backWidget: new BackWidget(
onPressedButton: () {
setState(() {
_flipped = !_flipped;
});
},
),
),
);
}
}
class BackWidget extends StatelessWidget {
BackWidget({
this.onPressedButton,
});
final VoidCallback onPressedButton;
@override
Widget build(BuildContext context) {
return new Container(
height: 180.0,
color: Colors.grey[200],
child:new FlatButton(
onPressed: onPressedButton,
child: new Text('Press me'),
),
);
}
}
class FlipWidget extends StatefulWidget {
FlipWidget({
@required this.frontWidget,
@required this.backWidget,
@required this.flipped,
});
final Widget frontWidget;
final Widget backWidget;
final bool flipped;
@override
_FlipWidgetState createState() => new _FlipWidgetState();
}
class _FlipWidgetState extends State<FlipWidget> with TickerProviderStateMixin {
AnimationController _controller;
Animation<double> _frontScale;
Animation<double> _backScale;
@override
void initState() {
super.initState();
_controller = new AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1500)
)..value = widget.flipped ? 1.0 : 0.0;
_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 didUpdateWidget(FlipWidget oldWidget) {
if (widget.flipped != oldWidget.flipped) {
if (widget.flipped) {
_controller.forward();
} else {
_controller.reverse();
}
}
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new Stack(
children: [
new AnimatedBuilder(
child: widget.frontWidget,
animation: _backScale,
builder: (BuildContext context, Widget child) {
final Matrix4 transform = new Matrix4.identity()
..scale(_backScale.value, 1.0, 1.0);
return new Transform(
transform: transform,
alignment: FractionalOffset.center,
child: child,
);
},
),
new AnimatedBuilder(
child: widget.backWidget,
animation: _frontScale,
builder: (BuildContext context, Widget child) {
final Matrix4 transform = new Matrix4.identity()
..scale(_frontScale.value, 1.0, 1.0);
return new Transform(
transform: transform,
alignment: FractionalOffset.center,
child: child,
);
}
),
],
),
),
);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With