Related
I'm trying to animate an alert dialog in flutter so that when it pop ups it shows an animation like this below.
How can I achieve following look and behaviour from Pokemon Go in an alertDialog?
I would really like to have this animation in my app.
Thanks for your Answers!
Try this, modify any variable to meet your requirement:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
AnimationController _controller;
Animation<Offset> _animation;
double _width = 20;
double _height = 200;
Color _color = Colors.transparent;
#override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
_animation = Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: const Offset(0.0, -2.0),
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInCubic,
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
actions: [
IconButton(
icon: Icon(Icons.send),
onPressed: () {
setState(() {
_color = Colors.white;
});
_controller.forward().then((_) {
_width = 200;
setState(() {});
});
},
),
],
),
body: Stack(
children: [
Align(
alignment: Alignment.bottomCenter,
child: SlideTransition(
position: _animation,
child: AnimatedContainer(
width: _width,
height: _height,
decoration: BoxDecoration(
color: _color,
borderRadius: BorderRadius.circular(10),
),
duration: Duration(seconds: 1),
curve: Curves.fastOutSlowIn,
),
),
),
],
),
);
}
}
I need to get a context for my Navigator push, i have this Navigator on my function _navigate. I try something like _navigate(BuildContext context) but i got an error like "type (BuildContext) => dynamic is not a subtype of type() => void. It's the first context of Navigator.push i don't know how to get it.
import 'package:flutter/material.dart';
import 'package:pandora_etna/assistance.dart';
import 'dart:math';
import 'package:vector_math/vector_math.dart' show radians;
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Color(0xFFB3E5FC),
body: SizedBox.expand(child: RadialMenu())),
);
}
}
class RadialMenu extends StatefulWidget {
createState() => _RadialMenuState();
}
class _RadialMenuState extends State<RadialMenu>
with SingleTickerProviderStateMixin {
AnimationController controller;
void initState() {
super.initState();
controller =
AnimationController(duration: Duration(milliseconds: 900), vsync: this);
}
Widget build(BuildContext context) {
return RadialAnimation(controller: controller);
}
}
class RadialAnimation extends StatelessWidget {
RadialAnimation({Key key, this.controller})
: scale = Tween<double>(
begin: 1.5,
end: 0.0,
).animate(
CurvedAnimation(parent: controller, curve: Curves.fastOutSlowIn),
),
translation = Tween<double>(
begin: 0.0,
end: 100.0,
).animate(
CurvedAnimation(parent: controller, curve: Curves.linear),
),
rotation = Tween<double>(begin: 0.0, end: 360.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(
0.0,
0.7,
curve: Curves.decelerate,
)),
),
super(key: key);
final AnimationController controller;
final Animation<double> scale;
final Animation<double> translation;
final Animation<double> rotation;
build(context) {
return AnimatedBuilder(
animation: controller,
builder: (context, builder) {
return Transform.rotate(
angle: radians(rotation.value),
child: Stack(alignment: Alignment.center, children: [
_buildButton(0, _close,
color: Color(0xFF29B6F6), icon: FontAwesomeIcons.chartBar),
_buildButton(60, _close,
color: Color(0xFF29B6F6), icon: FontAwesomeIcons.clipboard),
_buildButton(120, _close,
color: Color(0xFF29B6F6), icon: FontAwesomeIcons.chartBar),
_buildButton(180, _close,
color: Color(0xFF29B6F6), icon: FontAwesomeIcons.book),
_buildButton(240, _close,
color: Color(0xFF29B6F6),
icon: FontAwesomeIcons.arrowsAltV),
_buildButton(300, _navigate,
color: Color(0xFF29B6F6), icon: FontAwesomeIcons.phoneAlt),
_buildButton(360, _close,
color: Color(0xFF29B6F6),
icon: FontAwesomeIcons.compressAlt),
Transform.scale(
scale: scale.value - 1.5,
child: FloatingActionButton(
child: Icon(FontAwesomeIcons.timesCircle),
onPressed: _close,
backgroundColor: Color(0xFF29B6F6)),
),
Transform.scale(
scale: scale.value,
child: FloatingActionButton(
child: Icon(FontAwesomeIcons.solidDotCircle),
onPressed: _open,
backgroundColor: Color(0xFF29B6F6)),
),
]));
});
}
_buildButton(double angle, Function callback, {Color color, IconData icon}) {
final double rad = radians(angle);
return Transform(
transform: Matrix4.identity()
..translate(
(translation.value) * cos(rad), (translation.value) * sin(rad)),
child: FloatingActionButton(
child: Icon(icon), backgroundColor: color, onPressed: callback));
}
_open() {
controller.forward();
}
_close() {
controller.reverse();
}
_navigate(BuildContext context) {
Navigator.push(
context, new MaterialPageRoute(builder: (context) => Assistance()));
}
}
I think it is because you lose your context argument when calling _buildButton.
/// By writing your Function parameter like this you will pass the
/// BuildContext parameter needed.
_buildButton(
300,
() => _navigate(context),
color: Color(0xFF29B6F6),
icon: FontAwesomeIcons.phoneAlt
),
You need to pass BuildContext to the _navigate function when you reference it. You could do this with an anonymous function. _buildButton(300,(context) => _navigate(context), ...
It doesn't work because you didn't give the context parameter of the function's method. I guess so
_buildButton(300, _navigate(context),
color: Color(0xFF29B6F6), icon: FontAwesomeIcons.phoneAlt),
Try out below example for more idea:-
_buildButton(300, _navigate(context), // Added Context here,It will contains context of build function
color: Color(0xFF29B6F6), icon: FontAwesomeIcons.phoneAlt),
_navigate(BuildContext context) {
Navigator.push(
context, new MaterialPageRoute(builder: (context) => Assistance()));
}
When calling your function _buildButton you must wrap _navigate function into another function, since it need your current context to work
Try this
_buildButton(300, () {
_navigate(context);
}, color: Color(0xFF29B6F6), icon: FontAwesomeIcons.phoneAlt),
I want to use some animations on my main screen but only when app start, because while using the app users have to navigate back to main screen oftenly and those animations makes loading time and became annoying to see again and again.. So, is there any way to make animatoins active only the first start(not first run) of app then make them inactive till restart the app?
Here is a simple code; not same but similar logic with the animations I've used, my code is too complex to copy-paste so I'm sending a sample code which I send to another question as answer;
import 'dart:async';
import 'package:flutter/cupertino.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: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ListView(
children: [Container(
color: Colors.blue,
height: 1000,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SlideFadeTransition(
delayStart: Duration(milliseconds: 800),
offset: 2,
child: new Text('Test Text 1')),
SlideFadeTransition(
delayStart: Duration(milliseconds: 1200),
offset: 4,
child: new Text('Test Text 2'))
],
),
),
Container(
color: Colors.red,
height: 1000,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SlideFadeTransition(
delayStart: Duration(milliseconds: 1200),
offset: 2,
child: new Text('Test Text 1')),
SlideFadeTransition(
delayStart: Duration(milliseconds: 2400),
offset: 4,
child: new Text('Test Text 2'))
],
),
),
Container(
color: Colors.orange,
height: 1000,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SlideFadeTransition(
delayStart: Duration(milliseconds: 1200),
offset: 2,
child: new Text('Test Text 1')),
SlideFadeTransition(
delayStart: Duration(milliseconds: 2400),
offset: 4,
child: new Text('Test Text 2'))
],
),
),
]
)
)
);
}
}
enum Direction { vertical, horizontal }
class SlideFadeTransition extends StatefulWidget {
///The child on which to apply the given [SlideFadeTransition]
final Widget child;
///The offset by which to slide and [child] into view from [Direction].
///Defaults to 0.2
final double offset;
///The curve used to animate the [child] into view.
///Defaults to [Curves.easeIn]
final Curve curve;
///The direction from which to animate the [child] into view. [Direction.horizontal]
///will make the child slide on x-axis by [offset] and [Direction.vertical] on y-axis.
///Defaults to [Direction.vertical]
final Direction direction;
///The delay with which to animate the [child]. Takes in a [Duration] and
/// defaults to 0.0 seconds
final Duration delayStart;
///The total duration in which the animation completes. Defaults to 800 milliseconds
final Duration animationDuration;
SlideFadeTransition({
#required this.child,
this.offset = 0.2,
this.curve = Curves.easeIn,
this.direction = Direction.vertical,
this.delayStart = const Duration(seconds: 0),
this.animationDuration = const Duration(milliseconds: 800),
});
#override
_SlideFadeTransitionState createState() => _SlideFadeTransitionState();
}
class _SlideFadeTransitionState extends State<SlideFadeTransition>
with SingleTickerProviderStateMixin {
Animation<Offset> _animationSlide;
AnimationController _animationController;
Animation<double> _animationFade;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: widget.animationDuration,
);
//configure the animation controller as per the direction
if (widget.direction == Direction.vertical) {
_animationSlide =
Tween<Offset>(begin: Offset(0, widget.offset), end: Offset(0, 0))
.animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
} else {
_animationSlide =
Tween<Offset>(begin: Offset(widget.offset, 0), end: Offset(0, 0))
.animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
}
_animationFade =
Tween<double>(begin: -1.0, end: 1.0).animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
Timer(widget.delayStart, () {
_animationController.forward();
});
}
#override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _animationFade,
child: SlideTransition(
position: _animationSlide,
child: widget.child,
),
);}}
You can copy paste run full code below
Step 1: You can define a bool firstRun = true
Step 2: When Navigate to other page set firstRun to false
Step 3: In _SlideFadeTransitionState 's build method check if not firstRun and return widget.child directly
return firstRun? FadeTransition(...widget.child) : widget.child
working demo
code snippet
bool firstRun = true;
...
RaisedButton(
child: Text('Open route'),
onPressed: () {
firstRun = false;
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
)
...
#override
Widget build(BuildContext context) {
print('first run ${firstRun}');
return firstRun? FadeTransition(
opacity: _animationFade,
child: SlideTransition(
position: _animationSlide,
child: widget.child,
),
) : widget.child;
}
full code
import 'dart:async';
import 'package:flutter/cupertino.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: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
bool firstRun = true;
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ListView(children: [
Container(
color: Colors.blue,
height: 1000,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SlideFadeTransition(
delayStart: Duration(milliseconds: 800),
offset: 2,
child: new Text('Test Text 1')),
SlideFadeTransition(
delayStart: Duration(milliseconds: 1200),
offset: 4,
child: new Text('Test Text 2'))
],
),
),
Container(
color: Colors.red,
height: 1000,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SlideFadeTransition(
delayStart: Duration(milliseconds: 1200),
offset: 2,
child: new Text('Test Text 1')),
SlideFadeTransition(
delayStart: Duration(milliseconds: 2400),
offset: 4,
child: new Text('Test Text 2'))
],
),
),
Container(
color: Colors.orange,
height: 1000,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SlideFadeTransition(
delayStart: Duration(milliseconds: 1200),
offset: 2,
child: new Text('Test Text 1')),
SlideFadeTransition(
delayStart: Duration(milliseconds: 2400),
offset: 4,
child: new Text('Test Text 2'))
],
),
),
RaisedButton(
child: Text('Open route'),
onPressed: () {
firstRun = false;
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
),
])));
}
}
enum Direction { vertical, horizontal }
class SlideFadeTransition extends StatefulWidget {
///The child on which to apply the given [SlideFadeTransition]
final Widget child;
///The offset by which to slide and [child] into view from [Direction].
///Defaults to 0.2
final double offset;
///The curve used to animate the [child] into view.
///Defaults to [Curves.easeIn]
final Curve curve;
///The direction from which to animate the [child] into view. [Direction.horizontal]
///will make the child slide on x-axis by [offset] and [Direction.vertical] on y-axis.
///Defaults to [Direction.vertical]
final Direction direction;
///The delay with which to animate the [child]. Takes in a [Duration] and
/// defaults to 0.0 seconds
final Duration delayStart;
///The total duration in which the animation completes. Defaults to 800 milliseconds
final Duration animationDuration;
SlideFadeTransition({
#required this.child,
this.offset = 0.2,
this.curve = Curves.easeIn,
this.direction = Direction.vertical,
this.delayStart = const Duration(seconds: 0),
this.animationDuration = const Duration(milliseconds: 800),
});
#override
_SlideFadeTransitionState createState() => _SlideFadeTransitionState();
}
class _SlideFadeTransitionState extends State<SlideFadeTransition>
with SingleTickerProviderStateMixin {
Animation<Offset> _animationSlide;
AnimationController _animationController;
Animation<double> _animationFade;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: widget.animationDuration,
);
//configure the animation controller as per the direction
if (widget.direction == Direction.vertical) {
_animationSlide =
Tween<Offset>(begin: Offset(0, widget.offset), end: Offset(0, 0))
.animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
} else {
_animationSlide =
Tween<Offset>(begin: Offset(widget.offset, 0), end: Offset(0, 0))
.animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
}
_animationFade =
Tween<double>(begin: -1.0, end: 1.0).animate(CurvedAnimation(
curve: widget.curve,
parent: _animationController,
));
Timer(widget.delayStart, () {
_animationController.forward();
});
}
#override
Widget build(BuildContext context) {
print('first run ${firstRun}');
return firstRun? FadeTransition(
opacity: _animationFade,
child: SlideTransition(
position: _animationSlide,
child: widget.child,
),
) : widget.child;
}
}
class SecondRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: Center(
child: RaisedButton(
onPressed: () {
// Navigate back to first route when tapped.
},
child: Text('Go back!'),
),
),
);
}
}
I'm new in flutter, I need to create an interface like this
I've created a circular button in this way:
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => _buttonCallBack(),
child: ClipOval(
child: FractionallySizedBox(
heightFactor : 0.4,
widthFactor : 0.6,
child : Container(
color: Colors.lightGreen,
child : Center(
child: Text(
"Start",
style: new TextStyle(fontSize: 36.0),
)
)
)
),
),
);
But now I don't how to show buttons around the circle. Anyone has any ideas ?
If you're trying a circular menu you can follow this :
Add vector_math: ^2.0.8 and font_awesome_flutter: 8.4.0 to pubspec.yaml
Here is the full source of the example :
import 'package:flutter/material.dart';
import 'dart:math';
import 'package:vector_math/vector_math.dart' show radians, Vector3;
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FlutterBase',
home: Scaffold(
body: SizedBox.expand(child: RadialMenu())
)
);
}
}
class RadialMenu extends StatefulWidget {
createState() => _RadialMenuState();
}
class _RadialMenuState extends State<RadialMenu> with SingleTickerProviderStateMixin {
AnimationController controller;
#override
void initState() {
super.initState();
controller = AnimationController(duration: Duration(milliseconds: 900), vsync: this);
// ..addListener(() => setState(() {}));
}
#override
Widget build(BuildContext context) {
return RadialAnimation(controller: controller);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
}
class RadialAnimation extends StatelessWidget {
RadialAnimation({ Key key, this.controller }) :
translation = Tween<double>(
begin: 0.0,
end: 100.0,
).animate(
CurvedAnimation(
parent: controller,
curve: Curves.elasticOut
),
),
scale = Tween<double>(
begin: 1.5,
end: 0.0,
).animate(
CurvedAnimation(
parent: controller,
curve: Curves.fastOutSlowIn
),
),
rotation = Tween<double>(
begin: 0.0,
end: 360.0,
).animate(
CurvedAnimation(
parent: controller,
curve: Interval(
0.0, 0.7,
curve: Curves.decelerate,
),
),
),
super(key: key);
final AnimationController controller;
final Animation<double> rotation;
final Animation<double> translation;
final Animation<double> scale;
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (context, widget) {
return Transform.rotate(
angle: radians(rotation.value),
child: Stack(
alignment: Alignment.center,
children: <Widget>[
_buildButton(0, color: Colors.red, icon: FontAwesomeIcons.thumbtack),
_buildButton(45, color: Colors.green, icon:FontAwesomeIcons.sprayCan),
_buildButton(90, color: Colors.orange, icon: FontAwesomeIcons.fire),
_buildButton(135, color: Colors.blue, icon:FontAwesomeIcons.kiwiBird),
_buildButton(180, color: Colors.black, icon:FontAwesomeIcons.cat),
_buildButton(225, color: Colors.indigo, icon:FontAwesomeIcons.paw),
_buildButton(270, color: Colors.pink, icon: FontAwesomeIcons.bong),
_buildButton(315, color: Colors.yellow, icon:FontAwesomeIcons.bolt),
Transform.scale(
scale: scale.value - 1,
child: FloatingActionButton(child: Icon(FontAwesomeIcons.timesCircle), onPressed: _close, backgroundColor: Colors.red),
),
Transform.scale(
scale: scale.value,
child: FloatingActionButton(child: Icon(FontAwesomeIcons.solidDotCircle), onPressed: _open),
)
])
);
});
}
_open() {
controller.forward();
}
_close() {
controller.reverse();
}
_buildButton(double angle, { Color color, IconData icon }) {
final double rad = radians(angle);
return Transform(
transform: Matrix4.identity()..translate(
(translation.value) * cos(rad),
(translation.value) * sin(rad)
),
child: FloatingActionButton(
child: Icon(icon), backgroundColor: color, onPressed: _close, elevation: 0)
);
}
}
is any solution to make drag and drop dialogs in flutter? for example after showing dialog in center of screen i would like to drag it to top of screen to make fullscreen dialog over current cover, for example this code is simple implementation to show dialog and i'm not sure, how can i do that
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(title: 'Flutter Demo', theme: ThemeData(), home: Page());
}
}
class Page extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton.icon(
onPressed: () {
showDialog(
context: context,
builder: (_) => FunkyOverlay(),
);
},
icon: Icon(Icons.message),
label: Text("PopUp!")),
),
);
}
}
class FunkyOverlay extends StatefulWidget {
#override
State<StatefulWidget> createState() => FunkyOverlayState();
}
class FunkyOverlayState extends State<FunkyOverlay>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> scaleAnimation;
#override
void initState() {
super.initState();
controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 450));
scaleAnimation =
CurvedAnimation(parent: controller, curve: Curves.elasticInOut);
controller.addListener(() {
setState(() {});
});
controller.forward();
}
#override
Widget build(BuildContext context) {
return Center(
child: Material(
color: Colors.transparent,
child: ScaleTransition(
scale: scaleAnimation,
child: Container(
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0))),
child: Padding(
padding: const EdgeInsets.all(50.0),
child: Text("Well hello there!"),
),
),
),
),
);
}
}
This is one way to do it ,
import 'package:flutter/material.dart';
main() {
runApp(MaterialApp(
theme: ThemeData(
primarySwatch: Colors.indigo,
),
home: App(),
));
}
class App extends StatefulWidget {
#override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.open_in_new),
onPressed: () {
showGeneralDialog(
context: context,
barrierDismissible: true,
barrierLabel: "hi",
barrierColor: Colors.black.withOpacity(0.2),
transitionDuration: Duration(milliseconds: 500),
pageBuilder: (context, pAnim, sAnim) {
return SafeArea(child: FloatingDialog());
},
transitionBuilder: (context, pAnim, sAnim, child) {
if (pAnim.status == AnimationStatus.reverse) {
return FadeTransition(
opacity: Tween(begin: 0.0, end: 0.0).animate(pAnim),
child: child,
);
} else {
return FadeTransition(
opacity: pAnim,
child: child,
);
}
},
);
},
),
);
}
}
class FloatingDialog extends StatefulWidget {
#override
_FloatingDialogState createState() => _FloatingDialogState();
}
class _FloatingDialogState extends State<FloatingDialog>
with TickerProviderStateMixin {
double _dragStartYPosition;
double _dialogYOffset;
Widget myContents = MyScaffold();
AnimationController _returnBackController;
Animation<double> _dialogAnimation;
#override
void initState() {
super.initState();
_dialogYOffset = 0.0;
_returnBackController =
AnimationController(vsync: this, duration: Duration(milliseconds: 1300))
..addListener(() {
setState(() {
_dialogYOffset = _dialogAnimation.value;
print(_dialogYOffset);
});
});
}
#override
void dispose() {
_returnBackController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(
top: 100.0,
bottom: 10.0,
left: 10.0,
right: 10.0,
),
child: Transform.translate(
offset: Offset(0.0, _dialogYOffset),
child: Column(
children: <Widget>[
Icon(
Icons.keyboard_arrow_up,
color: Colors.white,
),
Expanded(
child: GestureDetector(
onVerticalDragStart: (dragStartDetails) {
_dragStartYPosition = dragStartDetails.globalPosition.dy;
print(dragStartDetails.globalPosition);
},
onVerticalDragUpdate: (dragUpdateDetails) {
setState(() {
_dialogYOffset = (dragUpdateDetails.globalPosition.dy) -
_dragStartYPosition;
});
print(_dialogYOffset);
if (_dialogYOffset < -90.0) {
Navigator.of(context).pop();
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (context, pAnim, sAnim) => myContents,
transitionDuration: Duration(milliseconds: 500),
transitionsBuilder: (context, pAnim, sAnim, child) {
if (pAnim.status == AnimationStatus.forward) {
return ScaleTransition(
scale: Tween(begin: 0.8, end: 1.0).animate(
CurvedAnimation(
parent: pAnim,
curve: Curves.elasticOut)),
child: child,
);
} else {
return FadeTransition(
opacity: pAnim,
child: child,
);
}
}),
);
}
},
onVerticalDragEnd: (dragEndDetails) {
_dialogAnimation = Tween(begin: _dialogYOffset, end: 0.0)
.animate(CurvedAnimation(
parent: _returnBackController,
curve: Curves.elasticOut));
_returnBackController.forward(from: _dialogYOffset);
_returnBackController.forward(from: 0.0);
},
child: myContents,
),
),
],
),
),
);
}
}
class MyScaffold extends StatelessWidget {
const MyScaffold({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Channels"),
),
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.of(context).pop();
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(),
body: Placeholder(),
),
),
);
},
),
),
);
}
}
Output:
You can try this.
void main() => runApp(MaterialApp(home: HomePage()));
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool _shown = false;
double _topOffset = 20, _dialogHeight = 400;
Duration _duration = Duration(milliseconds: 400);
Offset _offset, _initialOffset;
#override
void didChangeDependencies() {
super.didChangeDependencies();
var size = MediaQuery.of(context).size;
_offset = Offset(size.width, (size.height - _dialogHeight) / 2);
_initialOffset = _offset;
}
#override
Widget build(BuildContext context) {
var appBarColor = Colors.blue[800];
return Scaffold(
floatingActionButton: FloatingActionButton(onPressed: () => setState(() => _shown = !_shown)),
body: SizedBox.expand(
child: Stack(
children: <Widget>[
Container(
color: appBarColor,
child: SafeArea(
bottom: false,
child: Align(
child: Column(
children: <Widget>[
MyAppBar(
title: "Image",
color: appBarColor,
icon: Icons.home,
onPressed: () {},
),
Expanded(child: Image.asset("assets/images/landscape.jpeg", fit: BoxFit.cover)),
],
),
),
),
),
AnimatedOpacity(
opacity: _shown ? 1 : 0,
duration: _duration,
child: Material(
elevation: 8,
color: Colors.grey[900].withOpacity(0.5),
child: _shown
? GestureDetector(
onTap: () => setState(() => _shown = !_shown),
child: Container(color: Colors.transparent, child: SizedBox.expand()),
)
: SizedBox.shrink(),
),
),
// this shows our dialog
Positioned(
top: _offset.dy,
left: 10,
right: 10,
height: _shown ? null : 0,
child: AnimatedOpacity(
duration: _duration,
opacity: _shown ? 1 : 0,
child: GestureDetector(
onPanUpdate: (details) => setState(() => _offset += details.delta),
onPanEnd: (details) {
// when tap is lifted and current y position is less than set _offset, navigate to the next page
if (_offset.dy < _topOffset) {
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, anim1, anim2) => Screen2(),
transitionDuration: _duration,
transitionsBuilder: (context, anim1, anim2, child) {
bool isForward = anim1.status == AnimationStatus.forward;
Tween<double> tween = Tween(begin: isForward ? 0.9 : 0.5, end: 1);
return ScaleTransition(
scale: tween.animate(
CurvedAnimation(
parent: anim1,
curve: isForward ? Curves.bounceOut : Curves.easeOut,
),
),
child: child,
);
},
),
).then((_) {
_offset = _initialOffset;
});
}
// make the dialog come back to the original position
else {
Timer.periodic(Duration(milliseconds: 5), (timer) {
if (_offset.dy < _initialOffset.dy - _topOffset) {
_offset = Offset(_offset.dx, _offset.dy + 15);
setState(() {});
} else if (_offset.dy > _initialOffset.dy + _topOffset) {
_offset = Offset(_offset.dx, _offset.dy - 15);
setState(() {});
} else
timer.cancel();
});
}
},
child: Column(
children: <Widget>[
Icon(Icons.keyboard_arrow_up, color: Colors.white, size: 32),
Hero(
tag: "MyTag",
child: SizedBox(
height: _dialogHeight, // makes sure we don't exceed than our specified height
child: SingleChildScrollView(child: CommonWidget(appBar: MyAppBar(title: "FlutterLogo", color: Colors.orange))),
),
),
],
),
),
),
),
],
),
),
);
}
}
// this app bar is used in 1st and 2nd screen
class MyAppBar extends StatelessWidget {
final String title;
final Color color;
final IconData icon;
final VoidCallback onPressed;
const MyAppBar({Key key, #required this.title, #required this.color, this.icon, this.onPressed}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
height: kToolbarHeight,
color: color,
width: double.maxFinite,
alignment: Alignment.centerLeft,
child: Row(
children: <Widget>[
icon != null ? IconButton(icon: Icon(icon), onPressed: onPressed, color: Colors.white,) : SizedBox(width: 16),
Text(
title,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white),
),
],
),
);
}
}
// this is the one which is shown in both Dialog and Screen2
class CommonWidget extends StatelessWidget {
final bool isFullscreen;
final Widget appBar;
const CommonWidget({Key key, this.isFullscreen = false, this.appBar}) : super(key: key);
#override
Widget build(BuildContext context) {
var child = Container(
width: double.maxFinite,
color: Colors.blue,
child: FlutterLogo(size: 300, colors: Colors.orange),
);
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
appBar,
isFullscreen ? Expanded(child: child) : child,
],
);
}
}
class Screen2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
var appBarColor = Colors.orange;
return Scaffold(
body: Container(
color: appBarColor,
child: SafeArea(
bottom: false,
child: CommonWidget(
isFullscreen: true,
appBar: MyAppBar(
title: "FlutterLogo",
color: appBarColor,
icon: Icons.arrow_back,
onPressed: () => Navigator.pop(context),
),
),
),
),
);
}
}